Setting up Docker-based Discourse instances behind an isolated Nginx reverse proxy server on openSUSE Leap 15.5
Prerequisites and Initial Setup
This guide assumes you have two distinct openSUSE Leap 15.5 servers: one dedicated to running Dockerized Discourse and its dependencies (let’s call it discourse-host), and another acting as an isolated Nginx reverse proxy (proxy-host). Both servers should have static IP addresses and be accessible within your internal network. Ensure that discourse-host has sufficient RAM (minimum 8GB recommended for production) and CPU resources. On both servers, update the system and install necessary packages.
On both servers, execute the following commands:
sudo zypper refresh sudo zypper update -y sudo zypper install -y docker docker-compose git curl wget
After installation, start and enable the Docker service on both machines:
sudo systemctl start docker sudo systemctl enable docker sudo systemctl start docker-compose sudo systemctl enable docker-compose
Verify Docker is running:
sudo docker info
Configuring the Discourse Host
On the discourse-host, we’ll set up the Discourse instance using Docker Compose. This involves cloning the official Discourse Docker image repository and configuring it. First, create a directory for your Discourse configuration and navigate into it.
mkdir ~/discourse cd ~/discourse
Clone the Discourse Docker image repository:
git clone https://github.com/discourse/discourse_docker.git .
Now, copy the example configuration file and edit it. Replace your_discourse_domain.com with your actual domain name and [email protected] with your Let’s Encrypt email. Crucially, set DISCOURSE_HOSTNAME to the domain name that will be accessible externally, and LETSENCRYPT_EMAIL for SSL certificate renewal.
cp samples/app.yml samples/production.yml nano samples/production.yml
Inside samples/production.yml, locate and modify these lines:
# ... other settings ... DISCOURSE_HOSTNAME: your_discourse_domain.com LETSENCRYPT_EMAIL: [email protected] # ... other settings ...
Save the file. Before building the image, ensure that the discourse-host‘s firewall allows inbound traffic on ports 80 and 443 if you plan to expose it directly for initial setup or testing. However, for the isolated proxy setup, this is less critical on the Discourse host itself, as traffic will be proxied.
Build the Discourse Docker image. This can take a significant amount of time depending on your server’s resources.
./launcher bootstrap production
Once the bootstrap is complete, start the Discourse container:
./launcher start app
At this stage, Discourse should be running and accessible via the discourse-host‘s IP address on port 80 (or 443 if Let’s Encrypt was configured and successful). However, we will not be accessing it directly. We need to configure Nginx on the proxy-host to route traffic.
Setting up the Isolated Nginx Reverse Proxy
On the proxy-host, we will configure Nginx to act as a reverse proxy for the Discourse instance. This isolates Discourse from direct internet exposure and centralizes SSL termination and access control. First, install Nginx if you haven’t already.
sudo zypper install -y nginx
Stop the default Nginx service and disable it, as we will manage Nginx via Docker Compose for consistency with the Discourse setup, or manage it manually. For this guide, we’ll assume manual Nginx configuration for simplicity on the proxy host.
sudo systemctl stop nginx sudo systemctl disable nginx
Create a new Nginx configuration file for Discourse. Replace your_discourse_domain.com with your actual domain and DISCOURSE_HOST_IP with the internal IP address of your discourse-host.
sudo nano /etc/nginx/conf.d/discourse.conf
Paste the following Nginx configuration into the file. This configuration handles HTTP to HTTPS redirection, SSL termination, and proxies requests to the Discourse container. Note that Discourse’s Docker image typically runs its internal web server on port 80. We are proxying to the discourse-host‘s IP on port 80.
# Redirect HTTP to HTTPS
server {
listen 80;
server_name your_discourse_domain.com;
location / {
return 301 https://$host$request_uri;
}
}
# HTTPS server block
server {
listen 443 ssl http2;
server_name your_discourse_domain.com;
# SSL certificate paths (replace with your actual paths)
# These will be managed by Certbot on this proxy server
ssl_certificate /etc/letsencrypt/live/your_discourse_domain.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/your_discourse_domain.com/privkey.pem;
ssl_trusted_certificate /etc/letsencrypt/live/your_discourse_domain.com/chain.pem;
# Recommended SSL settings
ssl_protocols TLSv1.2 TLSv1.3;
ssl_prefer_server_ciphers on;
ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384;
ssl_session_cache shared:SSL:10m;
ssl_session_timeout 10m;
ssl_session_tickets off;
ssl_stapling on;
ssl_stapling_verify on;
resolver 8.8.8.8 8.8.4.4 valid=300s; # Google DNS, adjust if needed
resolver_timeout 5s;
# HSTS (optional but recommended)
# add_header Strict-Transport-Security "max-age=63072000; includeSubDomains; preload" always;
location / {
proxy_pass http://DISCOURSE_HOST_IP:80; # Proxy to Discourse host on port 80
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
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 X-Forwarded-Port $server_port;
# WebSocket support for Discourse
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
}
# Optional: Add security headers
add_header X-Frame-Options "SAMEORIGIN";
add_header X-Content-Type-Options "nosniff";
add_header Referrer-Policy "strict-origin-when-cross-origin";
add_header Content-Security-Policy "default-src 'self' *.your_discourse_domain.com; script-src 'self' 'unsafe-inline' 'unsafe-eval'; connect-src 'self' *.your_discourse_domain.com; img-src 'self' data:; style-src 'self' 'unsafe-inline'; font-src 'self' data:;";
}
Replace DISCOURSE_HOST_IP with the actual IP address of your discourse-host. After saving the configuration, test Nginx for syntax errors:
sudo nginx -t
If the test is successful, start and enable the Nginx service:
sudo systemctl start nginx sudo systemctl enable nginx
SSL Certificate Management with Certbot
To secure your Discourse instance with HTTPS, you’ll use Certbot to obtain and manage Let’s Encrypt SSL certificates on the proxy-host. Ensure that port 80 is open on the proxy-host‘s firewall for the ACME challenge.
sudo zypper install -y certbot python311-certbot-nginx
Run Certbot to obtain the certificate. This command will automatically detect the domain from your Nginx configuration and attempt to obtain a certificate. It will also modify your Nginx configuration to include the SSL directives.
sudo certbot --nginx -d your_discourse_domain.com
Follow the prompts. Certbot will likely ask if you want to redirect HTTP traffic to HTTPS, which you should accept. It will also automatically configure the SSL settings in the /etc/nginx/conf.d/discourse.conf file. If it doesn’t, you may need to manually add the SSL directives as shown in the previous Nginx configuration section.
Certbot automatically sets up a cron job or systemd timer for certificate renewal. You can test the renewal process with:
sudo certbot renew --dry-run
Firewall Configuration
Ensure your firewalls are configured correctly. On the proxy-host, you need to allow inbound traffic on ports 80 and 443.
# On proxy-host sudo firewall-cmd --permanent --add-service=http sudo firewall-cmd --permanent --add-service=https sudo firewall-cmd --reload
On the discourse-host, you only need to allow inbound traffic from the proxy-host‘s IP address on port 80. All other external access should be blocked.
# On discourse-host # Assuming your proxy-host IP is 192.168.1.100 sudo firewall-cmd --permanent --add-rich-rule='rule family="ipv4" source address="192.168.1.100" port port="80" protocol="tcp" accept' sudo firewall-cmd --reload
If you are using ufw instead of firewalld, the commands would be similar but use ufw allow syntax.
Testing and Verification
Once all configurations are in place, access your Discourse instance through your domain name (e.g., https://your_discourse_domain.com) from a client machine. You should be redirected to the HTTPS version, and the Discourse interface should load correctly. Check the Nginx access and error logs on the proxy-host for any issues:
sudo tail -f /var/log/nginx/access.log sudo tail -f /var/log/nginx/error.log
On the discourse-host, check the Docker logs for the Discourse container:
cd ~/discourse ./launcher logs app
This setup provides a robust and secure way to host Discourse, leveraging Docker for easy management and an isolated Nginx proxy for enhanced security and SSL handling.
Leave a Reply
You must be logged in to post a comment.