Introduction
By default, a Discourse “standalone” installation binds to ports 80 and 443. To host another application like WordPress on the same server, you must reconfigure Discourse to listen on an internal port and use a Host-level Nginx as a Reverse Proxy to manage traffic and SSL certificates.
1. Architecture Overview
-
Host Nginx: The primary gateway listening on ports 80 and 443. It handles SSL termination and routes requests to the appropriate container based on the
server_name. -
Discourse Container: Reconfigured to listen on
localhost:8080. -
WordPress Container: Managed via Docker Compose, listening on
localhost:8081.
2. Phase A: Reconfiguring Discourse
Modify your /var/discourse/containers/app.yml to relinquish the public ports:
-
Change Port Mapping:
YAML
expose: - "8080:80" # Maps host port 8080 to container port 80 -
Disable Internal SSL: Comment out the SSL and Let’s Encrypt templates:
YAML
templates: - "templates/postgres.template.yml" - "templates/redis.template.yml" - "templates/web.template.yml" # - "templates/web.ssl.template.yml" # - "templates/web.letsencrypt.ssl.template.yml" -
Rebuild: Run
./launcher rebuild app.
3. Phase B: Deploying WordPress via Docker Compose
Organize your WordPress site in a dedicated directory. Ensure the database volume is persistent to prevent data loss.
YAML
services:
db:
image: mariadb:10.11
environment:
MYSQL_ROOT_PASSWORD: 'your_secure_password'
volumes:
- ./mysql_data:/var/lib/mysql
wordpress:
image: wordpress:latest
ports:
- "8081:80"
volumes:
- .:/var/www/html
4. Phase C: Final Nginx Configuration (SSL & Port 443)
A professional setup in 2025 requires full HTTPS and HTTP/2 support. After installing Nginx on your host (sudo apt install nginx), create a configuration for your domains.
Pro Tip: Run sudo certbot --nginx to automatically generate the SSL blocks, but ensure they include the following proxy headers so Discourse functions correctly.
Nginx
server {
listen 443 ssl http2;
server_name discourse.com;
# SSL Certs by Certbot
ssl_certificate /etc/letsencrypt/live/discourse.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/discourse.com/privkey.pem;
location / {
proxy_pass http://localhost:8080;
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; # Crucial for Discourse HTTPS detection
}
}
5. Hard-Won Lessons & Best Practices
-
Database Credentials: If your passwords contain special characters like
&or?, always wrap them in single quotes' 'in your config files and terminal commands to prevent shell interpretation errors. -
File Permissions: WordPress containers run as
www-data(UID 33). If you upload or unzip files asroot, you must runchown -R 33:33 .to avoid 500 Internal Server Errors. -
Cloudflare Settings: When using an on-server SSL certificate (Let’s Encrypt), set Cloudflare SSL/TLS to Full (Strict). This prevents the “Too Many Redirects” loop commonly caused by the “Flexible” mode.
-
Persistent Volumes: Never run
docker compose downorrebuildwithout verifying your database files are stored in a persistent volume (e.g.,./mysql_data).
Conclusion: Decoupling your apps from ports 80/443 using a Reverse Proxy is the most scalable way to manage a multi-site VPS. It allows for centralized SSL management and easy debugging through host-level Nginx logs.