WordPress mit Docker & Traefik sauber betreiben
WordPress mit Docker und MariaDB hinter Traefik aufsetzen – mit HTTPS, isolierter Datenbank und ehrlicher Update-Strategie statt Wartungs-Albtraum.
Inhaltsverzeichnis
WordPress betreibt einen großen Teil des Webs – und ist berüchtigt für gehackte Installationen. Der Unterschied zwischen „läuft und ist sicher“ und „wird zur Spam-Schleuder“ liegt im sauberen Aufbau und in ehrlicher Wartung. Dieses Rezept liefert beides: WordPress mit Docker, isolierter Datenbank, HTTPS – und einer klaren Ansage, was du dauerhaft tun musst.
Was bauen wir?
Eine produktionstaugliche WordPress-Installation mit WordPress 7.1.0 (PHP 8.5) und MariaDB 11.8 hinter Traefik. Die Datenbank läuft in einem abgeschotteten internen Netz ohne Internetzugang, WordPress erhält sein HTTPS von Traefik, und wir lösen den klassischen Reverse-Proxy-Stolperstein (Redirect-Schleife/Mixed-Content) sauber. Am Ende hast du einen laufenden Blog – und weißt, wie du ihn sicher hältst.
WordPress oder Hugo?
Voraussetzungen
- Ein Server mit laufendem Traefik und Docker Compose
- Eine Domain, die auf den Server zeigt – im Folgenden
DEINE_DOMAIN - Etwas mehr RAM als für einfache Dienste – WordPress + Datenbank + PHP laufen komfortabel ab dem VPS 2000
VPS 2000 G12.5
8 vCore · 16 GB RAM · 256 GB SSD
ab 26,92 €/Monat
WordPress mit Datenbank und PHP läuft entspannt auf dem VPS 2000.
💶 5 € Gutschein für netcup-Neukunden: (nicht für Domains und VPS Lite)
Schritt für Schritt
Schritt 1: Zwei Container, zwei Netze
WordPress besteht aus zwei Teilen: der PHP-Anwendung und einer MariaDB-Datenbank. Ein sauberes Setup trennt beide und schottet die Datenbank ab – sie soll nur von WordPress erreichbar sein und braucht selbst kein Internet. Genau dafür nutzen wir zwei Netze (siehe Docker-Netzwerke verstehen): das öffentliche proxy-Netz zwischen WordPress und Traefik, und ein internal-Netz zwischen WordPress und Datenbank.
mkdir -p /opt/wordpress && cd /opt/wordpressSchritt 2: Die Compose-Datei
Ersetze DEINE_DOMAIN und alle Passwörter durch eigene Werte:
services:
db:
image: mariadb:11.8
restart: unless-stopped
environment:
MARIADB_DATABASE: wordpress
MARIADB_USER: wordpress
MARIADB_PASSWORD: DEIN_DB_PASSWORT
MARIADB_RANDOM_ROOT_PASSWORD: "1"
volumes:
- db_data:/var/lib/mysql
networks: [intern]
healthcheck:
test: ["CMD", "healthcheck.sh", "--connect", "--innodb_initialized"]
start_period: 10s
start_interval: 2s
interval: 30s
timeout: 5s
retries: 5
wordpress:
image: wordpress:7.1.0-php8.5-apache
restart: unless-stopped
depends_on:
db:
condition: service_healthy
environment:
WORDPRESS_DB_HOST: db
WORDPRESS_DB_USER: wordpress
WORDPRESS_DB_PASSWORD: DEIN_DB_PASSWORT
WORDPRESS_DB_NAME: wordpress
WORDPRESS_CONFIG_EXTRA: |
if (isset($$_SERVER['HTTP_X_FORWARDED_PROTO']) && $$_SERVER['HTTP_X_FORWARDED_PROTO'] === 'https') { $$_SERVER['HTTPS'] = 'on'; }
define('WP_HOME', 'https://DEINE_DOMAIN');
define('WP_SITEURL', 'https://DEINE_DOMAIN');
volumes:
- wp_data:/var/www/html
networks: [proxy, intern]
labels:
- "traefik.enable=true"
- "traefik.http.routers.wp.rule=Host(`DEINE_DOMAIN`)"
- "traefik.http.routers.wp.entrypoints=websecure"
- "traefik.http.routers.wp.tls.certresolver=le"
- "traefik.http.services.wp.loadbalancer.server.port=80"
volumes:
db_data: {}
wp_data: {}
networks:
proxy:
external: true
intern:
internal: trueDrei Details sind hier entscheidend:
depends_on: condition: service_healthy– WordPress startet erst, wenn die Datenbank per Healthcheck als bereit gemeldet wird. Ohne das scheitert der erste Start in einer Race Condition.internistinternal: true– die Datenbank hängt nur in diesem Netz und hat damit keinen Internetzugang; WordPress erreicht sie unter dem Hostnamendb.WORDPRESS_CONFIG_EXTRA– der wichtigste Teil (nächster Schritt).
Der Reverse-Proxy-Stolperstein: HTTPS erkennen
http://-Links – das führt zu Mixed-Content-Fehlern oder einer Redirect-Schleife (ERR_TOO_MANY_REDIRECTS). Die Zeile if (... HTTP_X_FORWARDED_PROTO ... === 'https') { $_SERVER['HTTPS'] = 'on'; } sagt WordPress, dass die ursprüngliche Verbindung HTTPS war. Zusammen mit WP_HOME/WP_SITEURL ist das die saubere Lösung. In Compose müssen die PHP-Variablen als $$ geschrieben werden, sonst interpretiert Docker sie als eigene Variablen.Schritt 3: Starten
docker compose up -dDocker startet zuerst die Datenbank, wartet auf ihren Healthcheck und startet dann WordPress. Prüfe:
docker compose psNAME IMAGE SERVICE STATUS
wordpress-db-1 mariadb:11.8 db Up (healthy)
wordpress-wordpress-1 wordpress:7.1.0-php8.5-apache wordpress UpPrüfe von außen, dass WordPress mit gültigem HTTPS antwortet (die 302 leitet zur Installation):
curl -sI https://DEINE_DOMAIN/ | head -1HTTP/2 302Schritt 4: Die berühmte 5-Minuten-Installation
Öffne https://DEINE_DOMAIN/. WordPress leitet zum Installationsassistenten. Nach der Sprachwahl (Deutsch) füllst du die Grunddaten aus:

Kein „admin“ als Benutzername
admin – er ist das erste Ziel jedes Brute-Force-Angriffs auf WordPress. Nimm einen eigenen Namen und ein starkes Passwort. Das ist die einfachste und wirksamste Härtungsmaßnahme überhaupt.Nach dem Klick auf „WordPress installieren“ und dem ersten Login landest du im Dashboard:

Schritt 5: Die Website ist live
Deine Seite ist sofort öffentlich erreichbar – mit gültigem HTTPS von Traefik, ohne Mixed-Content-Fehler dank der Proxy-Konfiguration:

Schritt 6: Grundlegende Härtung
WordPress ist ab Werk funktional, aber nicht optimal abgesichert. Die wichtigsten Sofortmaßnahmen:
- Automatische Updates aktivieren: Unter Dashboard → Aktualisierungen sicherstellen, dass zumindest Sicherheitsupdates automatisch eingespielt werden.
- Unnötige Standard-Inhalte löschen: Der „Hallo Welt!“-Beitrag, die Beispiel-Seite und das mitgelieferte Beispiel-Plugin (
Hello Dolly) können weg. - Weniger ist mehr bei Plugins: Jedes Plugin ist zusätzliche Angriffsfläche. Installiere nur, was du wirklich brauchst, und nur aus vertrauenswürdiger Quelle.
- Login zusätzlich schützen: Vor die WordPress-Anmeldung (
/wp-login.php) gehört idealerweise eine weitere Hürde. Bewährt sind eine Rate-Limit-/BasicAuth-Middleware in Traefik oder ein 2FA-Plugin. Auch Fail2ban kann WordPress-Login-Versuche auswerten. - XML-RPC im Blick behalten: Die Datei
xmlrpc.phpist ein häufiges Ziel für Brute-Force und DDoS. Wird sie nicht gebraucht, per Traefik-Middleware oder Plugin sperren.
Wenn es nicht funktioniert
Redirect-Schleife (ERR_TOO_MANY_REDIRECTS) oder Mixed-Content-Warnungen. Die HTTPS-Erkennung fehlt. Prüfe, dass der WORDPRESS_CONFIG_EXTRA-Block mit HTTP_X_FORWARDED_PROTO und den $$-Zeichen exakt gesetzt ist (siehe Warnung in Schritt 2), und starte den WordPress-Container neu.
WordPress startet nicht, meldet „Error establishing a database connection“. Meist eine Race Condition oder ein Passwort-Mismatch. Prüfe, dass depends_on: condition: service_healthy gesetzt ist und WORDPRESS_DB_PASSWORD exakt MARIADB_PASSWORD entspricht. Logs: docker compose logs db.
Der erste Start scheitert, obwohl später alles läuft. Die Datenbank braucht beim allerersten Start einige Sekunden zur Initialisierung. Mit dem Healthcheck ist das abgedeckt; ohne ihn hilft ein erneutes docker compose up -d.
Medien-Uploads schlagen bei großen Dateien fehl. PHP begrenzt die Upload-Größe. Lege eine eigene uploads.ini an und mounte sie nach /usr/local/etc/php/conf.d/ mit z. B. upload_max_filesize = 64M und post_max_size = 64M.
Nach einem Domainwechsel zeigt die Seite ins Leere. WP_HOME/WP_SITEURL in der Compose-Datei stehen fest verdrahtet – bei einer neuen Domain dort anpassen und Container neu starten (die Werte überschreiben die Datenbank-Einstellung).
Wartung & Backups
- Ehrlich: WordPress ist Wartungsarbeit. Anders als eine statische Seite läuft hier aktiver Code mit Datenbank – Kern, Themes und Plugins bekommen regelmäßig Sicherheitsupdates, die zeitnah eingespielt werden müssen. Ein vernachlässigtes WordPress wird zuverlässig gekapert. Aktiviere automatische Updates und prüfe monatlich den Aktualisierungsstatus. Den Container-Tag (
wordpress:7.1.0-php8.5-apache) hältst du über deinen normalen Update-Prozess aktuell. - Backup ist zweiteilig – beides ist Pflicht. WordPress besteht aus Datenbank (Beiträge, Einstellungen, Nutzer) und Dateien (
wp_data-Volume: Uploads, Themes, Plugins). Ein Restic-Snapshot des laufenden MariaDB-Volumes allein ist keine Garantie – sichere die Datenbank zusätzlich per Dump. Wichtig sind dabei-T(sonst hängt der Befehl an der Passwortabfrage und legt eine leere Datei an) und das Passwort direkt am-p, ohne Leerzeichen:
docker compose exec -T db mariadb-dump -u wordpress -p'DEIN_DB_PASSWORT' --single-transaction wordpress > backup.sqlPrüfe danach unbedingt, dass die Datei nicht leer ist (ls -l backup.sql). Dump und wp_data-Volume gehören ins Restic-Backup.
- Wiederherstellung testen. Ein Backup, das nie zurückgespielt wurde, ist nur eine Hoffnung – spiele einen Restore mindestens einmal probeweise durch, damit du im Ernstfall weißt, dass Dump und Dateien zusammenpassen.
Feedback per E-Mail: feedback@serverkueche.de
Das könnte dir auch schmecken

Nextcloud selbst hosten: deine eigene Cloud hinter Traefik
Nextcloud mit Docker hinter Traefik aufsetzen: eigene Cloud für Dateien, Kalender und Kontakte – mit MariaDB, Redis und …

linkding: Lesezeichen selbst hosten
Alle Lesezeichen an einem Ort, geräteübergreifend und ohne Browser-Zwang: linkding als schlanker Bookmark-Manager hinter …

Navidrome: die eigene Musik streamen wie mit Spotify
Deine Musiksammlung von überall streamen – ohne Abo und Tracking: Navidrome hinter Traefik, mit App-Anbindung über den …