Eintrag

Traefik: Der Cloud-Native Reverse Proxy für Docker und mehr

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.

Traefik Konzepte


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 Domain
  • PathPrefix(/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 Label traefik.enable=true haben. 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?

  1. traefik.enable=true - Container wird von Traefik erkannt
  2. rule=Host(...) - Traffic für media.example.com wird hierhin geleitet
  3. entrypoints=websecure - Nur HTTPS (Port 443)
  4. tls.certresolver=letsencrypt - Automatisches SSL-Zertifikat
  5. loadbalancer.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:

  1. IP-Check (local-only)
  2. Rate-Limiting
  3. Authentication
  4. 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.json regelmäß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:

  1. DNS-Eintrag prüfen: dig media.example.com sollte auf deine Traefik-IP zeigen
  2. Im Dashboard nachsehen: Ist der Router aktiv?
  3. Host-Header prüfen: curl -H "Host: media.example.com" http://localhost
  4. 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:

  1. Ist der Container überhaupt gestartet? docker ps
  2. Port richtig? docker inspect <container> | grep ExposedPorts
  3. Gleiches Docker-Netzwerk? docker network inspect bridge
  4. Backend-Logs prüfen: docker logs <container>
  5. 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:

  1. acme.json Permissions: ls -l letsencrypt/acme.json sollte 600 sein
  2. Challenge funktioniert?
    • HTTP: Port 80 von außen erreichbar?
    • DNS: API-Token korrekt?
  3. Rate-Limit erreicht? Warte eine Stunde oder nutze Staging
  4. 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:

  1. Dashboard aktiviert? --api.dashboard=true in der Command-Sektion
  2. Router für Dashboard definiert? Siehe Labels oben
  3. Middleware blockiert? Probiere ohne Auth/IP-Whitelist
  4. URL korrekt? Muss /dashboard/ sein (mit Slash am Ende)

Container wird nicht erkannt

Ursache: Traefik sieht den Container nicht.

Lösung:

  1. traefik.enable=true gesetzt?
  2. Docker Socket eingebunden? /var/run/docker.sock:/var/run/docker.sock:ro
  3. Container läuft? docker ps
  4. Traefik neu starten: docker restart traefik
  5. Dashboard → HTTP → Services: Wird der Service angezeigt?

Middlewares funktionieren nicht

Ursache: Falsche Syntax oder Reihenfolge.

Lösung:

  1. Middleware definiert? Muss als traefik.http.middlewares.<name>... existieren
  2. Middleware angehängt? routers.<name>.middlewares=<middleware-name>
  3. Mehrere Middlewares: Komma-getrennt, keine Leerzeichen
  4. 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:

  1. Backend schickt auch Redirect? Deaktiviere das
  2. Nur einen Redirect-Mechanismus verwenden
  3. X-Forwarded-Proto Header 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=DEBUG aktivieren. 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 Service
  • traefik_entrypoint_request_duration_seconds - Response Times
  • traefik_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!

Dieser Eintrag ist vom Autor unter CC BY 4.0 lizensiert.