What is Traefik?
Traefik is a cloud-native reverse proxy and load balancer designed to
integrate natively with Docker, Kubernetes, and other orchestrators. Unlike Nginx, Traefik
auto-discovers your containers via labels and dynamically routes traffic โ
no manual config reloads needed.
Version 3 brings HTTP/3 support, improved middleware chaining, and a cleaner routing DSL. For homelab use, the killer feature is automatic HTTPS via Let's Encrypt with zero configuration on your part.
Prerequisites
- Docker + Docker Compose installed on your host
- A domain name pointed at your server's public IP
- Ports
80and443open on your firewall
Note
If you're behind a CGNAT or don't have a public IP, use the DNS-01 ACME challenge instead of HTTP-01. We'll cover both methods below.
Step 1: Create the Docker Network
Traefik needs to share a network with every container it proxies. Create a dedicated external network so all your Compose stacks can attach to it:
$ docker network create --driver bridge traefik-proxy
$ docker network ls --filter name=traefik-proxy
NETWORK ID NAME DRIVER SCOPE
a1b2c3d4e5f6 traefik-proxy bridge local
Step 2: The Traefik docker-compose.yml
Create a directory for Traefik and add the Compose file below. Certificate data is stored in a local file so it persists across container restarts.
services:
traefik:
image: traefik:v3.0
container_name: traefik
restart: unless-stopped
ports:
- "80:80"
- "443:443"
- "8080:8080" # dashboard (disable in prod)
command:
- --api.dashboard=true
- --providers.docker=true
- --providers.docker.exposedbydefault=false
- --entrypoints.web.address=:80
- --entrypoints.websecure.address=:443
- --certificatesresolvers.le.acme.httpchallenge=true
- --certificatesresolvers.le.acme.httpchallenge.entrypoint=web
- --certificatesresolvers.le.acme.email=you@example.com
- --certificatesresolvers.le.acme.storage=/letsencrypt/acme.json
volumes:
- /var/run/docker.sock:/var/run/docker.sock:ro
- ./letsencrypt:/letsencrypt
networks:
- traefik-proxy
networks:
traefik-proxy:
external: true
Warning
Mounting /var/run/docker.sock gives Traefik root-equivalent access to your host. In production, front it with tecnativa/docker-socket-proxy to limit API surface area.
Step 3: Expose a Service via Labels
Add Traefik labels to any container you want to proxy. Here's a working example with Portainer:
services:
portainer:
image: portainer/portainer-ce:latest
restart: unless-stopped
volumes:
- /var/run/docker.sock:/var/run/docker.sock
- portainer_data:/data
networks:
- traefik-proxy
labels:
- "traefik.enable=true"
- "traefik.http.routers.portainer.rule=Host(`portainer.yourdomain.com`)"
- "traefik.http.routers.portainer.entrypoints=websecure"
- "traefik.http.routers.portainer.tls.certresolver=le"
- "traefik.http.services.portainer.loadbalancer.server.port=9000"
volumes:
portainer_data:
networks:
traefik-proxy:
external: true
Deploy and verify
$ docker compose up -d
$ docker logs -f traefik
time="2026-04-08T10:00:00Z" level=info msg="Configuration loaded from Docker" providerName=docker
time="2026-04-08T10:00:01Z" level=info msg="Obtaining ACME certificate" domain=portainer.yourdomain.com
Tip
The Traefik dashboard at :8080 shows all discovered routers, services, and middleware in real-time. It's invaluable for debugging label misconfiguration.
Danger
Never expose the Traefik dashboard publicly without authentication. Add a basicAuth middleware before going to production or anyone on the internet can reach your API.
What's Next
With Traefik running you can now expose any service in your homelab with a single
traefik.enable=true label. Next up: adding middleware for
rate limiting, IP allowlisting, and automatic HTTPโHTTPS redirects โ covered in the
next post.