From d526025926bbd64ae9a70c84e51ef8df4edeeb97 Mon Sep 17 00:00:00 2001 From: olsch01 Date: Mon, 2 Mar 2026 19:59:24 -0500 Subject: [PATCH] fix: map Docker nginx to port 8080 to avoid conflict with host reverse proxy The base docker-compose.yml maps nginx to 80:80, which conflicts with the host-level nginx that handles SSL termination on production servers. The production overlay now explicitly maps to 8080:80 so the host proxy can forward to localhost:8080. Updated DEPLOYMENT.md with host reverse proxy setup instructions and corrected architecture diagrams. Co-Authored-By: Claude Opus 4.6 --- docker-compose.prod.yml | 5 ++- docs/DEPLOYMENT.md | 84 +++++++++++++++++++++++++++++++++++------ 2 files changed, 77 insertions(+), 12 deletions(-) diff --git a/docker-compose.prod.yml b/docker-compose.prod.yml index 5c8774f..3b83e2c 100644 --- a/docker-compose.prod.yml +++ b/docker-compose.prod.yml @@ -10,10 +10,13 @@ # - Restart policies for reliability # # SSL/TLS is handled at the host level (e.g., host nginx + certbot). -# The Docker nginx container listens on port 80 only. +# The Docker nginx container listens internally on port 80, mapped to +# host port 8080 so it doesn't conflict with the host reverse proxy. services: nginx: + ports: + - "8080:80" # override: avoid conflict with host nginx volumes: - ./nginx/production.conf:/etc/nginx/conf.d/default.conf:ro restart: unless-stopped diff --git a/docs/DEPLOYMENT.md b/docs/DEPLOYMENT.md index 17d76c8..fc0fd9b 100644 --- a/docs/DEPLOYMENT.md +++ b/docs/DEPLOYMENT.md @@ -171,14 +171,54 @@ nano .env docker compose -f docker-compose.yml -f docker-compose.prod.yml up -d --build ``` -To add SSL on top of the production stack: +> **Port mapping:** The production overlay maps the Docker nginx to **host +> port 8080** (not 80) so it doesn't conflict with a host-level reverse +> proxy. If you're NOT using a host reverse proxy, you can override this: +> ```bash +> # Direct access on port 80 (no host reverse proxy) +> docker compose -f docker-compose.yml -f docker-compose.prod.yml \ +> up -d --build --scale nginx=0 +> # Then manually: docker compose ... run -d -p 80:80 nginx +> ``` +### Host reverse proxy setup (recommended) + +In production, SSL termination is typically handled by a **host-level nginx** +(or Caddy, Traefik, etc.) that proxies to the Docker stack on port 8080: + +```nginx +# /etc/nginx/sites-available/app.yourdomain.com +server { + listen 80; + server_name app.yourdomain.com; + return 301 https://$host$request_uri; +} + +server { + listen 443 ssl; + server_name app.yourdomain.com; + + ssl_certificate /etc/letsencrypt/live/app.yourdomain.com/fullchain.pem; + ssl_certificate_key /etc/letsencrypt/live/app.yourdomain.com/privkey.pem; + + location / { + proxy_pass http://127.0.0.1:8080; + proxy_http_version 1.1; + 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 Upgrade $http_upgrade; + proxy_set_header Connection "upgrade"; + } +} +``` + +Then enable and reload: ```bash -docker compose \ - -f docker-compose.yml \ - -f docker-compose.prod.yml \ - -f docker-compose.ssl.yml \ - up -d --build +sudo ln -s /etc/nginx/sites-available/app.yourdomain.com /etc/nginx/sites-enabled/ +sudo certbot --nginx -d app.yourdomain.com # if using certbot on host +sudo nginx -t && sudo systemctl reload nginx ``` > **Tip:** Create a shell alias to avoid typing the compose files every time: @@ -690,8 +730,9 @@ docker compose logs -f nginx # nginx access/error log ## Architecture Overview ``` + Development: ┌──────────────────┐ - Browser ─────────► │ nginx :80/:443 │ + Browser ─────────► │ nginx :80 │ └────────┬─────────┘ ┌──────────┴──────────┐ ▼ ▼ @@ -701,10 +742,31 @@ docker compose logs -f nginx # nginx access/error log └──────┬───────┘ └──────────────┘ ┌────┴────┐ ▼ ▼ - ┌────────────┐ ┌───────────┐ ┌───────────┐ - │postgres:5432│ │redis :6379│ │ certbot │ - │ (PG 15) │ │ (Redis 7) │ │ (renewal) │ - └────────────┘ └───────────┘ └───────────┘ + ┌────────────┐ ┌───────────┐ + │postgres:5432│ │redis :6379│ + │ (PG 15) │ │ (Redis 7) │ + └────────────┘ └───────────┘ + + Production (with host reverse proxy): + ┌──────────────────────┐ + Browser ─────────► │ Host nginx :80/:443 │ ← SSL termination + └────────┬─────────────┘ + ▼ + ┌──────────────────┐ + │ Docker nginx:8080│ ← proxy to services + └────────┬─────────┘ + ┌──────────┴──────────┐ + ▼ ▼ + ┌──────────────┐ ┌──────────────┐ + │ backend :3000│ │frontend :3001│ + │ (compiled) │ │ (static nginx)│ + └──────┬───────┘ └──────────────┘ + ┌────┴────┐ + ▼ ▼ + ┌────────────┐ ┌───────────┐ + │postgres:5432│ │redis :6379│ + │ (PG 15) │ │ (Redis 7) │ + └────────────┘ └───────────┘ ``` **Multi-tenant isolation:** Each HOA organization gets its own PostgreSQL