Traefik Reverse Proxy¶
Traefik is a Docker-native reverse proxy that reads Docker labels to automatically configure routing. If you're already running Traefik (common in self-hosted setups), adding Dreadnought is straightforward.
Prerequisites¶
- Traefik running as a Docker container on the same Docker host
- A Traefik network (commonly named
traefikorproxy) - Traefik configured with a Let's Encrypt certificate resolver
- DNS records for your subdomains pointing to your server
Architecture¶
Internet (HTTPS)
│
▼
Traefik (listens on 80 + 443)
│
├── ddns.yourdomain.com ──► web container (port 3000)
│
└── ddns-api.yourdomain.com ──► api container (port 8000)
(Traefik communicates with containers via Docker network labels)
Step 1 — Set NEXT_PUBLIC_API_URL¶
Edit docker-compose.yml (or your .env) and set the API's public URL:
web:
environment:
- NEXT_PUBLIC_API_URL=https://ddns-api.yourdomain.com
- NODE_ENV=production
Step 2 — Create docker-compose.override.yml¶
Create a docker-compose.override.yml file alongside docker-compose.yml. Docker Compose automatically merges this with the base file.
services:
web:
environment:
- NEXT_PUBLIC_API_URL=https://ddns.yourdomain.com
labels:
- traefik.enable=true
- traefik.http.routers.ddns-web.rule=Host(`ddns.yourdomain.com`)
- traefik.http.routers.ddns-web.entrypoints=websecure
- traefik.http.routers.ddns-web.tls=true
- traefik.http.routers.ddns-web.tls.certresolver=letsencrypt
- traefik.http.services.ddns-web.loadbalancer.server.port=3000
api:
labels:
- traefik.enable=true
- traefik.http.routers.ddns-api.rule=Host(`ddns-api.yourdomain.com`)
- traefik.http.routers.ddns-api.entrypoints=websecure
- traefik.http.routers.ddns-api.tls=true
- traefik.http.routers.ddns-api.tls.certresolver=letsencrypt
- traefik.http.services.ddns-api.loadbalancer.server.port=8000
networks:
default:
external: true
name: traefik # Must match your Traefik network name
Replace:
- ddns.yourdomain.com with your actual web domain
- ddns-api.yourdomain.com with your actual API domain
- letsencrypt with your Traefik certificate resolver name
- traefik with your Traefik Docker network name (check with docker network ls)
Step 3 — Deploy¶
# Rebuild with new environment and labels
docker compose up -d --build
Traefik will detect the new containers via Docker labels, request certificates from Let's Encrypt, and begin routing traffic.
Watch Traefik logs to confirm:
docker logs traefik -f # or whatever your Traefik container is named
Step 4 — Verify¶
# Test web UI
curl -I https://ddns.yourdomain.com
# Test API
curl https://ddns-api.yourdomain.com/health
Complete Traefik Setup (From Scratch)¶
If you don't already have Traefik running, here's a minimal setup:
1. Create the Traefik network¶
docker network create traefik
2. Create Traefik configuration¶
mkdir -p /opt/traefik
touch /opt/traefik/acme.json
chmod 600 /opt/traefik/acme.json
Create /opt/traefik/traefik.yml:
api:
dashboard: true
insecure: false
entryPoints:
web:
address: ":80"
http:
redirections:
entryPoint:
to: websecure
scheme: https
websecure:
address: ":443"
certificatesResolvers:
letsencrypt:
acme:
email: your-email@example.com # Your email for Let's Encrypt
storage: /acme.json
httpChallenge:
entryPoint: web
providers:
docker:
exposedByDefault: false
network: traefik
3. Create Traefik docker-compose.yml¶
In /opt/traefik/docker-compose.yml:
services:
traefik:
image: traefik:v3.0
container_name: traefik
restart: unless-stopped
ports:
- "80:80"
- "443:443"
volumes:
- /var/run/docker.sock:/var/run/docker.sock:ro
- ./traefik.yml:/traefik.yml:ro
- ./acme.json:/acme.json
networks:
- traefik
labels:
- traefik.enable=true
# Dashboard (optional — remove if you don't need it)
- traefik.http.routers.traefik.rule=Host(`traefik.yourdomain.com`)
- traefik.http.routers.traefik.entrypoints=websecure
- traefik.http.routers.traefik.tls.certresolver=letsencrypt
- traefik.http.routers.traefik.service=api@internal
- traefik.http.routers.traefik.middlewares=auth
- traefik.http.middlewares.auth.basicauth.users=admin:$$apr1$$... # htpasswd hash
networks:
traefik:
external: true
Start Traefik:
cd /opt/traefik
docker compose up -d
Then follow Step 2 above to add Dreadnought labels.
Using Traefik Middleware¶
Rate Limiting (optional but recommended for the API)¶
Add a rate limit middleware to protect the API:
api:
labels:
- traefik.enable=true
- traefik.http.routers.ddns-api.rule=Host(`ddns-api.yourdomain.com`)
- traefik.http.routers.ddns-api.entrypoints=websecure
- traefik.http.routers.ddns-api.tls=true
- traefik.http.routers.ddns-api.tls.certresolver=letsencrypt
- traefik.http.services.ddns-api.loadbalancer.server.port=8000
- traefik.http.routers.ddns-api.middlewares=api-ratelimit
- traefik.http.middlewares.api-ratelimit.ratelimit.average=100
- traefik.http.middlewares.api-ratelimit.ratelimit.burst=50
IP Allowlist (restrict access to specific IPs)¶
If you only want to access Dreadnought from your home IP or a VPN:
- traefik.http.routers.ddns-web.middlewares=ddns-allowlist
- traefik.http.middlewares.ddns-allowlist.ipallowlist.sourcerange=203.0.113.0/32,10.0.0.0/8
Troubleshooting Traefik¶
Certificates not issued¶
# Check Traefik logs for ACME errors
docker logs traefik 2>&1 | grep -i "acme\|certif\|error"
Common causes:
- DNS not propagated yet
- Port 80 blocked (needed for HTTP-01 ACME challenge)
- acme.json not writable
404 from Traefik¶
Traefik found the router but no service. Check that:
- The container is on the same Docker network as Traefik
- traefik.http.services.ddns-web.loadbalancer.server.port is set correctly
Container not picked up by Traefik¶
# Check Traefik dashboard (if enabled) for the router
# Or check that the container is on the traefik network:
docker inspect dreadnought-ddns-web-1 | grep -A 10 Networks
Make sure the override file's networks.default.name matches your Traefik network.