Skip to content

Production Deployment

This guide covers deploying Nodebyte to production with Docker Compose. Follow each section to ensure a secure, reliable deployment.

Before going live, address every item:

  • JWT_SECRET — set to a long random string: openssl rand -hex 32
  • POSTGRES_PASSWORD — set to a strong, unique password
  • COOKIE_SECURE=true — required when serving over HTTPS
  • COOKIE_SAMESITE=lax — or strict if frontend and API share a domain
  • FRONTEND_ORIGIN — set to your actual frontend URL (e.g. https://nodebyte.example.com)
  • NEXT_PUBLIC_API_BASE_URL — set to your actual backend URL
  • NODEBYTE_ENV=production — enables security headers and restricts API docs visibility
  • Turnstile — replace test keys with real Cloudflare Turnstile keys, or set TURNSTILE_ENABLED=false
  • Uvicorn workers — remove --reload from backend/entrypoint.sh and add --workers N
  • HTTPS — terminate TLS in front of the containers
  • Database backups — ensure nodebyte_postgres volume is backed up

Nodebyte needs a reverse proxy for TLS termination. Here are configurations for common options.

server {
listen 443 ssl http2;
server_name nodebyte.example.com;
ssl_certificate /etc/letsencrypt/live/nodebyte.example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/nodebyte.example.com/privkey.pem;
location / {
proxy_pass http://127.0.0.1:3000;
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;
}
location /api/ {
proxy_pass http://127.0.0.1:8000;
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;
}
}
server {
listen 80;
server_name nodebyte.example.com;
return 301 https://$host$request_uri;
}
nodebyte.example.com {
handle /api/* {
reverse_proxy localhost:8000
}
handle {
reverse_proxy localhost:3000
}
}

Caddy automatically provisions and renews TLS certificates via Let’s Encrypt.

If you’re using Traefik (e.g. in a Docker or Kubernetes setup), add labels to your docker-compose.yml:

services:
frontend:
labels:
- "traefik.enable=true"
- "traefik.http.routers.nodebyte.rule=Host(`nodebyte.example.com`)"
- "traefik.http.routers.nodebyte.tls.certresolver=letsencrypt"
- "traefik.http.services.nodebyte.loadbalancer.server.port=3000"

When NODEBYTE_ENV=production, the backend automatically adds security headers:

  • Strict-Transport-Security (HSTS)
  • X-Frame-Options: DENY
  • X-Content-Type-Options: nosniff
  • X-XSS-Protection: 1; mode=block

Additional recommendations:

  • Rate limiting — the backend includes built-in rate limiting on auth endpoints
  • Firewall — restrict direct access to ports 8000 and 5432 from the public internet; only expose the reverse proxy (443)
  • Registration — set REGISTRATION_ENABLED=false for invite-only deployments

The PostgreSQL data lives in the nodebyte_postgres Docker volume. Back it up regularly:

Terminal window
docker compose exec db pg_dump -U nodebyte nodebyte > backup_$(date +%Y%m%d).sql

To restore:

Terminal window
docker compose exec -T db psql -U nodebyte nodebyte < backup_20250101.sql

For automated backups, set up a cron job:

Terminal window
0 2 * * * cd /path/to/nodebyte && docker compose exec -T db pg_dump -U nodebyte nodebyte | gzip > /backups/nodebyte_$(date +\%Y\%m\%d).sql.gz

For higher throughput, increase Uvicorn workers. Edit backend/entrypoint.sh:

Terminal window
uvicorn app.main:app --host 0.0.0.0 --port 8000 --workers 4

A good starting point is 2 * CPU cores + 1.