• Skip to secondary menu
  • Skip to main content
  • Skip to primary sidebar
  • Home
  • Projects
  • Products
  • Themes
  • Tools
  • Request for Quote

Vengala Vinay

Having 12+ Years of Experience in Software Development

  • Home
  • WordPress
  • PHP
    • Codeigniter
  • Django
  • Magento
  • Selenium
  • Server
Home » Setting up Docker-based Discourse instances behind an isolated Nginx reverse proxy server on openSUSE Leap 15.5

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.

Reader Interactions

Leave a Reply Cancel reply

You must be logged in to post a comment.

Primary Sidebar

A little about the Author

Having 12+ Years of Experience in Software Development, Vinay is a principal software architect, senior systems engineer, and elite technical consultant. He specializes in bespoke PHP/WordPress development, high-performance Magento 2 & Shopify architectures, custom plugin/theme development from scratch, and legacy code modernization (including VB6, VB.NET, PyQt, and Crystal Reports). Known for solving complex database bottlenecks, speed optimization (Core Web Vitals), and advanced security code auditing, Vinay engineers production-ready systems designed to scale under heavy concurrent load conditions.



Chat on WhatsApp

Recent Posts

  • Debugging Guide: Diagnosing PHP-FPM child process pool exhaustion in multi-site network environments with modern tools
  • Debugging and Resolving complex namespace class loading collisions issues during heavy concurrent database traffic
  • Step-by-Step Guide: Offloading high-frequency customer support tickets metadata writes to a Redis KV store
  • How to refactor legacy event ticket registers queries using modern WP_Query and custom Transient caching
  • Step-by-Step Guide: Offloading high-frequency member profile directories metadata writes to a Redis KV store

Categories

  • apache (1)
  • Business & Monetization (390)
  • Centos (4)
  • Comparisons & Decision Making (55)
  • Debian (2)
  • Debugging & Troubleshooting (662)
  • Desktop Applications (14)
  • DevOps (7)
  • DevOps & Cloud Scaling (962)
  • Django (1)
  • Laravel (4)
  • Migration & Architecture (192)
  • Mobile Applications (24)
  • MySQL (1)
  • Performance & Optimization (873)
  • PHP (5)
  • PHP Development (49)
  • Plugins & Themes (244)
  • Programming Languages (9)
  • Python (20)
  • Ruby on Rails (1)
  • Security & Compliance (647)
  • SEO & Growth (492)
  • Server (118)
  • Ubuntu (9)
  • VB6 & VB.NET (8)
  • Web Applications & Frontend (19)
  • Web Assembly (Wasm) (2)
  • WordPress (22)
  • WordPress Plugin Development (726)
  • WordPress Theme Development (357)

Recent Posts

  • Debugging Guide: Diagnosing PHP-FPM child process pool exhaustion in multi-site network environments with modern tools
  • Debugging and Resolving complex namespace class loading collisions issues during heavy concurrent database traffic
  • Step-by-Step Guide: Offloading high-frequency customer support tickets metadata writes to a Redis KV store

Top Categories

  • DevOps & Cloud Scaling (962)
  • Performance & Optimization (873)
  • WordPress Plugin Development (726)
  • Debugging & Troubleshooting (662)
  • Security & Compliance (647)
  • SEO & Growth (492)

Our Products

  • ERP & LMS Systems (4)
  • Directories & Marketplaces (4)
  • Healthcare Portals (3)
  • Point of Sale (POS) (2)
  • E-Commerce Engines (2)

Our Services

  • E-Commerce Development (10)
  • WordPress Development (8)
  • Python & Desktop GUI (7)
  • General Consulting (7)
  • Legacy Modernization (5)
  • Mobile App Development (4)

Copyright © 2026 · Vinay Vengala