Deployn

Traefik 3 einrichten: Reverse Proxy mit Docker, HTTPS und Authentifizierung

Traefik 3 als Reverse Proxy für Docker: Server vorbereiten, Domain einrichten, automatische Let's-Encrypt-Zertifikate, Middlewares, Basic Auth und Google-Login. Schritt für Schritt für Anfänger.

Traefik 3 einrichten: Reverse Proxy mit Docker, HTTPS und Authentifizierung hero image
Inhalt
Getestet mit
Traefik 3.7traefik-forward-auth 4.14Docker 29.9Docker Compose 5.7Ubuntu 24.04
Zuletzt geprüft am

Du hast einen Server und möchtest darauf mehrere Dienste betreiben, zum Beispiel ein Dokumentenmanagementsystem, einen Passwortmanager und eine Webseite? Dann stößt du schnell auf ein Problem: Jeder Dienst möchte erreichbar sein, aber der Server hat nur eine IP-Adresse und für Webseiten nur zwei Standardports, 80 und 443. Die Lösung heißt Reverse Proxy.

In diesem Beitrag richte ich einen Server von Grund auf ein und installiere darauf Traefik in Version 3. Traefik nimmt alle Anfragen entgegen, besorgt sich selbst HTTPS-Zertifikate und leitet jede Anfrage an den richtigen Docker-Container weiter. Neue Dienste kommen später mit ein paar Zeilen in der Docker-Compose-Datei dazu, ohne dass du Traefik neu starten musst. Ich habe jeden Schritt auf einem frischen Server mit Ubuntu 24.04 durchgespielt, die Ausgaben und Screenshots stammen von diesem Test.

Das lernst du in diesem Beitrag

  • Was ein Reverse Proxy tut und wie Traefik aufgebaut ist (Entrypoints, Router, Middlewares, Services)
  • Server einrichten: Updates, eigener Benutzer, Firewall, Docker
  • Domain mit Wildcard-Eintrag auf den Server zeigen lassen
  • Traefik 3 mit automatischem HTTPS (Let’s Encrypt) und geschütztem Dashboard betreiben
  • Middlewares nutzen: Passwortschutz, Rate-Limit, Security-Header, Kompression, IP-Filter
  • Dienste per Labels anbinden und Fehler finden
  • Optional: Wildcard-Zertifikat, Google-Login und ein abgesicherter Docker-Socket

Du brauchst dafür einen Server (VPS) mit Ubuntu, eine Domain und etwa zwei Stunden Zeit. Erfahrung mit Docker ist nicht nötig. Wenn dir Befehle im Terminal noch fremd sind, lies zuerst meinen Linux-Terminal-Guide. Wie du einen Server auswählst und absicherst, steht ausführlicher im VPS-Leitfaden für Anfänger.

Was ist ein Reverse Proxy und warum Traefik?

Stell dir ein Bürogebäude mit einem Empfang vor. Besucher kommen alle durch dieselbe Tür und sagen, zu wem sie möchten. Der Mitarbeiter an der Rezeption schickt sie in das richtige Büro. Genau das macht ein Reverse Proxy für deinen Server:

                     ┌─────────────────────────────┐
Browser ──► :443 ──► │ Traefik                     │ ──► paperless:8000
 (HTTPS)             │  paperless.domain.de        │ ──► vault:80
                     │  vault.domain.de            │ ──► blog:2368
                     └─────────────────────────────┘

Ohne Reverse Proxy müsstest du jeden Dienst auf einem eigenen Port laufen lassen (123.45.67.89:9000), und jeder Dienst müsste sich selbst um HTTPS kümmern. Mit Reverse Proxy erreichst du alles über Subdomains wie paperless.domain.de, auf den Standardports 80 (HTTP) und 443 (HTTPS). Das Zertifikat verwaltet der Proxy an einer zentralen Stelle.

Es gibt mehrere gute Reverse Proxys. So ordne ich sie ein:

Proxy Stärke Schwäche
Nginx Proxy Manager Weboberfläche, sehr einsteigerfreundlich Jede Änderung klickst du per Hand
Caddy Sehr kurze Konfiguration, HTTPS automatisch Docker-Container musst du selbst in die Datei eintragen
Traefik Findet Docker-Container selbst und konfiguriert sich über Labels Mehr Begriffe, steilere Lernkurve

Ich nutze hier Traefik, weil ich Dienste damit direkt in ihrer eigenen docker-compose-Datei beschreibe. Startest du einen neuen Container mit den passenden Labels, erkennt Traefik ihn in wenigen Sekunden und legt Route und Zertifikat an.

Die Begriffe in Traefik

Traefik hat ein paar eigene Begriffe. Wenn du sie einmal verstanden hast, wird jede Konfiguration lesbar:

Begriff Bedeutung Beispiel in dieser Anleitung
Entrypoint Ein Port, auf dem Traefik lauscht http (80) und https (443)
Router Regel, die eine Anfrage einem Dienst zuordnet Host(`whoami.domain.de`)
Middleware Zwischenschritt, der die Anfrage prüft oder verändert Passwortschutz, Rate-Limit
Service Das Ziel, an das die Anfrage am Ende geht Port 80 im Container whoami
Provider Quelle, aus der Traefik seine Konfiguration liest Docker-Labels und Dateien
Certificate Resolver Holt und erneuert Zertifikate Let’s Encrypt

Eine Anfrage nimmt immer denselben Weg:

Anfrage ► Entrypoint ► Router ► Middlewares ► Service ► Container

Außerdem unterscheidet Traefik zwei Arten von Konfiguration:

  • Statische Konfiguration: Gilt für Traefik selbst (Ports, Zertifikate, Provider). Traefik liest sie nur beim Start. Änderungen brauchen einen Neustart. Sie steht bei uns in der Datei traefik.yml.
  • Dynamische Konfiguration: Beschreibt Router, Middlewares und Services. Traefik liest sie laufend und übernimmt Änderungen sofort. Sie kommt aus den Labels der Docker-Container und aus YAML-Dateien im Ordner rules.

Server vorbereiten

Welchen Server nehme ich?

Ich benutze für diese Anleitung einen Server, der nicht bei mir zu Hause steht. Das hat folgende Vorteile:

  • Einfachere Erreichbarkeit durch eine feste IPv4-Adresse
  • Schnelle Internetanbindung
  • Gut planbare Kosten
  • Keine Abnutzung eigener Hardware, keine Stromkosten und keine Geräusche zu Hause

Natürlich gibt es auch Nachteile, zum Beispiel laufende Kosten und die Abhängigkeit vom Anbieter. Die Anleitung funktioniert mit jedem Server, auf dem Docker laufen kann. Probleme hatte ich früher mit virtuellen Servern, die mit OpenVZ statt KVM virtualisiert waren. Für den Test habe ich einen Cloud-Server bei Hetzner mit 2 vCPUs und 4 GB RAM genutzt. Empfehlenswert sind außerdem die VPS und Root-Server (dedizierte CPU) von Netcup. Falls du dort noch nicht Kunde bist, bekommst du mit diesen Gutscheinen einen Rabatt. Ich bekomme eine Provision für geworbene Neukunden.

Als Betriebssystem wähle ich Ubuntu 24.04 LTS. Ein anderes wäre auch möglich, aber ich habe viel Erfahrung mit Ubuntu. Der Standard-Support läuft bis April 2029, du musst das System also lange nicht auf die nächste Version heben.

Mit dem Server verbinden

Sobald der Server bereit ist, verbindest du dich per SSH. Unter macOS und Linux reicht das Terminal. Unter Windows nutzt du das Windows Terminal, PuTTY oder ein ähnliches Programm. Ich nutze das Windows Terminal.

Terminalbash
ssh root@{{server_ip}}

Windows Terminal

Beim ersten Mal fragt SSH, ob du dem Server vertraust. Antworte mit yes und gib danach das Passwort ein. Das Passwort wird beim Tippen nicht angezeigt. Anschließend begrüßt dich der Server (die Meldung kann je nach Anbieter etwas anders aussehen):

Login

System aktualisieren

Als Erstes holen wir alle Updates. apt update lädt die aktuelle Paketliste, apt upgrade installiert die neuen Versionen:

Terminalbash
apt update
apt upgrade -y

Das -y beantwortet die Rückfrage automatisch mit “ja”. Wenn der Kernel aktualisiert wurde, starte den Server danach mit reboot neu und verbinde dich erneut.

Servername und Zeitzone

Der Server bekommt einen Namen, den du in der Eingabeaufforderung siehst. Außerdem stellen wir die Zeitzone ein, damit Logs die richtige Uhrzeit zeigen:

Terminalbash
hostnamectl set-hostname meinserver
timedatectl set-timezone Europe/Berlin

Prüfe das Ergebnis mit den folgenden Befehlen:

Terminalbash
hostnamectl | grep "Static hostname"
timedatectl | grep "Time zone"
 Static hostname: meinserver
                Time zone: Europe/Berlin (CET, +0100)

Die Zeitzone hilft dir später, wenn du Zertifikate und Logs vergleichst. Alle Zeitzonen listet timedatectl list-timezones auf.

Eigenen Benutzer anlegen

Mit dem Benutzer root darf man alles, auch das System zerstören. Deshalb legen wir einen normalen Benutzer an, der Administratorbefehle nur mit vorangestelltem sudo ausführen darf (Superuser do).

Terminalbash
adduser {{username}}
usermod -aG sudo {{username}}

adduser fragt nach einem Passwort und ein paar optionalen Angaben (Name, Telefon), die du mit Enter überspringen kannst. usermod -aG sudo fügt den Benutzer der Gruppe sudo hinzu. Dabei steht -a für “anhängen” und -G für “Gruppe”. Ohne -a würde der Benutzer aus allen anderen Gruppen entfernt werden.

Prüfe, ob alles geklappt hat:

Terminalbash
id {{username}}
uid=1000({{username}}) gid=1000({{username}}) groups=1000({{username}}),27(sudo),100(users)

Wenn du dich bisher mit einem SSH-Schlüssel anmeldest, kopiere ihn in das Home-Verzeichnis des neuen Benutzers. Sonst fragt der Server beim Login nach dem Passwort:

Terminalbash
rsync --archive --chown={{username}}:{{username}} ~/.ssh /home/{{username}}

Melde dich jetzt ab und mit dem neuen Benutzer wieder an:

Terminalbash
exit
ssh {{username}}@{{server_ip}}

Ab jetzt arbeitest du als dieser Benutzer. Alle Befehle, die Administratorrechte brauchen, beginnen mit sudo. Was ohne sudo passiert, siehst du am Beispiel von apt:

Terminalbash
apt update
Reading package lists... Done
E: Could not open lock file /var/lib/apt/lists/lock - open (13: Permission denied)
E: Unable to lock directory /var/lib/apt/lists/

Mit sudo apt update klappt es. Wie du den Server weiter absicherst (SSH-Schlüssel erzwingen, Root-Login sperren), steht im VPS-Leitfaden.

Firewall einrichten

Eine Firewall lässt nur die Verbindungen durch, die du erlaubst. Ubuntu bringt dafür ufw mit. Wir erlauben SSH (sonst sperrst du dich selbst aus!), HTTP und HTTPS:

Terminalbash
sudo ufw allow OpenSSH
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
sudo ufw enable
sudo ufw status

ufw enable warnt dich, dass bestehende SSH-Verbindungen unterbrochen werden könnten. Da wir SSH vorher erlaubt haben, bestätigst du mit y.

Status: active

To                         Action      From
--                         ------      ----
OpenSSH                    ALLOW       Anywhere
80/tcp                     ALLOW       Anywhere
443/tcp                    ALLOW       Anywhere
OpenSSH (v6)               ALLOW       Anywhere (v6)
80/tcp (v6)                ALLOW       Anywhere (v6)
443/tcp (v6)               ALLOW       Anywhere (v6)

Docker installieren

Jetzt kommt der wichtigste Teil: Docker und Docker Compose. Wir nutzen das offizielle Paket-Repository von Docker, damit du immer aktuelle Versionen bekommst:

Terminalbash
sudo apt-get update
sudo apt-get install -y ca-certificates curl
sudo install -m 0755 -d /etc/apt/keyrings
sudo curl -fsSL https://download.docker.com/linux/ubuntu/gpg -o /etc/apt/keyrings/docker.asc
sudo chmod a+r /etc/apt/keyrings/docker.asc

echo \
 "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.asc] https://download.docker.com/linux/ubuntu \
 $(. /etc/os-release && echo "${UBUNTU_CODENAME:-$VERSION_CODENAME}") stable" | \
 sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
sudo apt-get update
sudo apt-get install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin

Docker startet unter Ubuntu automatisch mit dem System. Das kannst du mit sudo systemctl is-enabled docker prüfen, die Antwort lautet enabled.

Damit du nicht vor jeden Docker-Befehl sudo schreiben musst, nimmst du deinen Benutzer in die Gruppe docker auf:

Terminalbash
sudo usermod -aG docker {{username}}

Die Gruppenänderung gilt erst nach einer neuen Anmeldung. Melde dich ab, wieder an und teste die Installation:

Terminalbash
docker --version
docker compose version
docker run --rm hello-world
Docker version 29.9.3, build 5457199
Docker Compose version v5.7.0

Hello from Docker!
This message shows that your installation appears to be working correctly.

Beachte: Docker Compose ist als Plugin integriert und heißt docker compose (mit Leerzeichen). Das frühere eigenständige Programm docker-compose (mit Bindestrich) wird nicht mehr weiterentwickelt. Die Dateien aus dieser Anleitung funktionieren mit beiden.

Terminal-Grundlagen für die nächsten Schritte

Ein paar Befehle brauchen wir ständig. Wenn du sie schon kennst, überspringe diesen Teil. Mit cd (“change directory”) wechselst du den Ordner, wobei die Tilde (~) für dein Home-Verzeichnis steht. mkdir erstellt einen neuen Ordner, ls zeigt den Inhalt.

Terminalbash
cd ~
mkdir docker
ls

Mit ls -l siehst du Berechtigungen und Besitzer, mit ls -A auch versteckte Dateien (alles, was mit einem Punkt beginnt), und ls -Al kombiniert beides. Mit cd docker wechselst du in den neuen Ordner, mit cd .. gehst du wieder hoch.

Texte bearbeitest du mit nano, einem einfachen Editor im Terminal. Du speicherst mit Strg + O und Enter und verlässt ihn mit Strg + X. Du kannst Dateien aber auch an deinem Rechner bearbeiten und per Copy-and-paste in nano einfügen. Achte darauf, dass die Einrückung dabei erhalten bleibt, denn YAML ist empfindlich dafür.

Die Shell kann außerdem Text in Dateien schreiben. Das brauchst du später für Passwörter und Schlüssel. Mit echo gibst du einen Text aus, mit > schreibst du ihn in eine Datei und überschreibst sie dabei, mit >> hängst du ihn an:

Terminalbash
echo "hallo" > test.txt
echo "tschüss" >> test.txt
cat test.txt
hallo
tschüss

cat gibt den Inhalt einer Datei aus. Mit rm test.txt löschst du die Datei wieder. Eine Zeile, die mit einer Raute (#) beginnt, ist ein Kommentar. Die Shell und auch YAML-Dateien ignorieren sie.

Zwei Tipps sparen dir viel Tipparbeit: Die Taste Tab vervollständigt Befehle und Dateinamen (tippe cd d und drücke Tab). Mit den Pfeiltasten ↑ und ↓ holst du frühere Befehle zurück.

Domain einrichten

Der Server hat nur eine IP-Adresse, wir möchten aber mehrere Dienste betreiben. Du könntest sie über verschiedene Ports ansteuern (123.45.67.89:443, 123.45.67.89:9000), aber das ist schwer zu merken. Außerdem bekommst du für eine reine IP-Adresse kein vertrauenswürdiges HTTPS-Zertifikat. Mit einem Reverse Proxy auf den Ports 80 und 443 gibt es dagegen eine Subdomain pro Dienst: mail.domain.de für das Mailprogramm, blog.domain.de für den Blog. Möglich wären auch Unterverzeichnisse wie domain.de/blog, aber viele Anwendungen kommen damit schlecht klar. Subdomains sind zuverlässiger.

Es gibt kostenlose Domains, zum Beispiel bei DuckDNS, aber die sind meist mit Einschränkungen verbunden. Studenten bekommen mit dem GitHub Student Developer Pack oft eine Domain für ein Jahr gratis. Eine eigene Domain zu kaufen ist aber nicht teuer. Bei Netcup kostet eine .de-Domain dauerhaft rund 5 € im Jahr. Jeder andere Anbieter geht auch, solange du DNS-Einträge selbst setzen kannst. Achte darauf, dass manche Anbieter ab dem zweiten Jahr deutlich mehr verlangen, besonders wenn das erste Jahr nur 1 € kostet.

DNS-Einträge setzen

Im Kundenbereich deines Anbieters findest du die DNS-Einstellungen. Wir brauchen zwei Einträge:

Typ Name Wert Bedeutung
A @ SERVER-IP Die Domain selbst (domain.de) zeigt auf den Server
A * SERVER-IP Alle Subdomains (blog.domain.de, mail.domain.de usw.) zeigen auf den Server

Der A-Record verbindet einen Namen mit einer IPv4-Adresse. @ steht für die Domain selbst, das Sternchen * ist ein Wildcard und passt auf jede Subdomain. Dank Wildcard musst du für neue Dienste nie wieder DNS-Einträge anlegen. Betreibst du mehrere Server mit unterschiedlichen IP-Adressen, legst du statt des Sternchens einzelne Einträge an. Hat dein Server auch eine IPv6-Adresse, kannst du zusätzlich AAAA-Einträge mit dieser Adresse anlegen.

So sah das bei mir im Kundenbereich von Netcup aus. Die Oberfläche kann sich seitdem geändert haben, der Aufbau ist bei anderen Anbietern ähnlich:

DNS-Einträge bei Netcup

Ich verringere außerdem die TTL (Time to Live, wie lange andere Server eine DNS-Antwort zwischenspeichern) von 24 Stunden auf etwa eine Stunde. Dann wirken Änderungen schneller. Speichere die Einstellungen. Es kann einige Minuten dauern, in seltenen Fällen auch ein bis zwei Tage, bis die Einträge überall bekannt sind.

DNS testen

Ob es geklappt hat, prüfst du mit dig (unter Windows mit nslookup). Du fragst eine Subdomain ab, die es noch gar nicht gibt, und bekommst trotzdem die IP-Adresse deines Servers zurück:

Terminalbash
dig +short test.{{domain}}
203.0.113.10

Erscheint keine Antwort, sind die Einträge noch nicht übernommen. Manchmal hilft es, den DNS-Cache deines Rechners zu leeren (Windows: ipconfig /flushdns).

DNS-Cache leeren

Wenn die IP-Adresse deines Servers erscheint, ist die Domain eingerichtet.

Traefik 3 installieren

Wir legen eine Ordnerstruktur an, schreiben die Konfiguration und starten Traefik. Alles liegt im Ordner ~/docker, den wir vorhin angelegt haben.

Ordner und Dateien anlegen

Terminalbash
cd ~/docker
mkdir -p traefik/acme traefik/rules traefik/logs shared
touch traefik/acme/acme.json
chmod 600 traefik/acme/acme.json

Die Option -p von mkdir legt auch fehlende Zwischenordner an. In acme.json speichert Traefik später deine Zertifikate und den Zugang zu Let’s Encrypt. Die Berechtigung 600 bedeutet, dass nur der Besitzer die Datei lesen und schreiben darf. Traefik verweigert sonst den Start, weil ein privater Schlüssel nicht für alle lesbar sein darf.

Die Struktur sieht danach so aus (die Dateien kommen gleich dazu):

~/docker
├── .env
├── docker-compose.yml
├── shared
│   └── .htpasswd
└── traefik
    ├── acme
    │   └── acme.json
    ├── logs
    ├── rules
    │   ├── chains.yml
    │   └── middlewares.yml
    └── traefik.yml

Die Datei .env

In .env stehen Werte, die mehrere Compose-Dateien brauchen. Docker Compose liest die Datei automatisch, wenn sie im selben Ordner liegt. Zuerst brauchen wir deine Benutzer-ID. Sie steht in der Ausgabe von id:

Terminalbash
id
uid=1000({{username}}) gid=1000({{username}}) groups=1000({{username}}),27(sudo),100(users),988(docker)

Die Zahlen hinter uid und gid kommen in die .env. Öffne die Datei mit nano ~/docker/.env und trage ein:

.env
PUID=1000
PGID=1000
TZ=Europe/Berlin
USERDIR=/home/{{username}}
DOCKERDIR=/home/{{username}}/docker
DOMAINNAME={{domain}}
  • PUID und PGID: Benutzer- und Gruppen-ID. Viele Container-Images (zum Beispiel von LinuxServer.io) laufen damit unter deinem Benutzer, statt als Root.
  • TZ: Zeitzone für die Container.
  • USERDIR und DOCKERDIR: Pfade, die wir in den Compose-Dateien nutzen. pwd zeigt dir den Pfad deines aktuellen Ordners.
  • DOMAINNAME: Deine Domain ohne www und ohne Subdomain.

Es gibt Stimmen, die vor Umgebungsvariablen für Geheimnisse warnen. Für den Anfang finde ich sie trotzdem besser als Passwörter direkt in Compose-Dateien, die du vielleicht in Git speicherst. Wichtig: Nimm die .env nie in ein öffentliches Repository auf.

Passwortschutz vorbereiten (htpasswd)

Das Traefik-Dashboard soll nicht für jeden offen sein. Dafür brauchen wir ein Passwort, das Traefik als Hash prüft. Das Programm htpasswd aus den Apache-Hilfsprogrammen erzeugt ihn:

Terminalbash
sudo apt install -y apache2-utils
htpasswd -nbB admin 'DeinStarkesPasswort'
admin:$2y$05$1eU8Kn67Ix62KNyjfXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX

Die Optionen: -n gibt das Ergebnis aus, statt eine Datei zu schreiben, -b nimmt das Passwort aus der Befehlszeile, und -B verwendet den sicheren Hash-Algorithmus bcrypt. Die Ausgabe enthält Benutzername und Hash. Das Passwort selbst steht nirgends im Klartext. Nutze für die echte Datei am besten ein neues Passwort aus einem Passwortgenerator und speichere die Ausgabe direkt in die Datei:

Terminalbash
htpasswd -nbB admin 'DeinStarkesPasswort' > ~/docker/shared/.htpasswd

Die statische Konfiguration: traefik.yml

Das ist die Hauptdatei. Sie beschreibt Traefik selbst und wird beim Start gelesen. Öffne nano ~/docker/traefik/traefik.yml:

traefik/traefik.ymlyaml
# Statische Konfiguration: Traefik liest sie nur beim Start
global:
  checkNewVersion: true
  sendAnonymousUsage: false
  # Verwirft Header mit ähnlichen Namen wie X_Auth_User, damit niemand Header fälschen kann
  aliasHeadersStrategy: delete

api:
  dashboard: true

# Aktiviert den Endpunkt /ping, den der Healthcheck von Docker abfragt
ping: {}

log:
  level: INFO

accessLog:
  filePath: /logs/access.log
  bufferingSize: 100
  filters:
    statusCodes:
      - "400-599"

entryPoints:
  http:
    address: ":80"
    http:
      redirections:
        entryPoint:
          to: https
          scheme: https
  https:
    address: ":443"
    http:
      tls:
        certResolver: letsencrypt

providers:
  docker:
    exposedByDefault: false
    network: web
  file:
    directory: /rules
    watch: true

certificatesResolvers:
  letsencrypt:
    acme:
      storage: /acme.json
      # Zum Testen: Staging-Server von Let's Encrypt (Zertifikate sind nicht vertrauenswürdig, es gibt aber keine Limits)
      caServer: https://acme-staging-v02.api.letsencrypt.org/directory
      httpChallenge:
        entryPoint: http

Was steht da im Einzelnen?

  • global: checkNewVersion fragt gelegentlich nach neuen Traefik-Versionen, sendAnonymousUsage: false verhindert das Senden von Nutzungsstatistiken. aliasHeadersStrategy: delete ist eine Schutzmaßnahme: Angreifer können sonst Header wie X_Auth_User schicken, die manche Anwendungen wie X-Auth-User lesen und dadurch eine Anmeldung vortäuschen. Ohne die Zeile warnt Traefik 3.7 beim Start.
  • api.dashboard: Schaltet das Dashboard ein. Es ist nicht offen, sondern erreichbar nur über den Router in der Compose-Datei.
  • ping: Ein winziger Endpunkt, mit dem Docker prüft, ob Traefik läuft.
  • log und accessLog: INFO zeigt wichtige Meldungen, bei Problemen stellst du auf DEBUG. Das Access-Log schreibt jede Anfrage in eine Datei. Ich filtere auf die Statuscodes 400 bis 599, also Fehler. So bleibt die Datei klein und enthält nur, was dich interessiert.
  • entryPoints: Zwei Eingänge. Der Eingang http auf Port 80 leitet alles automatisch auf HTTPS um (redirections). Dadurch brauchst du keine eigene Weiterleitung mehr in jedem Container. Der Eingang https auf Port 443 holt für jede Domain, die ein Router nutzt, ein Zertifikat vom Resolver letsencrypt. Die Namen http und https sind frei gewählt, in anderen Anleitungen heißen sie oft web und websecure. Ich behalte http und https bei, damit meine anderen Anleitungen weiter passen.
  • providers: Traefik soll Docker-Container lesen. exposedByDefault: false bedeutet, dass nur Container mit dem Label traefik.enable=true veröffentlicht werden. network: web legt fest, über welches Docker-Netzwerk Traefik die Container erreicht. Außerdem liest Traefik YAML-Dateien aus dem Ordner /rules und merkt sich Änderungen darin sofort (watch: true).
  • certificatesResolvers: Der Resolver letsencrypt holt Zertifikate per HTTP-Challenge. Let’s Encrypt ruft dabei kurz deine Domain auf Port 80 auf und prüft, ob der Server dir gehört. Das braucht keinen Zugang zu deinem DNS-Anbieter und funktioniert mit jeder Domain. Das caServer verweist zunächst auf die Testumgebung. Dazu später mehr.

Middlewares und Ketten vorbereiten

Das Dashboard soll mit Passwort geschützt sein. Das erledigt eine Middleware. Sie liegt als YAML-Datei im Ordner rules, den Traefik live überwacht. Öffne nano ~/docker/traefik/rules/middlewares.yml:

traefik/rules/middlewares.ymlyaml
http:
  middlewares:
    middlewares-basic-auth:
      basicAuth:
        realm: "Traefik Basic Auth"
        usersFile: /shared/.htpasswd

    middlewares-rate-limit:
      rateLimit:
        average: 100
        burst: 50

    middlewares-compress:
      compress: {}

    middlewares-secure-headers:
      headers:
        stsSeconds: 63072000
        stsIncludeSubdomains: true
        stsPreload: true
        forceSTSHeader: true
        customFrameOptionsValue: "SAMEORIGIN"
        contentTypeNosniff: true
        referrerPolicy: "same-origin"
        permissionsPolicy: "camera=(), geolocation=(), microphone=(), payment=(), usb=()"
        customResponseHeaders:
          X-Robots-Tag: "none,noarchive,nosnippet,notranslate,noimageindex"
          server: ""

Das sind vier Middlewares. Jede erledigt eine Aufgabe:

  • middlewares-basic-auth: Fragt nach Benutzername und Passwort. Die Zugangsdaten liest Traefik aus der Datei .htpasswd.
  • middlewares-rate-limit: Begrenzt, wie viele Anfragen ein Besucher schicken darf: durchschnittlich 100 pro Sekunde, kurzzeitig bis zu 50 zusätzliche (burst). Das bremst einfache Angriffe und Bots.
  • middlewares-compress: Komprimiert Antworten mit gzip (oder Brotli und Zstandard, je nachdem, was der Browser unterstützt). Das spart Bandbreite.
  • middlewares-secure-headers: Setzt Sicherheits-Header. stsSeconds und die verwandten Optionen weisen den Browser an, die Domain zwei Jahre lang nur per HTTPS aufzurufen (HSTS). contentTypeNosniff und die Frame-Option schützen vor ein paar typischen Angriffen, referrerPolicy begrenzt die Weitergabe der Herkunftsadresse. permissionsPolicy sperrt Browserfunktionen wie Kamera und Mikrofon, falls eine Seite sie nicht braucht. Zusätzlich entfernt die Middleware den server-Header und bittet Suchmaschinen über X-Robots-Tag, die Seiten nicht zu indexieren. Ändere das, wenn du einen Dienst öffentlich auffindbar machen willst.

Einzeln eingetragen wird das schnell unübersichtlich. Darum fasst man Middlewares zu Ketten zusammen. Eine Kette führt mehrere Middlewares nacheinander aus. Lege nano ~/docker/traefik/rules/chains.yml an:

traefik/rules/chains.ymlyaml
http:
  middlewares:
    chain-no-auth:
      chain:
        middlewares:
          - middlewares-rate-limit
          - middlewares-secure-headers
          - middlewares-compress

    chain-basic-auth:
      chain:
        middlewares:
          - middlewares-rate-limit
          - middlewares-secure-headers
          - middlewares-compress
          - middlewares-basic-auth

Du hast jetzt zwei Ketten: chain-no-auth für öffentliche Dienste und chain-basic-auth für Dienste mit Passwortschutz. Der Router eines Dienstes bekommt später nur einen Eintrag (zum Beispiel chain-basic-auth@file), statt vier. Der Zusatz @file sagt Traefik, dass die Middleware aus einer Datei stammt (nicht aus Docker-Labels). Die Reihenfolge in der Kette zählt: Das Rate-Limit steht vorn, damit Angreifer gar nicht erst die Passwortabfrage erreichen.

Die Compose-Datei für Traefik

Jetzt fehlt der Container. Öffne nano ~/docker/docker-compose.yml:

docker-compose.ymlyaml
networks:
  web:
    name: web

services:
  traefik:
    container_name: traefik
    image: traefik:v3.7
    restart: unless-stopped
    security_opt:
      - no-new-privileges:true
    ports:
      - "80:80"
      - "443:443"
    networks:
      - web
    environment:
      - TZ=$TZ
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock:ro
      - $DOCKERDIR/traefik/traefik.yml:/etc/traefik/traefik.yml:ro
      - $DOCKERDIR/traefik/rules:/rules:ro
      - $DOCKERDIR/traefik/acme/acme.json:/acme.json
      - $DOCKERDIR/traefik/logs:/logs
      - $DOCKERDIR/shared:/shared:ro
    healthcheck:
      test: ["CMD", "traefik", "healthcheck", "--ping"]
      interval: 30s
      timeout: 5s
      retries: 3
    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.traefik-rtr.entrypoints=https"
      - "traefik.http.routers.traefik-rtr.rule=Host(`traefik.$DOMAINNAME`)"
      - "traefik.http.routers.traefik-rtr.service=api@internal"
      - "traefik.http.routers.traefik-rtr.middlewares=chain-basic-auth@file"

Das ist die zentrale Datei. Gehen wir sie durch:

  • networks: Wir legen ein Docker-Netzwerk namens web an. Alle Dienste, die Traefik veröffentlichen soll, hängen sich in dieses Netzwerk. Dienste in anderen Netzwerken sind für Traefik unsichtbar. So bestimmst du, was erreichbar ist. Ein docker network create web per Hand brauchst du nicht unbedingt, Compose erledigt das beim ersten Start.
  • image: traefik:v3.7: Ich nenne die Version ausdrücklich. Mit latest kann ein Update dir unbemerkt eine neue Hauptversion bringen, die deine Konfiguration bricht. v3.7 folgt allen Patch-Updates der Reihe 3.7.
  • security_opt: no-new-privileges: Verhindert, dass Prozesse im Container mehr Rechte bekommen als beim Start.
  • ports: Nur 80 und 443 sind von außen erreichbar.
  • volumes: Die Docker-Schnittstelle (docker.sock, nur lesend), die Konfigurationsdateien, der Zertifikatsspeicher, das Log-Verzeichnis und der Ordner mit der .htpasswd. :ro heißt “read-only”. Den Docker-Socket sichern wir weiter unten noch zusätzlich ab.
  • healthcheck: Docker ruft alle 30 Sekunden den Ping-Endpunkt auf und zeigt dir in docker compose ps den Zustand healthy oder unhealthy.
  • labels: Hier steht der Router für das Dashboard. Traefik liest seine eigenen Labels wie die jedes anderen Containers. Das Dashboard hat einen internen Service namens api@internal. Der Router sagt: “Anfragen an traefik.domain.de auf dem Eingang https gehen zum Dashboard, geschützt durch chain-basic-auth”.

Docker Compose ersetzt $TZ, $DOCKERDIR und $DOMAINNAME aus der .env. Mit docker compose config kannst du dir die fertige Datei mit eingesetzten Werten anzeigen lassen und Tippfehler finden:

Terminalbash
cd ~/docker
docker compose config --quiet && echo "Konfiguration in Ordnung"

Traefik starten

Wir starten zunächst mit dem Staging-Server von Let’s Encrypt. Der hat keine strengen Limits, die Zertifikate sind aber nicht vertrauenswürdig. Für echte Zertifikate gelten Beschränkungen (zum Beispiel höchstens 50 neue Zertifikate pro Woche und Domain). Wenn dir beim Ausprobieren ein Fehler unterläuft, sperrt dich Let’s Encrypt sonst für eine Weile.

Terminalbash
cd ~/docker
docker compose up -d
docker compose ps
NAME      IMAGE          COMMAND                  SERVICE   CREATED         STATUS                            PORTS
traefik   traefik:v3.7   "/entrypoint.sh trae…"   traefik   9 seconds ago   Up 8 seconds (health: starting)   0.0.0.0:80->80/tcp, [::]:80->80/tcp, 0.0.0.0:443->443/tcp, [::]:443->443/tcp

Der Healthcheck braucht den ersten Durchlauf, also etwa 30 Sekunden, bis healthy dasteht. Schau in der Zwischenzeit ins Log:

Terminalbash
docker compose logs traefik

Fehlerfrei startet Traefik so (gekürzt):

traefik  | INF Traefik version 3.7.17 built on 2026-10-30T08:24:21Z version=3.7.17
traefik  | INF Starting provider *file.Provider
traefik  | INF Starting provider *docker.Provider
traefik  | INF Starting provider *acme.Provider

Das Log darfst du mit Strg + C verlassen, wenn du es mit -f gestartet hast (docker compose logs -f traefik). Möglicherweise siehst du eine Warnung zu “encoded characters”. Sie betrifft ungewöhnliche Zeichen in URL-Pfaden und ist kein Fehler. Du kannst sie ignorieren, wenn deine Dienste nicht ausdrücklich damit Probleme haben.

Das Dashboard aufrufen

Öffne jetzt https://traefik.domain.de im Browser. Beim ersten Aufruf holt Traefik das Zertifikat. Das dauert etwa 20 bis 30 Sekunden, solange liefert Traefik eine Fehlerseite oder 404 page not found. Das ist normal. Warte kurz und lade die Seite neu. Weil wir im Staging-Modus sind, zeigt der Browser eine Zertifikatswarnung. Das ist richtig, weil dem Staging-Zertifikat kein Browser vertraut. Du kannst die Warnung für den Test übergehen. Danach fragt die Seite nach dem Benutzernamen und Passwort aus der .htpasswd.

Im Log siehst du, wie das Zertifikat entsteht:

traefik  | INF Obtaining bundled SAN certificate. domains=traefik.{{domain}}
traefik  | INF Validations succeeded; requesting certificates. domains=traefik.{{domain}}
traefik  | INF Server responded with a certificate. domains=traefik.{{domain}}

Ob das Zertifikat vom Staging-Server stammt, prüfst du in der Konsole:

Terminalbash
echo | openssl s_client -connect traefik.{{domain}}:443 -servername traefik.{{domain}} 2>/dev/null | openssl x509 -noout -issuer
issuer=C = US, O = Let's Encrypt, CN = (STAGING) Ersatz Emmer YR2

Steht dort (STAGING), hat alles geklappt, und wir können auf echte Zertifikate umstellen. Bleibt das Dashboard dauerhaft leer oder das Zertifikat TRAEFIK DEFAULT CERT, hilft der Abschnitt Fehlersuche.

Auf echte Zertifikate umstellen

Dafür kommentieren wir das caServer aus. Danach kennt Traefik nur noch den normalen Server von Let’s Encrypt. Zertifikate vom Staging-Server bleiben aber in acme.json gespeichert. Traefik würde sie weiter benutzen, deshalb leeren wir die Datei:

Terminalbash
cd ~/docker
nano traefik/traefik.yml
traefik/traefik.ymlyaml
      # caServer: https://acme-staging-v02.api.letsencrypt.org/directory
Terminalbash
docker compose stop traefik
: > traefik/acme/acme.json
docker compose up -d

Der Befehl : > datei leert eine Datei, ohne sie zu löschen. Die Berechtigung 600 bleibt dabei erhalten. Rufe das Dashboard erneut auf, warte wieder etwa eine halbe Minute und prüfe den Aussteller:

Terminalbash
echo | openssl s_client -connect traefik.{{domain}}:443 -servername traefik.{{domain}} 2>/dev/null | openssl x509 -noout -issuer -dates
issuer=C = US, O = Let's Encrypt, CN = YR2
notBefore=Nov  5 08:03:15 2026 GMT
notAfter=Feb  3 08:03:14 2027 GMT

Jetzt vertraut dein Browser dem Zertifikat. Es gilt 90 Tage, und Traefik erneuert es automatisch.

Das Dashboard kennenlernen

Das Dashboard ist ein wichtiges Werkzeug, um zu sehen, was Traefik gerade tut. Die Startseite zeigt die Entrypoints und eine Zusammenfassung aller Router, Services und Middlewares. Fehler und Warnungen sind rot und orange markiert:

Das Traefik-Dashboard mit Entrypoints und Übersicht

Unter HTTP Routers siehst du jede Route. Die Spalte Provider zeigt, woher sie kommt: das Docker-Symbol für Labels, ein Dateisymbol für YAML-Dateien und ein Traefik-Symbol für interne Routen. In meiner Liste tauchen auch zwei Einträge auf, die ich nicht selbst angelegt habe: http-to-https@internal ist die automatische Weiterleitung von Port 80 auf 443, und acme-http@internal beantwortet die Prüfanfragen von Let’s Encrypt.

Liste der HTTP-Router im Dashboard

Unter HTTP Middlewares findest du alle Middlewares und Ketten aus den Dateien. Siehst du dort eine Middleware nicht, die du eingetragen hast, stimmt meist etwas an der Datei nicht (Einrückung oder Tippfehler). Prüfe dann das Log.

Liste der Middlewares im Dashboard

Den ersten Dienst anbinden

Jetzt kommt der Teil, für den wir das alles gemacht haben: ein Dienst hinter Traefik. Als Test nehme ich whoami. Das ist ein winziger Webserver der Traefik-Entwickler, der dir anzeigt, welche Anfrage bei ihm angekommen ist. Er eignet sich gut, um Routing und Middlewares zu testen.

Für jeden Dienst gibt es eine eigene Compose-Datei im Ordner ~/docker. Öffne nano ~/docker/docker-compose-whoami.yml:

docker-compose-whoami.ymlyaml
name: whoami

networks:
  web:
    external: true

services:
  whoami:
    container_name: whoami
    image: traefik/whoami:v1.12
    restart: unless-stopped
    networks:
      - web
    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.whoami-rtr.entrypoints=https"
      - "traefik.http.routers.whoami-rtr.rule=Host(`whoami.$DOMAINNAME`)"
      - "traefik.http.routers.whoami-rtr.middlewares=chain-no-auth@file"

Starte den Dienst:

Terminalbash
docker compose -f docker-compose-whoami.yml up -d

Mit -f gibst du die Datei an, weil sie nicht docker-compose.yml heißt. Nach etwa einer halben Minute (wieder wegen des Zertifikats) antwortet https://whoami.domain.de:

Terminalbash
curl https://whoami.{{domain}}
Hostname: 19a0bcea57b7
IP: 127.0.0.1
IP: ::1
IP: 172.18.0.3
RemoteAddr: 172.18.0.2:38954
GET / HTTP/1.1
Host: whoami.{{domain}}
User-Agent: curl/8.5.0
Accept: */*
Accept-Encoding: gzip
X-Forwarded-For: 203.0.113.10
X-Forwarded-Host: whoami.{{domain}}
X-Forwarded-Port: 443
X-Forwarded-Proto: https
X-Forwarded-Server: 96c86a6352d4
X-Real-Ip: 203.0.113.10

Hinter X-Forwarded-For steht deine IP-Adresse und hinter X-Forwarded-Host die ursprüngliche Domain. Genau diese Header fügt Traefik an, damit der Dienst weiß, wer ihn aufgerufen hat. Gehen wir die Compose-Datei durch:

  • name: whoami: Gibt dem Projekt einen eigenen Namen. Ohne diese Zeile hießen alle Compose-Dateien im Ordner docker gleich. Das bringt Docker durcheinander und führt zu Warnungen über “Orphans” (verwaiste Container). Ansonsten kann man auch Unterordner verwenden.
  • networks: Das Netzwerk web gibt es schon (Traefik hat es angelegt), deshalb external: true.
  • image: traefik/whoami:v1.12: Wieder eine feste Version.
  • ports: Fehlt absichtlich. Der Container veröffentlicht keinen Port, Traefik erreicht ihn nur über das Docker-Netzwerk selbst. Er hat also auch keine offene Flanke zum Internet.
  • Label traefik.enable=true: Ohne dieses Label ignoriert Traefik den Container, weil wir exposedByDefault: false gesetzt haben.
  • Label ...rtr.entrypoints=https: Der Router lauscht nur auf dem Eingang https. HTTP-Anfragen leitet der Entrypoint vorher automatisch um.
  • Label ...rtr.rule=Host(...): Die Regel, wann dieser Router zuständig ist. Hier: wenn die angefragte Domain whoami.domain.de lautet.
  • Label ...rtr.middlewares=chain-no-auth@file: Die Kette, durch die die Anfrage läuft.

Du hast die Zeile für den Service nicht gesehen: Hat ein Container genau einen Port, erkennt Traefik ihn selbst. Bietet er mehrere an oder soll ein anderer verwendet werden, ergänzt du das Label traefik.http.services.NAME.loadbalancer.server.port=PORT. Der Dienst whoami lauscht auf Port 80, deshalb war nichts nötig.

Der Aufbau eines Labels folgt immer demselben Muster: traefik.http.routers. + Name des Routers + . + Einstellung. Die Namen wählst du frei, sie müssen nur innerhalb von Traefik eindeutig sein. Ich hänge -rtr an Router und -svc an Services an, damit ich in Dashboard und Log sofort sehe, was was ist.

Eine Webseite hosten

Als zweites Beispiel veröffentliche ich eine einfache Webseite mit Nginx. Zuerst eine kleine HTML-Datei:

Terminalbash
mkdir -p ~/docker/appdata/testseite
nano ~/docker/appdata/testseite/index.html
index.htmlhtml
<!doctype html>
<html>
	<head>
		<title>Testseite</title>
	</head>
	<body>
		<p>Hallo</p>
	</body>
</html>

Und die Compose-Datei dazu:

docker-compose-testseite.ymlyaml
name: testseite

networks:
  web:
    external: true

services:
  testseite:
    container_name: testseite
    image: nginx:1.31-alpine
    restart: unless-stopped
    networks:
      - web
    volumes:
      - $DOCKERDIR/appdata/testseite:/usr/share/nginx/html:ro
    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.testseite-rtr.entrypoints=https"
      - "traefik.http.routers.testseite-rtr.rule=Host(`testseite.$DOMAINNAME`)"
      - "traefik.http.routers.testseite-rtr.service=testseite-svc"
      - "traefik.http.services.testseite-svc.loadbalancer.server.port=80"
      - "traefik.http.routers.testseite-rtr.middlewares=chain-no-auth@file"
Terminalbash
cd ~/docker
docker compose -f docker-compose-testseite.yml up -d

Hier habe ich den Service ausdrücklich benannt und den Port angegeben. Die Seite ist wenige Augenblicke später unter testseite.domain.de erreichbar. Eine zweite Seite mit Passwortschutz legst du genauso an. Ändere Ordner, Namen und Domain und tausche die Kette gegen chain-basic-auth@file:

docker-compose-testseite2.ymlyaml
      - "traefik.http.routers.testseite2-rtr.middlewares=chain-basic-auth@file"

Traefik erkennt neue Container von selbst. Du musst Traefik dafür weder neu starten noch eine Datei ändern. Dieser Punkt unterscheidet Traefik von klassischen Reverse Proxys, bei denen du jede neue Seite in eine zentrale Konfiguration einträgst. Alle weiteren Dienste aus meinen Anleitungen folgen demselben Schema.

Wenn ein Dienst nicht erscheint, prüfst du mit docker compose -f DATEI ps, ob der Container läuft, und im Dashboard, ob der Router auftaucht.

Middlewares im Detail

Middlewares sind das mächtigste Werkzeug in Traefik. Wir haben vier eingerichtet. Jetzt probieren wir sie aus und lernen ein paar weitere kennen. Alle Tests habe ich mit whoami durchgespielt.

Sicherheits-Header prüfen

Rufe den Dienst mit curl -I ab, das zeigt nur die Header der Antwort:

Terminalbash
curl -I https://whoami.{{domain}}
HTTP/2 200
content-type: text/plain; charset=utf-8
date: Thu, 05 Nov 2026 11:43:39 GMT
permissions-policy: camera=(), geolocation=(), microphone=(), payment=(), usb=()
referrer-policy: same-origin
strict-transport-security: max-age=63072000; includeSubDomains; preload
x-content-type-options: nosniff
x-frame-options: SAMEORIGIN
x-robots-tag: none,noarchive,nosnippet,notranslate,noimageindex
content-length: 340

Die Header kommen von der Middleware middlewares-secure-headers. Beachte, dass auch kein server-Header erscheint, wie von uns gewollt. Mit Diensten wie securityheaders.com kannst du die Wirkung von außen prüfen.

Kompression prüfen

Die Middleware middlewares-compress komprimiert Antworten. Das siehst du an der übertragenen Größe. Dazu fragt whoami über /data?size=5000 genau 5000 Byte Text ab:

Terminalbash
curl -s -o /dev/null -w "ohne gzip: %{size_download} Bytes\n" "https://whoami.{{domain}}/data?size=5000"
curl -s -o /dev/null -w "mit gzip:  %{size_download} Bytes\n" -H "Accept-Encoding: gzip" "https://whoami.{{domain}}/data?size=5000"
ohne gzip: 5000 Bytes
mit gzip:  101 Bytes

Traefik komprimiert erst ab einer gewissen Größe (standardmäßig 1024 Byte). Bei sehr kleinen Antworten siehst du deshalb keinen Unterschied.

Rate-Limit prüfen

Das Rate-Limit lässt zunächst 100 Anfragen pro Sekunde durch und bremst danach mit dem Statuscode 429 Too Many Requests. Wir schicken 500 Anfragen auf einmal (curl --parallel bündelt sie über eine HTTP/2-Verbindung):

Terminalbash
curl -s --parallel --parallel-max 100 -o /dev/null -w "%{http_code}\n" "https://whoami.{{domain}}/?[1-500]" | sort | uniq -c
     57 200
    443 429

Nur etwa 100 Anfragen plus der Burst kommen durch, den Rest weist Traefik ab. Die genauen Zahlen schwanken je nach Zeitpunkt.

Dateien live ändern

Die Middlewares liegen in Dateien, die Traefik überwacht. Änderungen gelten sofort, ohne Neustart. Setze zum Test burst: 50 auf burst: 10:

Terminalbash
sed -i "s/burst: 50/burst: 10/" ~/docker/traefik/rules/middlewares.yml
curl -s --parallel --parallel-max 100 -o /dev/null -w "%{http_code}\n" "https://whoami.{{domain}}/?[1-500]" | sort | uniq -c
     15 200
    485 429

Jetzt blockt das Limit deutlich mehr Anfragen, und das ohne Neustart. Stelle den Wert danach wieder auf 50. Änderungen an den Labels eines Containers wirken dagegen erst, wenn du den Container mit docker compose ... up -d neu erstellst. Änderungen an traefik.yml brauchen einen Neustart von Traefik.

Zugriff nach IP-Adresse einschränken

Mit ipAllowList lässt du nur bestimmte Adressen durch, zum Beispiel deine eigene oder die deines Heimnetzes. Das ist sinnvoll für Verwaltungsoberflächen. Ergänze in middlewares.yml:

traefik/rules/middlewares.ymlyaml
    middlewares-ip-allow:
      ipAllowList:
        sourceRange:
          - "{{server_ip}}/32"

Die Schreibweise /32 steht für genau eine Adresse. Mit /24 erlaubst du ein ganzes Netz aus 256 Adressen. Der Router who2 mit dieser Middleware lässt den Server selbst durch, mein Rechner bekommt 403 Forbidden:

vom Server:       200
von meinem Mac:   403

Sitzt Traefik hinter einem weiteren Proxy (zum Beispiel Cloudflare), sieht es nur dessen Adresse. Dann musst du über ipStrategy festlegen, welchem Header Traefik die echte Adresse entnimmt.

Passwortschutz direkt in den Labels

Einfache Middlewares kannst du auch direkt als Label an den Container hängen, statt sie in eine Datei zu schreiben. Das ist praktisch für Einzelfälle. Hier ein Passwortschutz nur für einen Dienst:

Terminalbash
htpasswd -nbB demo 'demopasswort' | sed -e 's/\$/$$/g'
demo:$$2y$$05$$nHbsDFiydw6270Cte2ym2e353xKr0SyG1mRy0u/d.iaVT...

Das sed verdoppelt jedes Dollarzeichen. Das ist nötig, weil Docker Compose $ als Beginn einer Variable versteht (wie $DOMAINNAME). Mit $$ schreibst du ein echtes Dollarzeichen. Die Datei .htpasswd hat dieses Problem nicht, dort steht der Hash unverändert. Die Labels sehen so aus:

yaml
    labels:
      - "traefik.http.routers.who2-rtr.middlewares=who2-auth"
      - "traefik.http.middlewares.who2-auth.basicauth.users=demo:$$2y$$05$$nHbsDFiydw6270Cte2ym2e353xKr0SyG1mRy0u/d.iaVT..."

Ohne Login antwortet der Dienst mit 401 Unauthorized, mit den richtigen Zugangsdaten mit 200. Mehrere Middlewares trennst du mit Komma, zum Beispiel chain-no-auth@file,who2-auth. Ihre Reihenfolge entscheidet, was zuerst geprüft wird: Steht die IP-Beschränkung vor dem Passwortschutz, bekommen fremde Adressen sofort 403, sonst zuerst 401.

Pfade weiterleiten

Router können nicht nur auf Domains reagieren, sondern auch auf Pfade. Das brauchst du, wenn unter derselben Domain verschiedene Dienste laufen sollen. Die Middleware stripPrefix entfernt den Pfad, bevor die Anfrage beim Dienst ankommt:

yaml
    labels:
      - "traefik.http.routers.who2-api-rtr.entrypoints=https"
      - "traefik.http.routers.who2-api-rtr.rule=Host(`who2.$DOMAINNAME`) && PathPrefix(`/api`)"
      - "traefik.http.routers.who2-api-rtr.priority=100"
      - "traefik.http.routers.who2-api-rtr.middlewares=who2-strip"
      - "traefik.http.middlewares.who2-strip.stripprefix.prefixes=/api"

Ein Aufruf von https://who2.domain.de/api/hallo kommt beim Dienst als GET /hallo an. Die priority sorgt dafür, dass dieser spezifischere Router vor dem allgemeinen gewinnt. Traefik sortiert Router sonst nach der Länge ihrer Regel.

Weitere nützliche Middlewares

Die Traefik-Dokumentation listet über 25 Middlewares. Diese brauchst du am häufigsten:

Middleware Wofür
redirectRegex Adressen umleiten, zum Beispiel www.domain.de auf domain.de
retry Fehlgeschlagene Anfragen automatisch wiederholen
circuitBreaker Einen Dienst bei vielen Fehlern vorübergehend abschalten
buffering Größere Uploads begrenzen oder zwischenspeichern
addPrefix / replacePath Pfade vor dem Dienst umschreiben
errors Eigene Fehlerseiten anzeigen

Die Syntax ist immer ähnlich. In YAML-Dateien schreibst du den Namen der Middleware und ihre Optionen, in Labels hängst du sie an traefik.http.middlewares.NAME.. Das Dashboard zeigt dir, ob Traefik die Konfiguration verstanden hat.

Wildcard-Zertifikat per DNS-Challenge (optional)

Bisher holt Traefik für jede Subdomain ein eigenes Zertifikat. Das reicht für die meisten. Ein Wildcard-Zertifikat (*.domain.de) gilt dagegen für alle Subdomains auf einmal. Es lohnt sich, wenn du viele Dienste betreibst und Subdomains nicht öffentlich im Zertifikatsregister (Certificate Transparency) auftauchen sollen. Dafür verlangt Let’s Encrypt die DNS-Challenge: Traefik beweist, dass die Domain dir gehört, indem es einen DNS-Eintrag anlegt. Dazu braucht Traefik Zugang zu deinem DNS-Anbieter. Dieses Verfahren funktioniert nur mit Anbietern, die eine API haben. Die Liste der unterstützten Anbieter ist lang (unter anderem Netcup, Cloudflare, Hetzner, IONOS und viele weitere).

Bei Netcup brauchst du drei Angaben: Kundennummer, API-Schlüssel und API-Passwort. Im Kundenbereich (CCP) findest du unter den Stammdaten den Menüpunkt “API”. Dort erzeugst du Schlüssel und Passwort. Lies vorher die Nutzungsbedingungen. Mit dem Schlüssel kann man auch Domains bestellen, behandle die Werte also wie ein Passwort.

API-Aktivierung im Netcup-Kundenbereich

Trage die Werte in die .env ein:

.env
NETCUP_CUSTOMER_NUMBER=12345
NETCUP_API_KEY=dein-api-schluessel
NETCUP_API_PASSWORD=dein-api-passwort

Dann ergänzt du in der traefik.yml einen zweiten Resolver, der die DNS-Challenge nutzt, und bindest ihn an den Eingang https:

traefik/traefik.ymlyaml
entryPoints:
  https:
    address: ":443"
    http:
      tls:
        certResolver: letsencrypt-dns
        certResolver: letsencrypt
        domains:
          - main: "{{domain}}"
            sans:
              - "*.{{domain}}"

certificatesResolvers:
  letsencrypt-dns:
    acme:
      storage: /acme.json
      dnsChallenge:
        provider: netcup
        resolvers:
          - "1.1.1.1:53"
          - "8.8.8.8:53"
        propagation:
          delayBeforeChecks: 60s

Und in der Compose-Datei von Traefik reichst du die Zugangsdaten an den Container weiter:

docker-compose.ymlyaml
    environment:
      - TZ=$TZ
      - NETCUP_CUSTOMER_NUMBER=$NETCUP_CUSTOMER_NUMBER
      - NETCUP_API_KEY=$NETCUP_API_KEY
      - NETCUP_API_PASSWORD=$NETCUP_API_PASSWORD

Mit domains fordert Traefik beim ersten Start ein Zertifikat für die Hauptdomain und alle Subdomains an. Die Option delayBeforeChecks gibt dem DNS-Anbieter Zeit, den neuen Eintrag zu verteilen. Wegen der Wartezeit dauert die Ausstellung etwa 5 Minuten. Wartest du kürzer, wirst du vielleicht fälschlich als gescheitert betrachtet. Hast du eine andere Domain, ersetzt du netcup durch den Namen deines Anbieters und die drei Variablen durch die dort verlangten Angaben. Sie stehen jeweils in der Dokumentation des Anbieters. Wer den DNS-Anbieter wechselt oder keine API hat, bleibt bei der HTTP-Challenge.

Google-Login einrichten (optional)

Der Passwortschutz mit Basic Auth ist einfach, aber unbequem: Du tippst auf jedem Gerät Benutzer und Passwort und kannst nicht zentral Nutzer sperren. Besser ist ein Single Sign-On mit einem Konto, das du ohnehin hast, zum Beispiel Google. Der Ablauf heißt Forward Auth: Traefik fragt bei jeder Anfrage einen kleinen Dienst, ob der Besucher angemeldet ist. Ist er es nicht, leitet der Dienst zu Google weiter. Nach der Anmeldung merkt sich ein Cookie, wer du bist.

Dafür nutze ich traefik-forward-auth in Version 4. Das Projekt wird weiterentwickelt, unterstützt außer Google auch Microsoft, GitHub und OpenID-Connect-Anbieter und ersetzt das ältere Projekt von Thomas Seddon, das ich in der ersten Fassung genutzt hatte.

Google-Zugang anlegen

Du musst dich in der Google Cloud Console registrieren (kostenlos) und einen OAuth-Client anlegen. Ich beschreibe die Schritte so, wie die Oberfläche zuletzt aufgebaut war. Google ändert sie öfter, die Menünamen können abweichen:

  1. Lege ein neues Projekt an und gib ihm einen Namen, den du wiederfindest.
  2. Öffne den Bereich Google Auth Platform (früher: APIs & Dienste, OAuth-Zustimmungsbildschirm) und richte den Zustimmungsbildschirm ein: App-Name, deine E-Mail-Adresse und als Zielgruppe “Extern”.
  3. Trage unter den Testnutzern deine eigene Google-Adresse ein. Die App bleibt im Testmodus, nur diese Adressen dürfen sich anmelden. Das reicht für private Dienste.
  4. Erstelle unter Clients einen neuen OAuth-Client vom Typ “Webanwendung”.
  5. Trage bei den autorisierten Weiterleitungs-URIs diese Adresse ein: https://oauth.domain.de/portals/main/oauth2/callback

Google zeigt dir danach Client-ID und Client-Secret. Kopiere beides.

Dienst konfigurieren

Lege einen Ordner und die Konfigurationsdatei an. Die Zugangsdaten stehen in dieser Datei, nicht in der .env. Schütze sie daher mit chmod 600. Der Container läuft dann unter deinem Benutzer (user: "$PUID:$PGID" in der Compose-Datei unten), sonst könnte er die Datei nicht lesen:

Terminalbash
mkdir -p ~/docker/oauth
nano ~/docker/oauth/config.yaml
chmod 600 ~/docker/oauth/config.yaml
oauth/config.yamlyaml
server:
  domains:
    - domain: "{{domain}}"
      authHost: "oauth.{{domain}}"

portals:
  - name: "main"
    providers:
      - google:
          clientID: "DEINE-CLIENT-ID.apps.googleusercontent.com"
          clientSecret: "DEIN-CLIENT-SECRET"

Unter domain steht die Domain, auf der das Cookie gilt. Dadurch genügt eine Anmeldung für alle deine Subdomains. authHost ist die Adresse des Anmeldedienstes selbst. Das Wort main steht für das Anmelde-Portal und taucht in der Callback-Adresse auf.

Jetzt der Container:

docker-compose-oauth.ymlyaml
name: oauth

networks:
  web:
    external: true

services:
  oauth:
    container_name: oauth
    image: ghcr.io/italypaleale/traefik-forward-auth:4
    restart: unless-stopped
    user: "$PUID:$PGID"
    networks:
      - web
    volumes:
      - $DOCKERDIR/oauth/config.yaml:/etc/traefik-forward-auth/config.yaml:ro
    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.oauth-rtr.entrypoints=https"
      - "traefik.http.routers.oauth-rtr.rule=Host(`oauth.$DOMAINNAME`)"
      - "traefik.http.routers.oauth-rtr.service=oauth-svc"
      - "traefik.http.services.oauth-svc.loadbalancer.server.port=4181"

Middleware und Kette

Jetzt kommt die Middleware, die Traefik mit dem Anmeldedienst verbindet. Hänge sie an middlewares.yml an:

traefik/rules/middlewares.ymlyaml
    middlewares-oauth-condition:
      headers:
        customRequestHeaders:
          X-Forward-Auth-If: 'Eq("email", "ich@{{domain}}")'

    middlewares-oauth:
      forwardAuth:
        address: "http://oauth:4181/portals/main"
        trustForwardHeader: true
        authResponseHeaders:
          - X-Forwarded-User
          - X-Forwarded-Displayname
          - X-Authenticated-User

Mit forwardAuth fragt Traefik bei jeder Anfrage den Dienst oauth. Antwortet er mit “angemeldet”, geht die Anfrage weiter, und die Header aus authResponseHeaders (zum Beispiel der Benutzername) werden an den Dienst übergeben. Sonst wird der Besucher zu Google umgeleitet. Die erste Middleware (middlewares-oauth-condition) beschränkt den Zugriff auf eine bestimmte E-Mail-Adresse. Ersetze ich@domain.de durch deine Google-Adresse. Mehrere Adressen verbindest du mit ||: Eq("email", "a@gmail.com") || Eq("email", "b@gmail.com"). Ohne diese Bedingung dürften sich alle Google-Nutzer anmelden, die die Zustimmungsseite zulässt.

Zum Schluss die Kette in chains.yml:

traefik/rules/chains.ymlyaml
    chain-oauth:
      chain:
        middlewares:
          - middlewares-rate-limit
          - middlewares-secure-headers
          - middlewares-compress
          - middlewares-oauth-condition
          - middlewares-oauth

Die Bedingung muss vor middlewares-oauth stehen, weil sie einen Header setzt, den der Anmeldedienst danach liest. Starte den Dienst und tausche bei whoami die Kette gegen chain-oauth@file:

Terminalbash
cd ~/docker
docker compose -f docker-compose-oauth.yml up -d
sed -i 's/chain-no-auth@file/chain-oauth@file/' docker-compose-whoami.yml
docker compose -f docker-compose-whoami.yml up -d

Wenn du jetzt https://whoami.domain.de im Browser öffnest, landest du bei Google. Im Terminal siehst du die Weiterleitungen, wenn du den Weg mit curl verfolgst:

Terminalbash
curl -skL -c /tmp/jar -b /tmp/jar -o /dev/null -w "%{http_code} %{url_effective}\n" https://whoami.{{domain}}

Nach dem Login kehrst du automatisch zu whoami zurück. Der Anmeldedienst stellt Cookies aus, die sich auch auf alle anderen Subdomains auswirken. Dort brauchst du also keine zweite Anmeldung.

Haben die Dienste einen eigenen Login und soll das Google-Konto lediglich eine zusätzliche Hürde sein, nutze chain-oauth für die Verwaltungsseiten. Für öffentliche Webseiten bleibst du bei chain-no-auth.

Docker-Socket absichern (optional)

Traefik braucht den Docker-Socket, um Container zu erkennen. Wer den Socket kontrolliert, kontrolliert Docker, und damit praktisch den Server. Selbst mit :ro kann ein Angreifer, der Traefik übernimmt, über den Socket neue Container starten. Ein Socket-Proxy schließt diese Lücke. Er steht zwischen Traefik und dem Socket und erlaubt nur lesende Abfragen, die Traefik wirklich braucht.

Dafür nehme ich den Docker Socket Proxy von Tecnativa. Die Änderungen an der docker-compose.yml von Traefik:

docker-compose.ymlyaml
networks:
  web:
    name: web
  socket:
    name: socket
    internal: true

services:
  socket-proxy:
    container_name: socket-proxy
    image: tecnativa/docker-socket-proxy:v0.5.0
    restart: unless-stopped
    networks:
      - socket
    environment:
      - CONTAINERS=1
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock:ro

  traefik:
    # ...
    networks:
      - web
      - socket
    depends_on:
      - socket-proxy
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock:ro

Und in der traefik.yml zeigt der Docker-Provider auf den Proxy:

traefik/traefik.ymlyaml
providers:
  docker:
    endpoint: tcp://socket-proxy:2375
    exposedByDefault: false
    network: web

Das Netzwerk socket hat internal: true: Es hat keinen Zugang zum Internet, und nur Traefik und der Proxy hängen darin. Mit CONTAINERS=1 erlaubt der Proxy nur das Lesen der Container-Liste. Alles andere verweigert er. Ein Versuch, über den Proxy einen Container zu erstellen oder Images aufzulisten, endet mit 403 Forbidden. Nach docker compose up -d laufen alle Dienste unverändert weiter, im Container von Traefik gibt es aber keinen Socket mehr:

Terminalbash
docker exec traefik ls /var/run/docker.sock
ls: /var/run/docker.sock: No such file or directory

Von Traefik 2 auf 3 umsteigen

Traefik 2 Traefik 3 Folge, wenn du nichts änderst
HostRegexp(`{host:.+}`) HostRegexp(`.+`) (einfache Regex ohne Namen) Die Regel passt auf nichts mehr. Eine damit gebaute Weiterleitung von HTTP auf HTTPS greift nicht
sslRedirect: true in den Headers Entrypoint-Weiterleitung (redirections) Die Option wird still ignoriert, es gibt keine Weiterleitung
featurePolicy: ... permissionsPolicy: ... Der Header fehlt, du hast keinen Schutz, merkst es aber nicht
browserXssFilter: true entfällt Setzt den veralteten Header X-XSS-Protection, den moderne Browser ignorieren
customFrameOptionsValue: "allow-from ..." SAMEORIGIN oder frameDeny Browser ignorieren allow-from
ipWhiteList ipAllowList Funktioniert noch, ist aber veraltet
TOML-Dateien in rules YAML oder TOML (beides geht) Keine, YAML ist nur gebräuchlicher
--api.insecure / Port 8080 Router mit api@internal Dashboard ohne Passwort im Netz
docker-compose mit version: "3.7" docker compose, ohne version Eine Warnung, weil version veraltet ist
thomseddon/traefik-forward-auth:latest ghcr.io/italypaleale/traefik-forward-auth:4 Funktioniert weiter, wird aber kaum noch gepflegt

Am tückischsten sind die Einstellungen, die nicht mit einem Fehler abbrechen, sondern stillschweigend wirkungslos bleiben. Wenn du aktualisierst, prüfst du deshalb Router und Header gezielt mit curl -I, so wie in diesem Blogpost gezeigt.

Updates und Wartung

Traefik aktualisieren: Mit v3.7 bleibst du automatisch bei den Patch-Versionen der Reihe 3.7. Zum Aktualisieren lädst du das Image neu und startest den Container neu:

Terminalbash
cd ~/docker
docker compose pull
docker compose up -d
docker image prune -f

Der letzte Befehl löscht alte Images, die nicht mehr gebraucht werden. Auf eine neue Hauptversion (zum Beispiel 3.8 oder 4.0) wechselst du bewusst, indem du den Tag in der Compose-Datei änderst. Lies vorher den Migrationsleitfaden der Traefik-Entwickler.

Sicherung: Wichtig sind traefik.yml, die Dateien im Ordner rules, .env, .htpasswd und acme.json. Letztere enthält deine Zertifikate. Du kannst sie neu ausstellen lassen, aber wegen der Let’s-Encrypt-Limits ist es bequemer, sie zu sichern.

Logs ansehen: Für das, was Traefik tut, gibt es drei Anlaufstellen: Das Log des Containers zeigt Meldungen und Fehler. Die Datei ~/docker/traefik/logs/access.log enthält die abgelehnten Anfragen (Statuscodes 400 bis 599). Das Dashboard zeigt den aktuellen Zustand. Das Access-Log sieht so aus:

203.0.113.10 - demo [05/Nov/2026:08:53:06 +0000] "GET / HTTP/2.0" 401 17 "-" "-" 629 "who2-rtr@docker" "-" 0ms
198.51.100.7 - - [05/Nov/2026:08:53:36 +0000] "GET / HTTP/2.0" 403 9 "-" "-" 632 "who2-rtr@docker" "-" 0ms

Jede Zeile enthält die Adresse des Besuchers, den Benutzer (falls angemeldet), Zeit, Anfrage, Statuscode und den Namen des Routers. Das ist hilfreich, wenn du nachvollziehen willst, wer sich ausgesperrt hat. Die Datei wächst nur langsam, weil sie nur Fehler enthält. Brauchst du alle Anfragen, entfernst du den Filter statusCodes in der traefik.yml und richtest eine Log-Rotation ein.

Fehlersuche

Wenn etwas nicht klappt, gehst du in dieser Reihenfolge vor: Läuft der Container (docker compose ps)? Was steht im Log (docker compose logs traefik)? Was zeigt das Dashboard? Die häufigsten Fälle, die ich nachgestellt habe:

  • 404 page not found direkt nach dem Start: Traefik braucht bis zu 40 Sekunden, bis die Routen geladen sind. Warte kurz und versuche es erneut.
  • 404 für einen Dienst, der laufen sollte: Meist fehlt traefik.enable=true. Ohne das Label ignoriert Traefik den Container, wenn exposedByDefault auf false steht. Schau im Dashboard, ob der Router erscheint. Auch ein Tippfehler im Host in der Regel (Host(...)) führt zu 404.
  • 404 bei einer unbekannten Domain oder der IP-Adresse: Das ist gewollt. Traefik antwortet nur auf Domains, für die ein Router existiert.
  • 504 Gateway Timeout: Der Container hängt nicht im Netzwerk web. Im Log steht dann Could not find network named "web" for container ... mit der Folgezeile Defaulting to first available network. Ergänze beim Dienst networks: [web].
  • 502 Bad Gateway: Traefik erreicht den Container, aber nicht auf dem angegebenen Port. Prüfe das Label loadbalancer.server.port und welchen Port die Anwendung im Container wirklich nutzt (das hat nichts mit dem Port auf dem Server zu tun).
  • Browser zeigt eine Zertifikatswarnung: Entweder bist du noch im Staging-Modus (Aussteller (STAGING)) oder Traefik hat noch kein Zertifikat ausgestellt (TRAEFIK DEFAULT CERT). Warte etwa 30 Sekunden und schau im Log nach Obtaining bundled SAN certificate. Bleibt es leer, prüfe, ob die Domain auf deine IP-Adresse zeigt (dig +short), ob Port 80 in der Firewall offen ist und ob du gerade nicht gegen das Limit von Let’s Encrypt gelaufen bist.
  • Der Router erscheint nicht im Dashboard: Bei Dateien ist es meist die Einrückung oder ein Tippfehler in rules/*.yml. Mit docker compose logs traefik siehst du die Fehlermeldung. Bei Labels hilft docker compose config, um zu sehen, was wirklich ankommt.
  • $-Zeichen in Labels: Ein einzelnes $ versteht Compose als Variable. Verdopple es ($$) oder nutze eine Datei.

Mit der Zeile level: DEBUG in der traefik.yml zeigt Traefik sehr ausführlich, was es tut. Das erzeugt viel Text, nutze es nur zur Fehlersuche und stelle danach wieder auf INFO.

Häufige Fragen

Was ist der Unterschied zwischen Traefik, Nginx Proxy Manager und Caddy?

Alle drei sind Reverse Proxys. Nginx Proxy Manager hat eine Weboberfläche, in der du jede Seite per Hand einträgst. Caddy kommt mit sehr kurzer Konfiguration aus, du musst Container aber selbst in die Datei schreiben. Traefik liest Docker-Container aus und konfiguriert sich über Labels, was bei vielen wechselnden Diensten Arbeit spart. Dafür hat Traefik eine steilere Lernkurve.

Brauche ich eine Domain für Traefik?

Für HTTPS mit einem vertrauenswürdigen Zertifikat ja. Let's Encrypt stellt keine Zertifikate für reine IP-Adressen aus. Für Tests im eigenen Netz kannst du Traefik auch ohne Domain und ohne HTTPS nutzen, dann entfallen Zertifikate und Weiterleitung.

Warum erst der Staging-Server von Let's Encrypt?

Der normale Server hat Limits, zum Beispiel höchstens 50 neue Zertifikate pro Woche und registrierter Domain. Ein Fehler beim Ausprobieren kann dich für eine Weile sperren. Der Staging-Server hat keine solchen Limits, seine Zertifikate vertraut aber kein Browser. Wenn dort alles funktioniert, stellst du auf den echten Server um.

HTTP-Challenge oder DNS-Challenge?

Die HTTP-Challenge ist einfacher: Sie funktioniert mit jedem DNS-Anbieter und braucht nur Port 80. Sie stellt aber ein Zertifikat pro Subdomain aus, und der Server muss aus dem Internet erreichbar sein. Die DNS-Challenge braucht eine API deines DNS-Anbieters, kann dafür Wildcard-Zertifikate ausstellen und funktioniert auch für Server ohne öffentlichen Port 80.

Wie binde ich einen neuen Dienst an Traefik an?

Hänge den Container in das Netzwerk web und setze die Labels traefik.enable=true, einen Router mit Host-Regel und Entrypoint https sowie optional eine Middleware-Kette. Hat der Container mehrere Ports, gibst du mit loadbalancer.server.port den richtigen an. Danach reicht docker compose up -d, Traefik muss nicht neu gestartet werden.

Warum bekomme ich direkt nach dem Start einen 404?

Traefik prüft nach dem Start die Zertifikate und liest die Docker-Container ein. Das dauert in meinen Tests bis zu 40 Sekunden. Bis dahin antwortet Traefik mit 404. Ein dauerhafter 404 hat meistens eine andere Ursache, zum Beispiel ein fehlendes traefik.enable=true oder einen Tippfehler in der Host-Regel.

Ist der Docker-Socket in Traefik ein Sicherheitsrisiko?

Ja, ein gewisses. Wer den Socket kontrolliert, kontrolliert Docker. Du kannst das Risiko mit einem Socket-Proxy verringern, der nur lesende Abfragen weiterreicht. Im Abschnitt zum Docker-Socket zeige ich die Einrichtung mit docker-socket-proxy.

Warum soll ich den Tag latest vermeiden?

Mit latest holt Docker bei jedem Neuladen die neueste Version, auch wenn sie nicht zu deiner Konfiguration passt. Wer Traefik 2 mit latest betrieben hat, bekam auf einmal Traefik 3 und musste Konfigurationen ändern. Mit einem festen Tag wie v3.7 aktualisierst du bewusst.

Meine alte Traefik-2-Konfiguration geht nicht mehr. Was tun?

Prüfe zuerst, ob du auf Version 3 gelandet bist (Dashboard, oben links). Die wichtigsten Änderungen: HostRegexp ohne {name:...}, sslRedirect entfällt, featurePolicy heißt permissionsPolicy.

Kann ich Traefik auch ohne Docker nutzen?

Ja. Traefik gibt es als einzelne Programmdatei, und der File-Provider ersetzt dann die Docker-Labels. Für diese Anleitung ist Docker aber die einfachste Variante, weil Traefik Container selbst erkennt.

Fazit

Du hast jetzt einen Server, der mehrere Dienste unter eigenen Subdomains bereitstellen kann, mit automatischem HTTPS, geschütztem Dashboard und wiederverwendbaren Middlewares. Neue Dienste kommen mit ein paar Labels dazu, ohne dass du Traefik anfassen musst. Die Einstellungen, die dir besonders weiterhelfen: ein fester Image-Tag statt latest, das Staging-Verfahren vor dem Umstieg auf echte Zertifikate, und Ketten statt einzelner Middlewares.

Als Nächstes lohnen sich echte Dienste: Portainer als Oberfläche für deine Container, n8n für Automatisierungen, Ghost als Blog oder Plausible für Statistiken. Wenn du das Konzept lieber an einem einfachen Beispiel üben willst, siehst du in Astro und Docker, wie eine Webseite in einen Container kommt. Bei Fragen schreib mir gern einen Kommentar.

Teilen
XRedditE-Mail
Zurück zu allen Beiträgen

Änderungsprotokoll

  1. Auf Traefik 3 und Ubuntu 24.04 umgestellt. Konfiguration in traefik.yml und YAML-Dateien, Zertifikate per HTTP-Challenge, Dashboard ohne offenen Port, aktuelle Middleware-Optionen, Docker-Socket-Proxy, neuer Forward-Auth-Dienst. Neue Abschnitte zu Begriffen, Firewall, Fehlersuche, Migration von Version 2 und FAQ.

Verwandte Artikel

Kommentare