← All documentation

Deploy Coldfeet behind Cloudflare (Full SSL)

Deploy Coldfeet behind Cloudflare (Full SSL)

This guide covers production deployment with Cloudflare Full SSL: the public dashboard and API are served over HTTPS via Cloudflare, while nginx on the origin listens on port 80 only (no origin TLS certificate required).

Mail (MX/SMTP) bypasses Cloudflare and uses a grey-cloud hostname or direct IP.

Architecture

TrafficHostnameCloudflareOrigin port
Dashboard + APIapp.example.comProxied (orange)80 → nginx
MX / SMTPmail.example.com or VPS IPDNS only (grey)25 / 2525 / 587
Grafana / PrometheusNot publiclocalhost only

One-command install

sudo bash install/install.sh \
  --public-hostname app.example.com \
  --mail-host mail.example.com

Cloudflare DNS

  1. Dashboard/API — Create an A record for app.example.com pointing to your VPS IP. Enable Proxied (orange cloud).
  2. Mail — Create an A record for mail.example.com pointing to the same IP. Set DNS only (grey cloud). Customer MX records should point to mail.example.com.

Cloudflare SSL/TLS

  1. SSL/TLS encryption mode: Full (not Flexible for production).
  2. Enable Always Use HTTPS.
  3. Optional: Minimum TLS Version 1.2+.

Cache rules

Bypass cache for dynamic paths:

Use a Cache Rule: If URI Path starts with /api → Bypass cache.

Rate limiting (optional)

Add a rate limit rule on the backend login endpoint /api/v1/auth/login (e.g. 10 requests per minute per IP).

Note: nginx proxies only /api/v1/* (plus /api/docs, /health, /metrics, /r/) to the backend API. NextAuth routes under /api/auth/* (sign-in callback, session, CSRF) are served by the web app and must not be routed to the backend.

SSO redirect URIs

Sign-in with Google, Entra ID or a generic OIDC provider needs DEPLOYMENT_MODE=cloudflare and OAUTH_LOGIN_ENABLED=true. These providers cover login only — mail always arrives over SMTP on the MX.

Firewall ports (UFW)

PortPurpose
80HTTP from Cloudflare to nginx
25MX (optional, --mx-port-25)
2525SMTP ingest
587Outbound SMTP STARTTLS

Do not expose 3000, 3001, 9090, or 3002 publicly. Grafana and Prometheus bind to 127.0.0.1 in docker-compose.prod.yml.

Ops access (Grafana / Prometheus)

From your workstation:

ssh -L 3002:127.0.0.1:3002 -L 9090:127.0.0.1:9090 user@your-vps

Then open http://localhost:3002 (Grafana) or http://localhost:9090 (Prometheus).

Alternatively use Cloudflare Access or a VPN for ops traffic.

Environment variables

Key production settings (set by installer with --public-hostname):

DEPLOYMENT_MODE=cloudflare
PUBLIC_URL=https://app.example.com
MAIL_HOST=mail.example.com
CLOUDFLARE_PROXY=true
WEB_URL=https://app.example.com
NEXTAUTH_URL=https://app.example.com
API_BASE_URL=https://app.example.com/api/v1
NEXT_PUBLIC_API_URL=https://app.example.com/api/v1
SCANNER_FAILURE_MODE=quarantine

Health check

After DNS propagates:

curl -sf https://app.example.com/api/health

Troubleshooting

ErrorCauseFix
525 SSL handshake failedOrigin not listening on 80Check nginx and docker compose ps
526 Invalid SSL certCloudflare set to Full (strict) without origin certUse Full not Full (strict), or add origin cert
OAuth redirect mismatchWrong callback URLMatch provider console to https://PUBLIC_HOSTNAME/api/auth/callback/...
Mail not arrivingMX proxied through CloudflareGrey-cloud mail. record; MX must not be orange-cloud

Backups

The installer schedules a nightly encrypted backup in /etc/cron.d/coldfeet-backup. Check the last run with cat backup_data/latest.json, and see BACKUP-RESTORE.md for restore steps and the drill.

Changing app or mail hostname (super admin)

After install you can change platform hostnames without editing SSH files manually:

  1. Sign in as super admin → Admin → Platform settings (/admin/settings).
  2. Set App hostname (dashboard + API) and Mail hostname (MX target for all customer domains).
  3. Click Validate DNS — checks A records and health on the proposed URL.
  4. Click Apply & restart services — writes .env.production, regenerates nginx snippet, and restarts API/web (when PLATFORM_APPLY_EXEC=true on the API host, or run install/apply-platform.sh).

Customer domains (per tenant) stay in the database; only platform MX copy in the domain wizard updates to the new MAIL_HOST.

For origin TLS with Cloudflare Full (strict), see DEPLOY-TLS-ORIGIN.md.