Skip to main content

Traefik v3 Reverse Proxy: Complete Self-Hosting Setup Guide (2026)

· 7 min read
Yassine El Haddad
Software Developer & Automation Specialist

I build production AI agents, web scrapers, and automation pipelines. Most of what I publish here comes from the actual problems they run into: proxies that get banned, anti-bot stacks that fingerprint your client, RAG that drifts when the underlying data moves. Stack: Python, TypeScript, Go, FastAPI, LangChain, Crawlee, Playwright, deployed on AWS, GCP, and Cloudflare.

Traefik is a reverse proxy that discovers services from Docker labels and config files, issues Let's Encrypt certificates automatically, and handles HTTP/3. Version 3 refines middleware syntax and improves performance. This guide covers installation, your first service, middleware for auth and rate limiting, and dashboard security on a Liquid Web VPS.

What Is Traefik?

Traefik is an auto-discovering reverse proxy. Unlike Nginx, you don't edit a config file for each new service — you add Docker labels or file-based config, and Traefik picks them up. It integrates with Let's Encrypt for SSL and supports HTTP/3 (QUIC).

Traefik v3 changes from v2:

  • Cleaner middleware syntax
  • Better HTTP/3 support
  • Improved performance
  • Refined plugin architecture

Docker Compose Setup

Create docker-compose.yml:

services:
traefik:
image: traefik:v3.2
restart: unless-stopped
command:
- "--api.dashboard=true"
- "--providers.docker=true"
- "--providers.docker.exposedbydefault=false"
- "--entrypoints.web.address=:80"
- "--entrypoints.websecure.address=:443"
- "--certificatesresolvers.letsencrypt.acme.httpchallenge=true"
- "--certificatesresolvers.letsencrypt.acme.httpchallenge.entrypoint=web"
- "--certificatesresolvers.letsencrypt.acme.email=you@example.com"
- "--certificatesresolvers.letsencrypt.acme.storage=/letsencrypt/acme.json"
- "--accesslog=true"
- "--accesslog.filepath=/var/log/traefik/access.log"
ports:
- "80:80"
- "443:443"
volumes:
- /var/run/docker.sock:/var/run/docker.sock:ro
- traefik-letsencrypt:/letsencrypt
- traefik-logs:/var/log/traefik
labels:
- "traefik.enable=true"
- "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=traefik-auth"
- "traefik.http.middlewares.traefik-auth.basicauth.users=admin:$$apr1$$..."
networks:
- traefik

volumes:
traefik-letsencrypt:
traefik-logs:

networks:
traefik:
external: true

Create the network:

docker network create traefik

Generate BasicAuth hash for the dashboard:

docker run --rm httpd:alpine htpasswd -nbB admin yourpassword
# Copy the output (e.g. admin:$apr1$...) and use in the basicauth label.
# Escape $ as $$ in docker-compose.

Configuration File (Optional)

For more control, use a static config file traefik.yml:

entryPoints:
web:
address: ":80"
websecure:
address: ":443"

certificatesResolvers:
letsencrypt:
acme:
email: you@example.com
storage: /letsencrypt/acme.json
httpChallenge:
entryPoint: web

providers:
docker:
endpoint: "unix:///var/run/docker.sock"
exposedByDefault: false
file:
directory: /etc/traefik/dynamic
watch: true

Mount traefik.yml and a dynamic/ directory for dynamic config (middleware, routers).

Adding Your First Service

Start a simple web app and add Traefik labels:

services:
whoami:
image: traefik/whoami
restart: unless-stopped
labels:
- "traefik.enable=true"
- "traefik.http.routers.whoami.rule=Host(`whoami.yourdomain.com`)"
- "traefik.http.routers.whoami.entrypoints=websecure"
- "traefik.http.routers.whoami.tls.certresolver=letsencrypt"
- "traefik.http.services.whoami.loadbalancer.server.port=80"
networks:
- traefik

networks:
traefik:
external: true

Point whoami.yourdomain.com to your server. Traefik will fetch a certificate and route HTTPS traffic to the container.

Middleware: BasicAuth, Rate Limiting, Headers

BasicAuth — protect a service:

labels:
- "traefik.http.middlewares.myauth.basicauth.users=admin:$$apr1$$..."
- "traefik.http.routers.myservice.middlewares=myauth"

Rate limiting:

labels:
- "traefik.http.middlewares.ratelimit.ratelimit.average=100"
- "traefik.http.middlewares.ratelimit.ratelimit.burst=50"
- "traefik.http.routers.myservice.middlewares=ratelimit"

Security headers:

labels:
- "traefik.http.middlewares.secureheaders.headers.browserXSSFilter=true"
- "traefik.http.middlewares.secureheaders.headers.contentTypeNosniff=true"
- "traefik.http.middlewares.secureheaders.headers.forceSTSHeader=true"
- "traefik.http.middlewares.secureheaders.headers.stsIncludeSubdomains=true"
- "traefik.http.middlewares.secureheaders.headers.stsPreload=true"
- "traefik.http.middlewares.secureheaders.headers.stsSeconds=31536000"
- "traefik.http.routers.myservice.middlewares=secureheaders"

Chain multiple middlewares: middlewares=myauth,ratelimit,secureheaders.

File-Based Dynamic Configuration

For complex setups, use the file provider. Create /etc/traefik/dynamic/middlewares.yml:

http:
middlewares:
secure-headers:
headers:
browserXssFilter: true
contentTypeNosniff: true
frameDeny: true
stsSeconds: 31536000
stsIncludeSubdomains: true
stsPreload: true

Reference it from Docker labels: traefik.http.routers.myservice.middlewares=secure-headers. The file provider watches for changes and reloads automatically. Combine with Docker discovery for a hybrid setup.

HTTP to HTTPS Redirect

Redirect all HTTP traffic to HTTPS. Add a catch-all router on the web entrypoint:

labels:
- "traefik.http.routers.http-catchall.rule=HostRegexp(`{host:.+}`)"
- "traefik.http.routers.http-catchall.entrypoints=web"
- "traefik.http.routers.http-catchall.middlewares=redirect-to-https"
- "traefik.http.middlewares.redirect-to-https.redirectscheme.scheme=https"

Dashboard Security

The dashboard exposes router and service info. Always protect it:

  1. BasicAuth — add basicauth middleware (as in the Traefik service above).
  2. IP whitelisttraefik.http.middlewares.dashboard-ip.ipallowlist.sourcerange=1.2.3.4/32
  3. Internal only — don't expose a public host; access via docker exec or SSH tunnel.

Traefik vs Nginx Proxy Manager vs Caddy

FeatureTraefikNginx Proxy ManagerCaddy
Config styleLabels, fileWeb UICaddyfile
Docker discoveryNativeManualManual
Auto SSLLet's EncryptLet's EncryptBuilt-in
HTTP/3YesVia Nginx (newer)Yes
MiddlewareRich (auth, rate limit, etc.)LimitedGood
Learning curveMediumLowLow
Best forDocker-native setupsGUI loversSimplicity

Choose Traefik when you run many Docker services and want label-based configuration. Use Liquid Web VPS for a stable host with good network performance.

When to Choose Traefik

Traefik shines when you run multiple Dockerized services and want zero-friction routing. Add a new container with a few labels — no config file edits, no reloads. Compare that to Nginx or Caddy, where each new service means editing and reloading a central config. Traefik is ideal for home labs, staging environments, and microservice stacks where services come and go frequently. For a single static site or a handful of long-lived apps, Nginx Proxy Manager's GUI might be simpler. For Docker-native setups with 5+ services, Traefik saves time and reduces config drift.

Logging and Observability

Traefik outputs access logs and metrics. Enable --accesslog=true and --accesslog.filepath as in the compose example. For Prometheus metrics, add --metrics.prometheus=true and --metrics.prometheus.entrypoint=traefik. Scrape http://traefik:8080/metrics for request counts, latency percentiles, and backend health. Combine with Grafana dashboards for full observability of your reverse proxy and backend services.

Production Checklist

Before going live with Traefik, verify: (1) ports 80 and 443 are open and forwarded if behind NAT; (2) DNS A records point to your server; (3) dashboard is protected with BasicAuth or IP allowlist; (4) HTTP redirects to HTTPS; (5) security headers middleware is attached to public services. Use a Liquid Web VPS for DDoS protection, managed backups, and 24/7 monitoring.

Troubleshooting

Certificate not issued — Ensure port 80 is reachable for HTTP-01 challenge. Check acme.json for errors. For wildcards, switch to DNS-01.

502 Bad Gateway — Container not running or wrong port. Verify loadbalancer.server.port matches the app. Ensure containers share the traefik network.

Dashboard unreachable — Enable api.dashboard=true. Add a router for the dashboard. Protect with BasicAuth before exposing.

For more Docker patterns, see Docker Compose production guide and Coolify for app deployment. Secure your host with the VPS security hardening checklist.

Apify Affiliate Banner 728x90Apify Affiliate Banner 728x90Apify Affiliate Banner 300x50Apify Affiliate Banner 300x50
Start simple

Deploy Traefik, add one service with labels, verify HTTPS. Then add middleware. Use file provider for shared middleware definitions.

Frequently Asked Questions

Traefik is a reverse proxy that auto-discovers services from Docker labels. It handles Let's Encrypt SSL, HTTP/3, and middleware like BasicAuth and rate limiting.

Traefik integrates with Let's Encrypt. Set a certificate resolver (certresolver) and storage path. It uses HTTP-01 or DNS-01 challenge. Certificates renew automatically.

Add Docker labels to your container: traefik.enable=true, traefik.http.routers.NAME.rule=Host(`domain.com`), traefik.http.routers.NAME.tls.certresolver=letsencrypt, and loadbalancer port. Put the container on the Traefik network.

Add a BasicAuth middleware and attach it to the dashboard router. Or use IP allowlist. Never expose the dashboard without authentication.

Common mistakes and fixes

Certificate not issued / ACME challenge fails

Ensure ports 80 and 443 are open. Verify DNS A record points to your server. Check Traefik logs: docker logs traefik. For wildcard certs, use DNS challenge.

502 Bad Gateway from Traefik

Backend container may not be running or wrong port. Check docker ps. Ensure the service port in labels matches the container's actual port. Verify network: containers must be on same Docker network as Traefik.

Dashboard unreachable

Dashboard is disabled by default in v3. Enable it in static config. Protect with BasicAuth or IP whitelist. Ensure entrypoint web and websecure are exposed.