Traefik: Der Cloud-Native Reverse Proxy für Docker und mehr
Nginx Proxy Manager ist super für den Einstieg, keine Frage. Aber was, wenn dein Homelab wächst und du plötzlich 20, 30 oder mehr Services verwaltest? Was, wenn du nicht bei jeder neuen App manuell in eine Web-UI klicken willst? Genau hier glänzt Traefik: Automatische Service-Discovery via Docker-Labels, Konfiguration als Code, und ein Middleware-System, das dir nahezu unbegrenzte Möglichkeiten gibt. Wenn du deine Infrastruktur ernsthaft automatisieren willst, solltest du Traefik definitiv auf dem Schirm haben.
Was ist Traefik?
Traefik ist ein Open-Source Reverse Proxy und Load Balancer, der speziell für moderne, cloud-native Infrastrukturen entwickelt wurde. Während klassische Reverse Proxies wie Nginx oder HAProxy statische Konfigurationsdateien verwenden, setzt Traefik auf dynamische Service-Discovery: Es erkennt automatisch, welche Services in deiner Umgebung laufen und konfiguriert sich selbst entsprechend.
Die wichtigsten Features:
- Automatische Service-Discovery für Docker, Kubernetes, Consul, etc.
- Configuration as Code via Docker-Labels oder YAML-Dateien
- Integriertes Let’s Encrypt mit automatischer Zertifikatsverwaltung
- Leistungsstarkes Middleware-System für Auth, Rate-Limiting, Header-Manipulation
- Built-in Dashboard zur Überwacht (Read-Only)
- In Go geschrieben, extrem performant und ressourcenschonend
- Production-Ready, wird von großen Unternehmen produktiv eingesetzt
Das Projekt ist auf GitHub unter traefik/traefik zu finden und hat über 50.000 Stars. Die aktive Community und die hervorragende Dokumentation machen den Einstieg deutlich einfacher, als man zunächst denken könnte.
Die vier Grundkonzepte
Bevor wir loslegen, musst du verstehen, wie Traefik intern funktioniert. Es gibt vier zentrale Konzepte, die zusammenspielen:
1. Entrypoints
Entrypoints sind die Ports, über die Traffic in Traefik hineinkommt. Typischerweise definierst du:
- web (Port 80) für HTTP
- websecure (Port 443) für HTTPS
Du kannst aber auch weitere Entrypoints für andere Protokolle oder Ports definieren.
2. Routers
Router entscheiden, wohin ein eingehender Request geschickt wird. Sie verwenden Regeln wie:
Host(example.com)- Traffic für diese DomainPathPrefix(/api)- Traffic für diesen Pfad- Header-basierte Regeln, Methoden-Filter, etc.
Ein Router verbindet einen Entrypoint mit einem Service und kann dabei Middlewares einbinden.
3. Middlewares
Middlewares transformieren den Request, bevor er zum Backend-Service weitergeleitet wird. Beispiele:
- Authentication (Basic Auth, Forward Auth, etc.)
- Rate Limiting (Schutz vor DDoS)
- Header-Manipulation (Security Headers, CORS)
- Redirects (HTTP zu HTTPS)
- IP-Whitelisting (Zugriff beschränken)
Middlewares können beliebig kombiniert werden.
4. Services
Services sind die eigentlichen Backend-Anwendungen, zu denen der Traffic geschickt wird. Ein Service kann aus mehreren Servern bestehen (Load Balancing).
Hier ist das Zusammenspiel visualisiert:
flowchart LR
Internet([Internet]) --> EP[Entrypoint :443]
EP --> R1[Router: Host rule]
R1 --> MW1[Middleware: Auth]
MW1 --> MW2[Middleware: Headers]
MW2 --> S1[Service: app:3000]
S1 --> Backend[(Backend Container)]
Installation mit Docker
Die einfachste Methode, Traefik zu installieren, ist Docker Compose. Hier ist eine vollständige, produktionsreife Konfiguration:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
services:
traefik:
image: traefik:v3.2
container_name: traefik
command:
# Dashboard aktivieren (aber nur intern erreichbar)
- --api.dashboard=true
# Docker als Provider
- --providers.docker=true
- --providers.docker.exposedbydefault=false
# Entrypoints definieren
- --entrypoints.web.address=:80
- --entrypoints.websecure.address=:443
# Let's Encrypt Konfiguration
- [email protected]
- --certificatesresolvers.letsencrypt.acme.storage=/letsencrypt/acme.json
- --certificatesresolvers.letsencrypt.acme.httpchallenge.entrypoint=web
# Logging
- --log.level=INFO
ports:
- "80:80"
- "443:443"
volumes:
# Docker Socket für Service-Discovery
- /var/run/docker.sock:/var/run/docker.sock:ro
# Persistente Speicherung der SSL-Zertifikate
- ./letsencrypt:/letsencrypt
labels:
# Dashboard über HTTPS verfügbar machen
- "traefik.enable=true"
- "traefik.http.routers.dashboard.rule=Host(`traefik.example.com`)"
- "traefik.http.routers.dashboard.service=api@internal"
- "traefik.http.routers.dashboard.entrypoints=websecure"
- "traefik.http.routers.dashboard.tls.certresolver=letsencrypt"
# Globale HTTP zu HTTPS Weiterleitung
- "traefik.http.routers.http-redirect.rule=HostRegexp(`{host:.+}`)"
- "traefik.http.routers.http-redirect.entrypoints=web"
- "traefik.http.routers.http-redirect.middlewares=redirect-to-https"
- "traefik.http.middlewares.redirect-to-https.redirectscheme.scheme=https"
restart: unless-stopped
Wichtige Punkte:
exposedbydefault=false: Container werden nur dann von Traefik exponiert, wenn sie explizit das Labeltraefik.enable=truehaben. Das verhindert, dass versehentlich interne Services nach außen geraten.- Docker Socket: Traefik braucht Zugriff auf
/var/run/docker.sock, um Docker-Container zu entdecken. Das ist quasi Root-Zugriff! - acme.json: Hier werden die Let’s Encrypt Zertifikate gespeichert. Muss 600 Permissions haben:
chmod 600 letsencrypt/acme.json
Docker Socket Zugriff bedeutet Root-Zugriff! Verwende idealerweise einen Docker Socket Proxy (tecnativa/docker-socket-proxy) für mehr Sicherheit.
Nach dem Start mit docker compose up -d läuft Traefik und wartet auf Container, die sich registrieren wollen.
Services mit Labels konfigurieren
Der größte Vorteil von Traefik: Du konfigurierst deine Services direkt über Docker-Labels. Kein Klicken in einer Web-UI, keine separaten Config-Dateien. Alles ist Teil deiner docker-compose.yml.
Einfaches Beispiel: Jellyfin
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
services:
jellyfin:
image: jellyfin/jellyfin:latest
container_name: jellyfin
ports:
- "8096:8096" # Nur lokal, nicht von außen erreichbar
volumes:
- ./config:/config
- ./cache:/cache
- /media:/media
labels:
# Traefik aktivieren
- "traefik.enable=true"
# Router konfigurieren
- "traefik.http.routers.jellyfin.rule=Host(`media.example.com`)"
- "traefik.http.routers.jellyfin.entrypoints=websecure"
- "traefik.http.routers.jellyfin.tls.certresolver=letsencrypt"
# Service konfigurieren (welcher Port im Container?)
- "traefik.http.services.jellyfin.loadbalancer.server.port=8096"
Was passiert hier?
traefik.enable=true- Container wird von Traefik erkanntrule=Host(...)- Traffic fürmedia.example.comwird hierhin geleitetentrypoints=websecure- Nur HTTPS (Port 443)tls.certresolver=letsencrypt- Automatisches SSL-Zertifikatloadbalancer.server.port=8096- Traefik weiß, dass der Container auf Port 8096 lauscht
Immich mit großen Uploads
Immich benötigt größere Body-Limits für Foto-Uploads:
1
2
3
4
5
6
7
8
9
10
11
12
13
services:
immich-server:
image: ghcr.io/immich-app/immich-server:release
labels:
- "traefik.enable=true"
- "traefik.http.routers.immich.rule=Host(`photos.example.com`)"
- "traefik.http.routers.immich.entrypoints=websecure"
- "traefik.http.routers.immich.tls.certresolver=letsencrypt"
- "traefik.http.services.immich.loadbalancer.server.port=3001"
# Middleware für große Uploads (50 MB)
- "traefik.http.middlewares.immich-body.buffering.maxRequestBodyBytes=52428800"
- "traefik.http.routers.immich.middlewares=immich-body"
GitLab mit Custom Ports
GitLab braucht mehrere Ports (HTTP, SSH, Registry):
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
services:
gitlab:
image: gitlab/gitlab-ce:latest
labels:
# Web-UI
- "traefik.enable=true"
- "traefik.http.routers.gitlab.rule=Host(`git.example.com`)"
- "traefik.http.routers.gitlab.entrypoints=websecure"
- "traefik.http.routers.gitlab.tls.certresolver=letsencrypt"
- "traefik.http.services.gitlab.loadbalancer.server.port=80"
# SSH (als eigener TCP Entrypoint, siehe Traefik-Config)
# Container Registry (Port 5005) als separater Router
- "traefik.http.routers.gitlab-registry.rule=Host(`registry.example.com`)"
- "traefik.http.routers.gitlab-registry.entrypoints=websecure"
- "traefik.http.routers.gitlab-registry.tls.certresolver=letsencrypt"
- "traefik.http.services.gitlab-registry.loadbalancer.server.port=5005"
Home Assistant mit WebSockets
Home Assistant benötigt WebSocket-Support:
1
2
3
4
5
6
7
8
9
10
11
12
13
services:
homeassistant:
image: homeassistant/home-assistant:stable
labels:
- "traefik.enable=true"
- "traefik.http.routers.hass.rule=Host(`hass.example.com`)"
- "traefik.http.routers.hass.entrypoints=websecure"
- "traefik.http.routers.hass.tls.certresolver=letsencrypt"
- "traefik.http.services.hass.loadbalancer.server.port=8123"
# WebSocket Headers
- "traefik.http.middlewares.hass-headers.headers.customrequestheaders.X-Forwarded-Proto=https"
- "traefik.http.routers.hass.middlewares=hass-headers"
Die meisten Apps funktionieren ohne besondere Konfiguration. Nur bei WebSockets, großen Uploads oder speziellen Headern musst du nachhelfen.
Middlewares im Detail
Middlewares sind Traefiks Superpower. Sie transformieren Requests, bevor sie zum Backend gehen. Du kannst sie global definieren und dann bei mehreren Routern verwenden.
HTTP zu HTTPS Redirect
Permanente Weiterleitung von HTTP zu HTTPS:
1
2
3
4
5
6
labels:
- "traefik.http.middlewares.redirect-https.redirectscheme.scheme=https"
- "traefik.http.middlewares.redirect-https.redirectscheme.permanent=true"
# Bei einem Router verwenden
- "traefik.http.routers.myapp.middlewares=redirect-https"
Noch besser: Globale Weiterleitung für alle Domains (bereits im Traefik-Container selbst):
1
2
3
4
5
6
# Im Traefik-Container
labels:
- "traefik.http.routers.http-redirect.rule=HostRegexp(`{host:.+}`)"
- "traefik.http.routers.http-redirect.entrypoints=web"
- "traefik.http.routers.http-redirect.middlewares=redirect-to-https"
- "traefik.http.middlewares.redirect-to-https.redirectscheme.scheme=https"
Basic Auth
Schütze dein Dashboard oder andere Services mit Username/Passwort:
Zuerst Hash generieren:
1
2
3
4
5
6
# htpasswd installieren (Debian/Ubuntu)
sudo apt install apache2-utils
# Hash generieren (User: admin, Passwort: geheim)
htpasswd -nb admin geheim
# Output: admin:$apr1$xyz...
Dann als Label verwenden (wichtig: $ muss zu $$ escaped werden):
1
2
3
labels:
- "traefik.http.middlewares.auth.basicauth.users=admin:$$apr1$$xyz..."
- "traefik.http.routers.dashboard.middlewares=auth"
Für mehrere User einfach mit Komma trennen:
1
2
labels:
- "traefik.http.middlewares.auth.basicauth.users=admin:$$apr1$$...,user:$$apr1$$..."
Rate Limiting
Schütze deine Services vor DDoS und Brute-Force-Attacken:
1
2
3
4
5
6
7
8
labels:
# Durchschnittlich 100 Requests pro Sekunde, Burst bis 50 extra
- "traefik.http.middlewares.ratelimit.ratelimit.average=100"
- "traefik.http.middlewares.ratelimit.burst=50"
- "traefik.http.middlewares.ratelimit.period=1s"
# Auf Router anwenden
- "traefik.http.routers.myapp.middlewares=ratelimit"
Für APIs oder Login-Seiten kannst du strengere Limits setzen:
1
2
3
4
labels:
# Nur 10 Requests pro Minute für Login
- "traefik.http.middlewares.login-limit.ratelimit.average=10"
- "traefik.http.middlewares.login-limit.ratelimit.period=1m"
Security Headers
Setze moderne Security-Headers für bessere Sicherheit:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
labels:
# HSTS (HTTP Strict Transport Security)
- "traefik.http.middlewares.security.headers.stsSeconds=63072000"
- "traefik.http.middlewares.security.headers.stsIncludeSubdomains=true"
- "traefik.http.middlewares.security.headers.stsPreload=true"
# Clickjacking-Schutz
- "traefik.http.middlewares.security.headers.frameDeny=true"
# Content-Type Sniffing verhindern
- "traefik.http.middlewares.security.headers.contentTypeNosniff=true"
# XSS-Schutz
- "traefik.http.middlewares.security.headers.browserXssFilter=true"
# Referrer Policy
- "traefik.http.middlewares.security.headers.referrerPolicy=no-referrer-when-downgrade"
# Content Security Policy (Beispiel)
- "traefik.http.middlewares.security.headers.customResponseHeaders.Content-Security-Policy=default-src 'self'"
Diese Middleware kannst du dann global auf alle deine Services anwenden:
1
- "traefik.http.routers.myapp.middlewares=security"
IP Whitelist
Beschränke Zugriff auf bestimmte IP-Bereiche (z.B. nur lokales Netzwerk):
1
2
3
4
5
6
labels:
# Nur lokale Netze erlauben
- "traefik.http.middlewares.local-only.ipallowlist.sourcerange=192.168.0.0/16,10.0.0.0/8,172.16.0.0/12"
# Auf sensible Services anwenden
- "traefik.http.routers.portainer.middlewares=local-only"
Für einzelne IPs:
1
2
labels:
- "traefik.http.middlewares.admin-only.ipallowlist.sourcerange=203.0.113.42/32"
Custom Headers
Füge beliebige Header hinzu oder entferne sie:
1
2
3
4
5
6
7
labels:
# Header hinzufügen
- "traefik.http.middlewares.custom-headers.headers.customrequestheaders.X-Custom-Header=MyValue"
- "traefik.http.middlewares.custom-headers.headers.customresponseheaders.X-Powered-By=Homelab"
# Server-Header entfernen (Fingerprinting erschweren)
- "traefik.http.middlewares.custom-headers.headers.customresponseheaders.Server="
Middleware-Ketten
Das Beste: Du kannst mehrere Middlewares kombinieren:
1
2
labels:
- "traefik.http.routers.myapp.middlewares=auth,security,ratelimit,local-only"
Die Reihenfolge spielt eine Rolle. In diesem Beispiel:
- IP-Check (local-only)
- Rate-Limiting
- Authentication
- Security Headers
Definiere häufig genutzte Middlewares global im Traefik-Container, dann kannst du sie bei allen Services wiederverwenden.
SSL-Zertifikate
Traefik hat Let’s Encrypt direkt integriert. Du musst dich um nichts kümmern, Zertifikate werden automatisch ausgestellt und erneuert.
HTTP Challenge
Die Standard-Methode. Traefik muss dafür über Port 80 erreichbar sein:
1
2
3
4
command:
- [email protected]
- --certificatesresolvers.letsencrypt.acme.storage=/letsencrypt/acme.json
- --certificatesresolvers.letsencrypt.acme.httpchallenge.entrypoint=web
Vorteile:
- Einfach, funktioniert out-of-the-box
- Keine zusätzliche Konfiguration nötig
Nachteile:
- Port 80 muss von außen erreichbar sein
- Keine Wildcard-Zertifikate möglich
DNS Challenge
Für Wildcard-Zertifikate oder wenn Port 80 nicht erreichbar ist. Traefik unterstützt über 100 DNS-Provider.
Beispiel mit Cloudflare:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
services:
traefik:
image: traefik:v3.2
command:
- [email protected]
- --certificatesresolvers.letsencrypt.acme.storage=/letsencrypt/acme.json
- --certificatesresolvers.letsencrypt.acme.dnschallenge=true
- --certificatesresolvers.letsencrypt.acme.dnschallenge.provider=cloudflare
- --certificatesresolvers.letsencrypt.acme.dnschallenge.resolvers=1.1.1.1:53,8.8.8.8:53
environment:
- [email protected]
- CF_DNS_API_TOKEN=your-cloudflare-api-token
volumes:
- ./letsencrypt:/letsencrypt
Der API-Token braucht nur die Berechtigung Zone:DNS:Edit.
Wildcard-Zertifikat verwenden:
1
2
3
4
labels:
- "traefik.http.routers.myapp.tls.certresolver=letsencrypt"
- "traefik.http.routers.myapp.tls.domains[0].main=example.com"
- "traefik.http.routers.myapp.tls.domains[0].sans=*.example.com"
Vorteile:
- Wildcard-Zertifikate möglich (
*.example.com) - Funktioniert auch wenn Port 80/443 nicht erreichbar sind
- Besser für interne Services
Nachteile:
- API-Token für DNS-Provider nötig
- Etwas komplexere Konfiguration
Staging Environment
Für Tests solltest du Let’s Encrypt Staging verwenden (höheres Rate-Limit):
1
2
command:
- --certificatesresolvers.letsencrypt.acme.caserver=https://acme-staging-v02.api.letsencrypt.org/directory
Vergiss nicht, das für Production wieder zu entfernen!
Backup deine
acme.jsonregelmäßig! Wenn du zu oft Zertifikate neu ausstellen lässt, läufst du in Let’s Encrypt Rate-Limits.
File Provider (nicht-Docker Services)
Nicht alle Services laufen in Docker. Für Proxmox, physische Server, oder VMs kannst du den File Provider verwenden.
Zuerst in der Traefik-Config aktivieren:
1
2
3
4
5
6
7
services:
traefik:
command:
- --providers.file.directory=/etc/traefik/dynamic
- --providers.file.watch=true # Automatisch neu laden bei Änderungen
volumes:
- ./dynamic:/etc/traefik/dynamic
Dann dynamische Konfiguration erstellen:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
http:
routers:
proxmox:
rule: "Host(`pve.example.com`)"
entryPoints:
- websecure
tls:
certResolver: letsencrypt
service: proxmox
middlewares:
- local-only # Nur aus lokalem Netz erreichbar
services:
proxmox:
loadBalancer:
servers:
- url: "https://192.168.60.15:8006"
serversTransport: insecure
middlewares:
local-only:
ipAllowList:
sourceRange:
- "192.168.0.0/16"
- "10.0.0.0/8"
serversTransports:
insecure:
insecureSkipVerify: true # Selbstsignierte Zertifikate akzeptieren
Weitere Beispiele:
Home Assistant (nicht in Docker)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
http:
routers:
homeassistant:
rule: "Host(`hass.example.com`)"
entryPoints:
- websecure
tls:
certResolver: letsencrypt
service: homeassistant
services:
homeassistant:
loadBalancer:
servers:
- url: "http://192.168.30.85:8123"
UniFi Controller
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
http:
routers:
unifi:
rule: "Host(`unifi.example.com`)"
entryPoints:
- websecure
tls:
certResolver: letsencrypt
service: unifi
middlewares:
- local-only
services:
unifi:
loadBalancer:
servers:
- url: "https://192.168.2.1:8443"
serversTransport: insecure
Load Balancing über mehrere Server
1
2
3
4
5
6
7
8
9
10
11
12
http:
services:
my-clustered-app:
loadBalancer:
servers:
- url: "http://192.168.60.10:8080"
- url: "http://192.168.60.11:8080"
- url: "http://192.168.60.12:8080"
healthCheck:
path: /health
interval: 30s
timeout: 5s
File Provider ist perfekt für Legacy-Systeme, die du nicht in Docker packen kannst oder willst.
Dashboard
Traefik hat ein eingebautes Dashboard, das dir Echtzeit-Einblick in deine Konfiguration gibt.
Es zeigt:
- Alle Routers mit ihren Regeln
- Alle Services mit ihren Backends
- Alle Middlewares
- Zertifikats-Status
- Health-Checks
- Fehler und Warnungen
Wichtig: Das Dashboard ist Read-Only. Du kannst die Konfiguration nicht über die UI ändern, nur anschauen. Das ist gut so, denn es verhindert, dass sich Config-Drift einschleicht.
Zugriff via api@internal Service (bereits in der Basis-Config oben enthalten):
1
2
3
4
5
6
7
8
labels:
- "traefik.http.routers.dashboard.rule=Host(`traefik.example.com`)"
- "traefik.http.routers.dashboard.service=api@internal"
- "traefik.http.routers.dashboard.entrypoints=websecure"
- "traefik.http.routers.dashboard.tls.certresolver=letsencrypt"
# WICHTIG: Dashboard absichern!
- "traefik.http.routers.dashboard.middlewares=auth,local-only"
Das Dashboard solltest du IMMER absichern:
- Mit Basic Auth
- Oder IP-Whitelist
- Oder beides
Alternativ kannst du es auch nur lokal auf 127.0.0.1:8080 verfügbar machen:
1
2
3
4
5
command:
- --api.dashboard=true
- --api.insecure=true # Nur für lokalen Zugriff ohne Auth
ports:
- "127.0.0.1:8080:8080"
Dann erreichst du es nur via SSH-Tunnel:
1
2
ssh -L 8080:localhost:8080 user@traefik-host
# Dann im Browser: http://localhost:8080/dashboard/
Das Dashboard ist super zum Debugging. Du siehst sofort, ob ein Router aktiv ist, welche Middlewares angehängt sind, und ob das Backend erreichbar ist.
Traefik vs. NPM: Wann was?
Zeit für einen ehrlichen Vergleich. Beide Tools haben ihre Berechtigung, aber sie spielen in unterschiedlichen Ligen.
| Aspekt | Traefik | NPM |
|---|---|---|
| Konfiguration | Docker-Labels, YAML-Files | Web-UI (klicken) |
| Docker-Integration | Native Auto-Discovery | Manuell über UI |
| Lernkurve | Steiler, Konzepte verstehen | Flach, intuitiv |
| Flexibilität | Extrem hoch, alles konfigurierbar | Begrenzt auf UI-Features |
| Middlewares | 20+ Built-in, kombinierbar | Basis-Features (Auth, Headers) |
| Performance | Go, sehr schnell, wenig RAM | Node.js, mehr Overhead |
| Skalierbarkeit | Production-ready, Cluster-fähig | Eher Small-Scale |
| Dashboard | Read-Only, zur Überwachung | Zentrale Config-UI |
| File-basierte Services | File Provider, sehr flexibel | Geht, aber umständlich |
| Ideal für | Viele Container, IaC, Automation | Wenige Services, Einstieg |
Weitere Unterschiede:
- GitOps: Traefik-Configs kannst du in Git committen und versionieren. Bei NPM müsstest du die SQLite-Datenbank exportieren.
- Disaster Recovery: Traefik =
docker compose up, fertig. NPM = Datenbank wiederherstellen, hoffen dass nichts kaputt ist. - Multi-Tenancy: Traefik kann mit verschiedenen Docker-Contexts, Kubernetes-Namespaces, etc. umgehen. NPM ist eher Single-Server.
- Monitoring: Traefik hat Prometheus-Metriken eingebaut. NPM nicht.
Wann NPM?
- Du hast weniger als 10 Services
- Du willst keine CLI/YAML anfassen
- Quick & Dirty Setup ist wichtiger als Langzeit-Wartbarkeit
- Du bist neu im Homelab-Game
Wann Traefik?
- Du betreibst 20+ Container
- Du lebst “Infrastructure as Code”
- Du willst vollständige Kontrolle und Flexibilität
- Du planst, zu Kubernetes zu migrieren (Traefik kann beides)
- Performance und Ressourcen-Effizienz sind wichtig
Faustregel: NPM wenn du eine Web-UI bevorzugst und wenige Services hast. Traefik wenn du viele Docker-Container betreibst und “Infrastructure as Code” leben willst.
Übrigens: Du kannst auch beides parallel laufen lassen! Nutze NPM für die Handvoll Legacy-Apps, die du nicht in Docker hast, und Traefik für deine Container-Infrastruktur. Einfach unterschiedliche Ports verwenden (z.B. NPM auf 81/444, Traefik auf 80/443).
Homelab-Beispiele
Theorie ist gut, Praxis ist besser. Hier ist eine komplette, produktionsreife Traefik-Setup für ein Homelab mit mehreren Services.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
services:
# Traefik Reverse Proxy
traefik:
image: traefik:v3.2
container_name: traefik
command:
# Dashboard
- --api.dashboard=true
# Provider
- --providers.docker=true
- --providers.docker.exposedbydefault=false
- --providers.file.directory=/etc/traefik/dynamic
- --providers.file.watch=true
# Entrypoints
- --entrypoints.web.address=:80
- --entrypoints.websecure.address=:443
# SSL mit DNS Challenge (Cloudflare)
- [email protected]
- --certificatesresolvers.letsencrypt.acme.storage=/letsencrypt/acme.json
- --certificatesresolvers.letsencrypt.acme.dnschallenge=true
- --certificatesresolvers.letsencrypt.acme.dnschallenge.provider=cloudflare
# Logging
- --log.level=INFO
- --accesslog=true
# Metrics (optional)
- --metrics.prometheus=true
ports:
- "80:80"
- "443:443"
environment:
- CF_API_EMAIL=${CF_API_EMAIL}
- CF_DNS_API_TOKEN=${CF_DNS_API_TOKEN}
volumes:
- /var/run/docker.sock:/var/run/docker.sock:ro
- ./letsencrypt:/letsencrypt
- ./dynamic:/etc/traefik/dynamic
labels:
- "traefik.enable=true"
# Dashboard
- "traefik.http.routers.dashboard.rule=Host(`traefik.example.com`)"
- "traefik.http.routers.dashboard.service=api@internal"
- "traefik.http.routers.dashboard.entrypoints=websecure"
- "traefik.http.routers.dashboard.tls.certresolver=letsencrypt"
- "traefik.http.routers.dashboard.middlewares=auth,local-only"
# Globale Middlewares
- "traefik.http.middlewares.auth.basicauth.users=${TRAEFIK_AUTH}"
- "traefik.http.middlewares.local-only.ipallowlist.sourcerange=192.168.0.0/16,10.0.0.0/8"
- "traefik.http.middlewares.security.headers.stsSeconds=63072000"
- "traefik.http.middlewares.security.headers.stsIncludeSubdomains=true"
- "traefik.http.middlewares.security.headers.frameDeny=true"
# HTTP zu HTTPS Redirect
- "traefik.http.routers.http-redirect.rule=HostRegexp(`{host:.+}`)"
- "traefik.http.routers.http-redirect.entrypoints=web"
- "traefik.http.routers.http-redirect.middlewares=redirect-to-https"
- "traefik.http.middlewares.redirect-to-https.redirectscheme.scheme=https"
restart: unless-stopped
# Jellyfin Media Server
jellyfin:
image: jellyfin/jellyfin:latest
container_name: jellyfin
volumes:
- ./jellyfin/config:/config
- /media:/media:ro
labels:
- "traefik.enable=true"
- "traefik.http.routers.jellyfin.rule=Host(`media.example.com`)"
- "traefik.http.routers.jellyfin.entrypoints=websecure"
- "traefik.http.routers.jellyfin.tls.certresolver=letsencrypt"
- "traefik.http.services.jellyfin.loadbalancer.server.port=8096"
- "traefik.http.routers.jellyfin.middlewares=security"
restart: unless-stopped
# Immich Photo Management
immich-server:
image: ghcr.io/immich-app/immich-server:release
container_name: immich
volumes:
- ./immich/upload:/usr/src/app/upload
environment:
- DB_HOSTNAME=immich-postgres
- REDIS_HOSTNAME=immich-redis
labels:
- "traefik.enable=true"
- "traefik.http.routers.immich.rule=Host(`photos.example.com`)"
- "traefik.http.routers.immich.entrypoints=websecure"
- "traefik.http.routers.immich.tls.certresolver=letsencrypt"
- "traefik.http.services.immich.loadbalancer.server.port=3001"
- "traefik.http.middlewares.immich-body.buffering.maxRequestBodyBytes=52428800"
- "traefik.http.routers.immich.middlewares=immich-body,security"
restart: unless-stopped
# Portainer (Container Management)
portainer:
image: portainer/portainer-ce:latest
container_name: portainer
volumes:
- /var/run/docker.sock:/var/run/docker.sock
- ./portainer/data:/data
labels:
- "traefik.enable=true"
- "traefik.http.routers.portainer.rule=Host(`portainer.example.com`)"
- "traefik.http.routers.portainer.entrypoints=websecure"
- "traefik.http.routers.portainer.tls.certresolver=letsencrypt"
- "traefik.http.services.portainer.loadbalancer.server.port=9000"
- "traefik.http.routers.portainer.middlewares=local-only,security"
restart: unless-stopped
# Grafana (Monitoring)
grafana:
image: grafana/grafana:latest
container_name: grafana
volumes:
- ./grafana/data:/var/lib/grafana
environment:
- GF_SERVER_ROOT_URL=https://grafana.example.com
labels:
- "traefik.enable=true"
- "traefik.http.routers.grafana.rule=Host(`grafana.example.com`)"
- "traefik.http.routers.grafana.entrypoints=websecure"
- "traefik.http.routers.grafana.tls.certresolver=letsencrypt"
- "traefik.http.services.grafana.loadbalancer.server.port=3000"
- "traefik.http.routers.grafana.middlewares=security"
restart: unless-stopped
# Vaultwarden (Password Manager)
vaultwarden:
image: vaultwarden/server:latest
container_name: vaultwarden
volumes:
- ./vaultwarden/data:/data
environment:
- DOMAIN=https://vault.example.com
- SIGNUPS_ALLOWED=false
labels:
- "traefik.enable=true"
- "traefik.http.routers.vault.rule=Host(`vault.example.com`)"
- "traefik.http.routers.vault.entrypoints=websecure"
- "traefik.http.routers.vault.tls.certresolver=letsencrypt"
- "traefik.http.services.vault.loadbalancer.server.port=80"
- "traefik.http.routers.vault.middlewares=security"
restart: unless-stopped
Zusätzlich noch die .env Datei:
1
2
3
4
5
6
# Cloudflare API
CF_API_EMAIL=[email protected]
CF_DNS_API_TOKEN=your-cloudflare-token
# Traefik Dashboard Auth (htpasswd generiert)
TRAEFIK_AUTH=admin:$$apr1$$xyz...
Und eine dynamische Config für nicht-Docker Services:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
http:
routers:
proxmox:
rule: "Host(`pve.example.com`)"
entryPoints:
- websecure
tls:
certResolver: letsencrypt
service: proxmox
middlewares:
- local-only
unifi:
rule: "Host(`unifi.example.com`)"
entryPoints:
- websecure
tls:
certResolver: letsencrypt
service: unifi
middlewares:
- local-only
services:
proxmox:
loadBalancer:
servers:
- url: "https://192.168.60.15:8006"
serversTransport: insecure
unifi:
loadBalancer:
servers:
- url: "https://192.168.2.1:8443"
serversTransport: insecure
serversTransports:
insecure:
insecureSkipVerify: true
Starten:
1
2
3
4
5
6
7
8
9
10
# Zertifikats-Datei vorbereiten
mkdir -p letsencrypt dynamic
touch letsencrypt/acme.json
chmod 600 letsencrypt/acme.json
# Starten
docker compose up -d
# Logs ansehen
docker compose logs -f traefik
Nach etwa 30 Sekunden sollten alle Services mit gültigen SSL-Zertifikaten erreichbar sein.
Troubleshooting
Auch mit Traefik läuft nicht immer alles glatt. Hier die häufigsten Probleme und ihre Lösungen.
404 - Seite nicht gefunden
Ursache: Router-Regel matched nicht.
Lösung:
- DNS-Eintrag prüfen:
dig media.example.comsollte auf deine Traefik-IP zeigen - Im Dashboard nachsehen: Ist der Router aktiv?
- Host-Header prüfen:
curl -H "Host: media.example.com" http://localhost - Traefik-Logs:
docker logs traefik | grep media.example.com
Häufiger Fehler: Domain stimmt nicht überein, weil Wildcard vs. Subdomain.
502 Bad Gateway
Ursache: Backend ist nicht erreichbar.
Lösung:
- Ist der Container überhaupt gestartet?
docker ps - Port richtig?
docker inspect <container> | grep ExposedPorts - Gleiches Docker-Netzwerk?
docker network inspect bridge - Backend-Logs prüfen:
docker logs <container> - Manuell testen:
curl http://<container-ip>:<port>
Bei File Provider: Ist die URL erreichbar? curl -k https://192.168.60.15:8006
SSL-Fehler / Selbstsigniertes Zertifikat
Ursache: Let’s Encrypt Zertifikat wurde nicht ausgestellt.
Lösung:
acme.jsonPermissions:ls -l letsencrypt/acme.jsonsollte 600 sein- Challenge funktioniert?
- HTTP: Port 80 von außen erreichbar?
- DNS: API-Token korrekt?
- Rate-Limit erreicht? Warte eine Stunde oder nutze Staging
- Logs:
docker logs traefik | grep acme
1
2
# acme.json ansehen (ist JSON)
cat letsencrypt/acme.json | jq
Wenn leer: Zertifikat wurde noch nicht ausgestellt. Warte 1-2 Minuten nach dem Start.
Dashboard nicht erreichbar
Ursache: Service api@internal nicht richtig konfiguriert.
Lösung:
- Dashboard aktiviert?
--api.dashboard=truein der Command-Sektion - Router für Dashboard definiert? Siehe Labels oben
- Middleware blockiert? Probiere ohne Auth/IP-Whitelist
- URL korrekt? Muss
/dashboard/sein (mit Slash am Ende)
Container wird nicht erkannt
Ursache: Traefik sieht den Container nicht.
Lösung:
traefik.enable=truegesetzt?- Docker Socket eingebunden?
/var/run/docker.sock:/var/run/docker.sock:ro - Container läuft?
docker ps - Traefik neu starten:
docker restart traefik - Dashboard → HTTP → Services: Wird der Service angezeigt?
Middlewares funktionieren nicht
Ursache: Falsche Syntax oder Reihenfolge.
Lösung:
- Middleware definiert? Muss als
traefik.http.middlewares.<name>...existieren - Middleware angehängt?
routers.<name>.middlewares=<middleware-name> - Mehrere Middlewares: Komma-getrennt, keine Leerzeichen
- Tippfehler im Namen? Middleware-Name muss exakt übereinstimmen
1
2
3
4
5
# Falsch
- "traefik.http.routers.app.middlewares=auth security"
# Richtig
- "traefik.http.routers.app.middlewares=auth,security"
Zu viele Redirects
Ursache: Loop zwischen HTTP-Redirect und Backend.
Lösung:
- Backend schickt auch Redirect? Deaktiviere das
- Nur einen Redirect-Mechanismus verwenden
X-Forwarded-ProtoHeader prüfen:
1
2
3
labels:
- "traefik.http.middlewares.forward-proto.headers.customrequestheaders.X-Forwarded-Proto=https"
- "traefik.http.routers.app.middlewares=forward-proto"
Debug-Tipp:
--log.level=DEBUGaktivieren. Dann siehst du jeden einzelnen Request und jede Middleware-Transformation. Vergiss nicht, danach wieder auf INFO zu setzen!
Tipps für die Praxis
Nach Monaten mit Traefik im Production-Einsatz hier die wichtigsten Lessons Learned:
Docker-Netzwerke strategisch nutzen
Erstelle ein dediziertes Netzwerk für Traefik:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
networks:
traefik:
name: traefik
driver: bridge
services:
traefik:
networks:
- traefik
myapp:
networks:
- traefik # Für Traefik erreichbar
- backend # Internes Netzwerk für DB etc.
Vorteil: Klare Trennung zwischen öffentlich erreichbaren Services und internen Dependencies.
acme.json Backup
Die Zertifikatsdatei ist kritisch. Verlierst du sie, musst du alle Zertifikate neu ausstellen (Rate-Limit!).
1
2
# Cronjob für tägliches Backup
0 2 * * * cp /opt/traefik/letsencrypt/acme.json /backup/acme-$(date +\%Y\%m\%d).json
Oder mit Docker Volume Backup:
1
2
3
4
5
6
volumes:
- ./letsencrypt:/letsencrypt
- /backup/traefik:/backup
# Im Container via Cron
echo "0 2 * * * cp /letsencrypt/acme.json /backup/acme-\$(date +\%Y\%m\%d).json" | crontab -
Secrets via Environment Variables
Hardcode niemals Passwörter oder Tokens in Docker-Compose:
1
2
3
environment:
- CF_DNS_API_TOKEN=${CF_DNS_API_TOKEN}
- TRAEFIK_AUTH=${TRAEFIK_AUTH}
Mit .env Datei (Git-ignored):
1
2
CF_DNS_API_TOKEN=your-secret-token
TRAEFIK_AUTH=admin:$apr1$...
Oder Docker Secrets (Swarm Mode):
1
2
3
4
5
6
7
8
9
10
secrets:
cf_token:
file: ./secrets/cf_token.txt
services:
traefik:
secrets:
- cf_token
environment:
- CF_DNS_API_TOKEN_FILE=/run/secrets/cf_token
Prometheus Monitoring
Traefik hat Metriken eingebaut. Nutze sie!
1
2
3
4
5
6
7
command:
- --metrics.prometheus=true
- --metrics.prometheus.entrypoint=metrics
entrypoints:
metrics:
address: :8082
Dann in Prometheus scrapen:
1
2
3
4
scrape_configs:
- job_name: 'traefik'
static_configs:
- targets: ['traefik:8082']
Metriken, die du tracken solltest:
traefik_entrypoint_requests_total- Request-Count pro Servicetraefik_entrypoint_request_duration_seconds- Response Timestraefik_backend_server_up- Backend Health
Log-Rotation
Access-Logs können schnell groß werden:
1
2
3
4
5
6
7
command:
- --accesslog=true
- --accesslog.filepath=/var/log/traefik/access.log
- --accesslog.bufferingsize=100
volumes:
- ./logs:/var/log/traefik
Mit Logrotate:
1
2
3
4
5
6
7
8
/opt/traefik/logs/access.log {
daily
rotate 14
compress
delaycompress
missingok
notifempty
}
Docker Socket Proxy für mehr Sicherheit
Statt Traefik direkten Zugriff auf den Docker Socket zu geben, nutze einen Proxy:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
services:
socket-proxy:
image: tecnativa/docker-socket-proxy
container_name: socket-proxy
volumes:
- /var/run/docker.sock:/var/run/docker.sock:ro
environment:
- CONTAINERS=1
- NETWORKS=1
- SERVICES=1
- TASKS=1
restart: unless-stopped
traefik:
# Statt Docker Socket direkt
# volumes:
# - /var/run/docker.sock:/var/run/docker.sock:ro
# Nutze Socket Proxy
environment:
- DOCKER_HOST=tcp://socket-proxy:2375
Jetzt hat Traefik nur noch limitierten, read-only Zugriff.
Wildcard vs. einzelne Subdomains
DNS Challenge macht Wildcard-Zertifikate möglich, aber solltest du sie nutzen?
Vorteile:
- Ein Zertifikat für alle Subdomains
- Schnellere Ausstellung (nur ein ACME-Request)
- Einfacher bei vielen Services
Nachteile:
- Wenn jemand das Zertifikat stiehlt, kann er alle Subdomains impersonieren
- Weniger Granularität bei Revocation
- Let’s Encrypt Rate-Limits sind strenger
Empfehlung: Wildcard für interne Services, einzelne Certs für öffentlich exponierte Dienste.
Testing vor Production
Nutze Docker Compose Profiles für Test-Umgebungen:
1
2
3
4
5
6
7
8
9
10
11
12
services:
traefik:
profiles:
- production
command:
- [email protected]
traefik-staging:
profiles:
- staging
command:
- --certificatesresolvers.letsencrypt.acme.caserver=https://acme-staging-v02.api.letsencrypt.org/directory
Dann:
1
2
3
4
5
# Staging testen
docker compose --profile staging up -d
# Production deployen
docker compose --profile production up -d
Investiere Zeit in ordentliche Monitoring- und Backup-Strategien. Traefik ist robust, aber wenn’s schief geht, willst du schnell reagieren können.
Fazit
Traefik ist definitiv keine “Plug-and-Play”-Lösung wie NPM. Du musst die Konzepte verstehen, YAML oder Docker-Labels schreiben, und dich mit Let’s Encrypt Challenges auseinandersetzen. Aber die Investition lohnt sich:
Was Traefik richtig gut kann:
- Automatische Service-Discovery für Docker-Container
- Infrastructure as Code - alles versionierbar in Git
- Extrem flexibles Middleware-System
- Production-ready Performance
- Nahtlose Skalierung (auch für Kubernetes)
- Umfassendes Monitoring und Metrics
Wo Traefik Schwächen hat:
- Lernkurve ist steil
- Debugging ist anfangs frustrierend
- Keine Config-UI (manche sehen das als Feature)
- Dokumentation ist gut, aber riesig
Für mich persönlich war der Wechsel von NPM zu Traefik ein Game-Changer. Kein Klicken mehr in UIs, keine Config-Drift, und wenn ich einen neuen Container starte, ist er automatisch via HTTPS erreichbar. Docker Compose up, fertig. Das ist die Art von Automatisierung, die ein Homelab haben sollte.
Wenn du noch bei NPM bist: Bleib dabei, solange es für dich funktioniert. Aber wenn dein Setup wächst und du merkst, dass manuelle Config nervig wird, dann ist Traefik der nächste logische Schritt. Du wirst die ersten paar Stunden fluchen, versprochen. Aber danach willst du nie wieder zurück.
Ressourcen
Offizielle Dokumentation und Community:
Hilfreiche Tools:
Weiterführende Themen:
- Migration zu Kubernetes mit Traefik Ingress Controller
- Traefik mit Consul für Service-Discovery
- Traefik als TCP/UDP Load Balancer
- Integration mit Authentik/Authelia für SSO
Viel Erfolg mit deinem Traefik-Setup!