Skip to content

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 traefik or proxy)
  • 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

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.