Redmine is a Ruby on Rails project management application. It does not include a production-ready web server — it is designed to run behind Puma or Passenger as a backend, with nginx or Apache as the TLS-terminating reverse proxy. This article covers configuring nginx with TLS for Redmine, setting the correct base URL so email notifications and file attachment links work over HTTPS, migrating existing HTTP links, and automating certificate renewal.
Redmine deployment architecture
Redmine can be deployed as:
- Puma (default with Rails 6+) — starts an HTTP server, typically on port 3000 or via a Unix socket
- Passenger (mod_rails for Apache/nginx) — Passenger embeds the Ruby runtime inside the web server
- WEBrick — development only, not suitable for production
In all cases, the web server (nginx or Apache) fronts the Ruby process and handles TLS.
Option A: nginx + Puma via Unix socket
Step 1: Configure Puma to use a Unix socket
In Redmine’s config/puma.rb (create it if it does not exist):
# config/puma.rb
environment ENV.fetch("RAILS_ENV") { "production" }
# Use a Unix socket (preferred over TCP for security)
bind "unix:///var/run/redmine/puma.sock"
# Or use TCP on localhost
# bind "tcp://127.0.0.1:3000"
workers 2
threads 1, 5
pidfile "/var/run/redmine/puma.pid"
state_path "/var/run/redmine/puma.state"
activate_control_app
Create the socket directory:
sudo mkdir -p /var/run/redmine
sudo chown redmine:redmine /var/run/redmine
Start Puma:
sudo -u redmine bash -c "cd /var/www/redmine && bundle exec puma -C config/puma.rb"
Or via systemd:
# /etc/systemd/system/redmine.service
[Unit]
Description=Redmine Puma application server
After=network.target
[Service]
Type=simple
User=redmine
Group=redmine
WorkingDirectory=/var/www/redmine
Environment=RAILS_ENV=production
ExecStart=/usr/local/bin/bundle exec puma -C config/puma.rb
Restart=on-failure
[Install]
WantedBy=multi-user.target
sudo systemctl enable --now redmine
Step 2: Configure nginx with TLS
sudo nano /etc/nginx/sites-available/redmine
upstream redmine {
server unix:///var/run/redmine/puma.sock fail_timeout=0;
# Or TCP: server 127.0.0.1:3000 fail_timeout=0;
}
server {
listen 80;
server_name redmine.example.com;
return 301 https://$host$request_uri;
}
server {
listen 443 ssl;
http2 on;
server_name redmine.example.com;
ssl_certificate /etc/letsencrypt/live/redmine.example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/redmine.example.com/privkey.pem;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers HIGH:!aNULL:!MD5;
ssl_session_cache shared:SSL:10m;
ssl_session_timeout 1d;
add_header Strict-Transport-Security "max-age=63072000; includeSubDomains; preload" always;
add_header X-Frame-Options SAMEORIGIN always;
add_header X-Content-Type-Options nosniff always;
root /var/www/redmine/public;
# Increase upload limit for file attachments
client_max_body_size 50M;
location ^~ /assets/ {
gzip_static on;
expires max;
add_header Cache-Control public;
}
# Try to serve static files from the public directory first
try_files $uri/index.html $uri @redmine;
location @redmine {
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Forwarded-Host $host;
proxy_set_header Host $http_host;
proxy_redirect off;
proxy_pass http://redmine;
proxy_read_timeout 300s;
proxy_connect_timeout 30s;
proxy_send_timeout 300s;
}
error_page 500 502 503 504 /500.html;
}
Enable and reload:
sudo ln -s /etc/nginx/sites-available/redmine /etc/nginx/sites-enabled/
sudo nginx -t && sudo systemctl reload nginx
Step 3: Configure Redmine’s base URL
Redmine uses the configured hostname to generate URLs in email notifications, attachment links, and internal references. If this is wrong, links in emails will point to the wrong URL or use HTTP.
Open the Redmine admin panel:
- Log in as admin.
- Go to Administration → Settings → General.
- Set Host name and path to
redmine.example.com(without protocol, without trailing slash). - Set Protocol to
HTTPS. - Click Save.
Or update via the Rails console:
sudo -u redmine bash -c "cd /var/www/redmine && bundle exec rails console production"
Setting.host_name = "redmine.example.com"
Setting.protocol = "https"
exit
These settings control how Redmine generates https://redmine.example.com/... URLs in emails and API responses.
Option B: nginx + Passenger (mod_rails)
Install Passenger:
gem install passenger
passenger-install-nginx-module
Or use the Phusion Passenger packages:
sudo apt install nginx-extras passenger
Create the nginx configuration with Passenger:
server {
listen 443 ssl;
http2 on;
server_name redmine.example.com;
ssl_certificate /etc/letsencrypt/live/redmine.example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/redmine.example.com/privkey.pem;
ssl_protocols TLSv1.2 TLSv1.3;
root /var/www/redmine/public;
passenger_enabled on;
passenger_ruby /usr/local/bin/ruby;
passenger_env_var RAILS_ENV production;
# Increase upload limit
client_max_body_size 50M;
}
Include Passenger configuration in the main nginx.conf:
http {
passenger_root /usr/local/lib/ruby/gems/3.x.x/gems/passenger-x.x.x;
passenger_ruby /usr/local/bin/ruby;
# ...
}
Option C: Apache + Passenger
For Apache-based deployments:
<VirtualHost *:80>
ServerName redmine.example.com
RewriteEngine On
RewriteRule ^ https://%{SERVER_NAME}%{REQUEST_URI} [END,QSA,R=permanent]
</VirtualHost>
<VirtualHost *:443>
ServerName redmine.example.com
SSLEngine on
SSLCertificateFile /etc/letsencrypt/live/redmine.example.com/fullchain.pem
SSLCertificateKeyFile /etc/letsencrypt/live/redmine.example.com/privkey.pem
SSLProtocol -All +TLSv1.2 +TLSv1.3
DocumentRoot /var/www/redmine/public
<Directory /var/www/redmine/public>
Allow from all
Options -MultiViews
Require all granted
</Directory>
PassengerEnabled on
PassengerAppEnv production
Header always set Strict-Transport-Security "max-age=63072000; includeSubDomains; preload"
</VirtualHost>
Configuring Redmine to detect HTTPS when behind a proxy
When nginx terminates TLS and proxies to Puma, Redmine needs to know the request was made over HTTPS. Nginx passes X-Forwarded-Proto: https for this.
In Redmine’s config/application.rb, ensure:
config.force_ssl = false # Let nginx handle redirects
And in config/environments/production.rb:
# Tell Rails to trust X-Forwarded-* headers from the proxy
config.web_console.allowed_request_origins = []
In config/initializers/session_store.rb:
Rails.application.config.session_store :cookie_store,
key: '_redmine_session',
secure: true, # Only send cookie over HTTPS
httponly: true
Restart Puma after configuration changes:
sudo systemctl restart redmine
Migrating existing HTTP links in Redmine content
After switching to HTTPS, existing wiki pages and issue descriptions may contain http:// links. Use Redmine’s rake task to update stored URLs:
cd /var/www/redmine
sudo -u redmine bundle exec rake redmine:convert_wiki_attachments_path RAILS_ENV=production
For find-and-replace of HTTP URLs in the database (PostgreSQL example):
UPDATE wiki_contents
SET text = REPLACE(text, 'http://redmine.example.com', 'https://redmine.example.com')
WHERE text LIKE '%http://redmine.example.com%';
UPDATE issues
SET description = REPLACE(description, 'http://redmine.example.com', 'https://redmine.example.com')
WHERE description LIKE '%http://redmine.example.com%';
UPDATE journals
SET notes = REPLACE(notes, 'http://redmine.example.com', 'https://redmine.example.com')
WHERE notes LIKE '%http://redmine.example.com%';
Always take a database backup before running UPDATE queries.
Email notifications with HTTPS links
After enabling HTTPS, verify that email notifications contain the correct HTTPS links:
- In Redmine admin: Administration → Settings → Email notifications.
- Click Send a test email to yourself.
- Check that the link in the email uses
https://.
If links still use http://, check:
- Administration → Settings → General → Protocol is set to HTTPS.
- Restart Redmine after the settings change.
Certificate renewal
sudo nano /etc/letsencrypt/renewal-hooks/deploy/redmine.sh
#!/bin/bash
# Reload nginx — Redmine itself does not need restarting
systemctl reload nginx
sudo chmod +x /etc/letsencrypt/renewal-hooks/deploy/redmine.sh
Troubleshooting
| Problem | Cause | Fix |
|---|---|---|
| Email links use http:// | Protocol setting not updated | Administration → Settings → General → Protocol = HTTPS |
| Cookie not set (login loop) | secure: true on cookie but accessing over HTTP | Always access via HTTPS; check redirect is working |
| File attachment download uses http:// | Host name setting wrong | Set correct host name in Administration → Settings → General |
| 502 Bad Gateway from nginx | Puma socket not running or wrong socket path | Check systemctl status redmine and socket path in nginx config |
| Assets not loading after HTTPS migration | Rails serving asset URLs with http:// | Set config.asset_host in production.rb or update nginx to pass correct headers |
| Slow response under load | Puma worker count too low | Increase workers in puma.rb and restart |
Summary
Redmine HTTPS requires an nginx reverse proxy with TLS termination, configured to pass X-Forwarded-Proto: https to the Ruby backend. The most important Redmine configuration is Administration → Settings → General where you set the hostname and protocol — this controls all generated URLs including email notification links and API responses. For Puma deployments, use a Unix socket for the nginx-to-Puma connection. Certificate renewal is handled by reloading nginx with the Certbot deploy hook.