Skip to main content

Make sure Traefik works when you deploy

When you deploy to a Linux server (not local testing), Traefik is the reverse proxy that routes traffic by hostname to the API, frontend, and admin. Follow this checklist so routing and HTTPS work correctly.

Config verified for VPS: The self-host kit includes traefik/traefik.yml and traefik/dynamic/traefik-dynamic.yml set up for production: entrypoints 80/443/8080/8082, Docker provider (reads labels from api, frontend, admin, websocket-worker, chat-worker), file provider for middlewares. API has priority 1; /hubs/notification and /hubs/onlineusers go to websocket-worker; /hubs/chat and /hubs/chat-online go to chat-worker (priority 100). WebSocket upgrade is handled by Traefik automatically. On the server, run without docker-compose.local.yml so Traefik starts.

1. Use the right compose command (no local override)

On the server, do not use docker-compose.local.yml. That file is only for local testing and leaves Traefik stopped.

docker compose pull
docker compose up -d

This starts Traefik and all services. Traefik listens on 80 (HTTP) and 443 (HTTPS).

2. Set your real hostnames in .env

Traefik routes by hostname. Every TRAEFIK_*_HOST in .env must be set to the hostnames that will point to this server (no https://, no port).

Check before starting: from the folder that contains docker-compose.yml (e.g. the dockerPublish self-host kit) run:

./scripts/check-traefik-env.sh

This warns if TRAEFIK_API_HOST, TRAEFIK_FRONTEND_HOST, or TRAEFIK_ADMIN_HOST are missing or still contain placeholders like your-domain.

VariableExampleMust match
TRAEFIK_API_HOSTapi.yourdomain.comDNS for API
TRAEFIK_FRONTEND_HOSTapp.yourdomain.comDNS for main app
TRAEFIK_ADMIN_HOSTadmin.yourdomain.comDNS for admin panel
TRAEFIK_DASHBOARD_HOSTdashboard.yourdomain.com (optional)If you expose Traefik dashboard
TRAEFIK_DASHBOARD_IPYour server's public IPSo http://YOUR_IP:8080 works for dashboard
TRAEFIK_FRONTEND_WWW_HOSTwww.yourdomain.com (optional)For SEO: www is redirected 301 to main domain; set if you use www

Also set the public URLs to match the same hostnames:

API_PUBLIC_URL=https://api.yourdomain.com
FRONTEND_PUBLIC_URL=https://app.yourdomain.com
ADMIN_PUBLIC_URL=https://admin.yourdomain.com

If you leave TRAEFIK_*_HOST as placeholders (e.g. api.your-domain.com or bellamybook.com), Traefik will only respond to those hostnames and your real domain will get 404 or wrong routing.

3. Configure Traefik dynamic file

File: traefik/dynamic/traefik-dynamic.yml (in the same folder as docker-compose.yml).

Traefik loads this file for middlewares (security headers, optional WebSocket-related headers) and for optional www→canonical redirects. The kit ships with placeholder values (your-domain.com, www.your-domain.com) in the redirect-www-to-canonical middleware and in the frontend-www-redirect / frontend-www-redirect-secure routers.

You need to align this file with your deployment:

SituationWhat to do
You use www (e.g. www.yourdomain.comapp.yourdomain.com)Replace every placeholder in traefik-dynamic.yml with your real domain. Set TRAEFIK_FRONTEND_WWW_HOST=www.yourdomain.com in .env, add www to CORS and Turnstile (see .env.example), and point DNS for www to this server. The redirect should send users to the same host you use as FRONTEND_PUBLIC_URL (your canonical frontend URL).
You do not use wwwRemove or comment out the redirect-www-to-canonical middleware block and the two frontend-www-redirect routers so Traefik does not register routes for www.your-domain.com. Otherwise you leave inactive stubs; cleaning them up avoids confusion and accidental copy-paste mistakes later.

After editing, restart Traefik (e.g. docker compose up -d traefik) so the file provider reloads.

3b. Keep CSP config aligned (frontend/admin nginx)

Even when Traefik routes correctly, UI can still break if CSP in frontend/admin nginx blocks required domains.

Required files to review after security updates:

FileWhy it matters
traefik/dynamic/traefik-dynamic.ymlSecurity headers and redirect routers loaded by Traefik file provider
Src/frontend/nginx.confFrontend CSP (script-src, connect-src, img-src, media-src)
Src/admin/nginx.confAdmin CSP and API/WebSocket/storage allowlist

Minimum domains typically needed in CSP:

  • API + WS: https://api.yourdomain.com, wss://api.yourdomain.com
  • Storage: your MinIO public URL or https://r2.yourdomain.com
  • Cloudflare challenge/insights
  • Optional analytics/livekit domains if enabled

If you use pre-built images only, nginx CSP changes require a new frontend/admin image tag from the publisher.

4. Point DNS to the server

For each hostname you use, create an A (or AAAA for IPv6) record pointing to this server's public IP.

Example: api.yourdomain.com203.0.113.10

Wait for DNS to propagate. Check with:

dig +short api.yourdomain.com

5. Open ports on the server

Traefik needs:

  • 80 (HTTP)
  • 443 (HTTPS)

Optional:

  • 8080 – Traefik dashboard (only if you use TRAEFIK_DASHBOARD_IP or a dashboard hostname)

On the server:

  • Firewall: allow inbound 80, 443 (and 8080 if you use the dashboard).
  • Cloud / security groups: allow the same ports to this host.

By default, Traefik has HTTP (80) and HTTPS (443) entrypoints, but no TLS certificate provider is enabled. Configure one of the following.

Option A – Let's Encrypt (Traefik on the server)

  1. In traefik/traefik.yml, uncomment the certificatesResolvers block and set your email.
  2. Ensure the traefik_letsencrypt volume exists (it's in the compose file) and that the ACME storage file has correct permissions (e.g. chmod 600).
  3. Attach the resolver to your HTTPS routers (e.g. certificatesResolvers: letsencrypt) so Traefik requests certs for your hostnames.

Option B – Cloudflare Tunnel

  • Run the tunnel in front of the server; Cloudflare terminates HTTPS.
  • Point the tunnel to your server's HTTP (80) or HTTPS (443) as per Cloudflare docs. No need to enable Let's Encrypt in Traefik.

Option C – Another reverse proxy

  • Terminate HTTPS on nginx, HAProxy, or Caddy and proxy to Traefik on 80 (or 443). Traefik itself doesn't need TLS.

7. Quick check that Traefik is working

  1. Containers: Run docker compose ps. Ensure traefik, api, frontend, and admin are running.

  2. Traefik dashboard (optional): Open http://YOUR_SERVER_IP:8080 (set TRAEFIK_DASHBOARD_IP to that IP in .env). Check that HTTP routers exist for api, frontend, and admin with the correct hostnames.

  3. HTTP (port 80): From your machine:

    curl -H "Host: app.yourdomain.com" http://YOUR_SERVER_IP/

    You should get the frontend (or a redirect), not a connection error or Traefik 404.

  4. Real hostname: Once DNS points to the server:

    curl -I https://app.yourdomain.com/

    (or http:// if HTTPS isn't set up yet). You should get 200 or 301/302, not 404.

Summary

StepAction
1On server use docker compose up -d (no -f docker-compose.local.yml)
2Set all TRAEFIK_*_HOST and *_PUBLIC_URL in .env to your domain hostnames; run ./scripts/check-traefik-env.sh
3Edit traefik/dynamic/traefik-dynamic.yml: replace www/canonical placeholders with your domain, or remove the www redirect middleware and routers if you do not use www; restart Traefik
4Point DNS (A/AAAA) for those hostnames (and www if used) to this server
5Open ports 80 and 443 (and 8080 if using dashboard)
6Configure HTTPS (Let's Encrypt, Cloudflare Tunnel, or another proxy)

Next steps