2.3 KiB
2.3 KiB
Production Deployment Guide
This guide describes how to deploy the Customer Portal in production with Docker Compose, Nginx reverse proxy, and automatic TLS renewal via Certbot.
Prerequisites
- Domain name pointed to your server (A/AAAA records)
- Docker and Docker Compose installed
- Open ports 80 and 443
Environment configuration
- Copy
.env.production.exampleto.envat the repository root:
cp .env.production.example .env
- Fill in all required values, especially:
- DATABASE_URL (matches docker-compose Postgres credentials)
- REDIS_URL (usually redis://cache:6379)
- JWT_SECRET (strong, random)
- CORS_ORIGIN (e.g., https://yourdomain.com)
- BFF_PORT (e.g., 4000)
- NEXT_PUBLIC_API_BASE (e.g., https://yourdomain.com)
- Salesforce/WHMCS credentials and key path
First-time TLS certificate
Run certbot once to issue a certificate (HTTP challenge):
docker compose -f docker/prod/docker-compose.yml run --rm \
-p 80:80 \
-e "CERTBOT_DOMAIN=yourdomain.com" \
-e "CERTBOT_EMAIL=you@example.com" \
certbot certonly --webroot -w /var/www/certbot \
-d yourdomain.com --agree-tos --no-eff-email
- Certificates will be stored under the
letsencryptvolume mounted at/etc/letsencrypt. - Nginx is configured to use
/etc/letsencrypt/live/portalby default. You can update the path or create a symlink to match your issued cert name.
Deploy
pnpm prod:deploy
This will:
- Build images
- Start database and cache, wait for health
- Run migrations
- Start proxy, frontend, backend
Health checks
- Proxy/Frontend: https://yourdomain.com/healthz
- Backend: proxied at https://yourdomain.com/api/health (and container-local http://backend:4000/health)
Renewals
- A certbot sidecar runs and renews every ~12 hours. On renew, it reloads Nginx.
Notes on security
- HSTS, modern TLS ciphers, and security headers are enabled in Nginx
- Backend disables x-powered-by and supports trust proxy
- Avoid exposing container ports directly; use the proxy service
- Keep
.envoutside of version control
Updates
pnpm prod:update
This rebuilds and recreates frontend and backend without downtime.
Troubleshooting
- Check logs:
pnpm prod:logs
- Validate Nginx config inside container:
docker exec -it portal-proxy nginx -t