# Running Discourse with WordPress (Docker) on a single VPS using Nginx Reverse Proxy

**URL:** https://meta.discourse.org/t/running-discourse-with-wordpress-docker-on-a-single-vps-using-nginx-reverse-proxy/392094
**Category:** Sysadmins
**Tags:** wordpress
**Created:** [December 24, 2025, 4:14pm UTC](https://meta.discourse.org/t/running-discourse-with-wordpress-docker-on-a-single-vps-using-nginx-reverse-proxy/392094 "2025-12-24T16:14:24Z")
**Posts on this page:** 1
**Page:** 1

<div class="post-metadata">

### Author: ![thaidb](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/thaidb/32/68488_2.png) [@thaidb](https://meta.discourse.org/u/thaidb)
#### Post date: [December 24, 2025, 4:14pm UTC](https://meta.discourse.org/t/running-discourse-with-wordpress-docker-on-a-single-vps-using-nginx-reverse-proxy/392094/1 "2025-12-24T16:14:24Z")

</div>

### **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:

1. **Change Port Mapping:**

2. **Disable Internal SSL:** Comment out the SSL and Let’s Encrypt templates:

3. **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

```plaintext
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

```plaintext
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 as `root`, you must run `chown -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 down` or `rebuild` without 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.
