// Tutorial · CMS · Docker

WordPress per Docker Compose: Site, MySQL & phpMyAdmin

📅 06.09.2026 ⏱ 10 Min. Lesezeit ✍️ Redaktion

WordPress, MariaDB und phpMyAdmin in einem Stack: Wir zeigen, wie du deine Site per Docker Compose aufsetzt, worauf du bei der Konfiguration achten musst und welche Stolperfallen dich erwarten.

Warum WordPress in Docker?

WordPress ist nach wie vor das am weitesten verbreitete CMS – und mit Docker Compose wird die Verwaltung der kompletten Umgebung übersichtlich: Datenbank, Webserver und optional phpMyAdmin laufen als sauber getrennte Container. Updates werden zu einem docker compose pull && up -d, Backups lassen sich auf wenige Ordner reduzieren, und bei einem Umzug zieht der ganze Stack einfach mit.

Gut zu wissen: Dein Setup nutzt MariaDB als Ersatz für MySQL – das ist offiziell ein vollwertiger Drop-in-Ersatz und bei Docker der häufigere Standard. Der Dienstname bleibt mysql, damit sich an WordPress nichts ändert.

Konzept: Drei Dienste, ein Stack

Dein Stack besteht aus drei Containern, die über das interne Compose-Netzwerk miteinander reden:

  • mysql (mariadb): Speichert alle Inhalte. Die Daten liegen in ./mysql auf dem Host – ohne dieses Volume wären alle Beiträge beim Container-Neustart weg.
  • phpmyadmin: Komfortable Web-Oberfläche für die Datenbank. Praktisch zum Entwickeln – aber ein beliebtes Angriffsziel, wenn sie öffentlich erreichbar ist (dazu unten mehr).
  • wordpress: Das offizielle Image bringt Apache + PHP + WordPress mit; dein Quellcode samt Uploads liegt in ./wp0.
Vorsicht – Startsynchronisation: Ein einfaches depends_on wartet nur darauf, dass der MySQL-Container gestartet ist – nicht, dass die Datenbank bereit ist. Beim allerersten Start kommt es dadurch oft zum „Error establishing a database connection“. Die Lösung findest du im Abschnitt Konfiguration.

Vorbereitung: Ordner & Passwörter

Zwei Bind-Mounts halten deine Daten am Leben – sie sollten vor dem ersten Start existieren:

mkdir -p ~/wordpress/mysql ~/wordpress/wp0
cd ~/wordpress

Lege die Compose-Datei als docker-compose.yml in diesem Ordner ab. Die Passwörter solltest du nicht direkt in der Datei führen, sondern in einer .env (mit chmod 600):

# .env – nicht ins Git!
MYSQL_ROOT_PASSWORD=meinsichererespassw0rt26#
MYSQL_PASSWORD=meinsmysqlpassw0rt#

In der Compose-Datei referenzierst du sie dann als \${MYSQL_ROOT_PASSWORD}. Für die Übersicht zeigt der Artikel unten die Klartext-Variante deiner Vorlage.

Docker Compose – die Datei

Deine Vorlage – bereinigt um drei Altlasten: Das veraltete version: "3.9" (Compose v2 ignoriert es ohnehin), den leeren networks: {}-Block sowie das ungenutzte Named Volume mysql_data (dein MySQL nutzt bereits den Bind-Mount ./mysql).

services:
  mysql:
    image: mariadb:latest          # MariaDB als MySQL-Drop-in
    restart: always
    environment:
      - MYSQL_ROOT_PASSWORD=meinsichererespassw0rt26#   # besser: via .env
      - MYSQL_DATABASE=site1
      - MYSQL_USER=wordpressuser
      - MYSQL_PASSWORD=meinsmysqlpassw0rt#
    volumes:
      - ./mysql:/var/lib/mysql     # DB-Datenbankdateien

  phpmyadmin:
    image: phpmyadmin/phpmyadmin
    depends_on:
      - mysql
    environment:
      - PMA_HOST=mysql
      - PMA_PORT=3306
      - PMA_ARBITRARY=1            # beliebige Server wählbar
      - UPLOAD_LIMIT=400M
    restart: always
    ports:
      - "127.0.0.1:8185:80"        # nur lokal – niemals öffentlich!

  wordpress:
    image: wordpress:latest
    depends_on:
      - mysql
    ports:
      - "127.0.0.1:50555:80"
    restart: always
    environment:
      - WORDPRESS_DB_HOST=mysql:3306
      - WORDPRESS_DB_USER=wordpressuser
      - WORDPRESS_DB_PASSWORD=meinsmysqlpassw0rt#
      - WORDPRESS_DB_NAME=site1
    volumes:
      - ./wp0:/var/www/html
Passwörter im Klartext: In dieser Form stehen alle Zugangsdaten lesbar in der Datei. Für lokale Test-Stacks okay – für Produktion bitte die .env-Variante aus dem vorigen Abschnitt nutzen und andere Passwörter wählen als die hier gezeigten.

Konfiguration & Umgebungsvariablen

VariableBedeutung
WORDPRESS_DB_HOSTDatenbank-Host im Compose-Netzwerk: mysql:3306
WORDPRESS_DB_USER / _PASSWORD / _NAMEZugangsdaten für WordPress – müssen zu den MYSQL_*-Werten passen
MYSQL_ROOT_PASSWORDRoot-Passwort der Datenbank (nur beim ersten Volume-Start relevant)
PMA_HOST / PMA_PORTVerbindung phpMyAdmin → MariaDB über den Dienstnamen mysql
PMA_ARBITRARYErlaubt in phpMyAdmin, jeden beliebigen Server einzutragen (praktisch, aber verzichtbar)
UPLOAD_LIMITUpload-Limit von phpMyAdmin (400 MB in deiner Config)

Empfohlener Fix für den Erststart: Damit WordPress wartet, bis die Datenbank wirklich bereit ist:

services:
  mysql:
    image: mariadb:latest
    # ... restliche Config ...
    healthcheck:
      # Nutzt den nativen CLI-Ping, der garantiert in jedem MariaDB-Image existiert
      test: ["CMD", "mariadb-admin", "ping", "-h", "localhost", "-u", "wordpressuser", "-pmeinsmysqlpassw0rt#"]
      interval: 5s
      timeout: 3s
      retries: 5

  wordpress:
    image: wordpress:latest
    depends_on:
      mysql:
        condition: service_healthy

So startet WordPress erst, wenn der MariaDB-Healthcheck erfolgreich war – der „database connection“-Fehler beim ersten up verschwindet damit.

Zugriff, Reverse Proxy & phpMyAdmin

Beide Web-Ports sind in deiner Config nur an 127.0.0.1 gebunden – WordPress unter :50555, phpMyAdmin unter :8185. Das ist die richtige Grundentscheidung:

phpMyAdmin niemals öffentlich machen! Eine frei erreichbare PMA-Instanz ist ein Dauerbrenner für Brute-Force-Angriffe auf die Datenbank. Lass sie auf 127.0.0.1 und greife per SSH-Tunnel zu – oder entferne den Dienst in Produktion komplett. Notwendige DB-Arbeiten gehen auch per CLI (docker compose exec mysql mysql …).

Für WordPress selbst hängst du einen Reverse Proxy mit TLS davor:

server {
  listen 443 ssl;
  server_name blog.meine.domain;
  # … Zertifikate …

  client_max_body_size 64M;   # für größere Uploads/Mediathek

  location / {
    proxy_pass http://127.0.0.1:50555;
    proxy_set_header Host $http_host;
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header X-Forwarded-Proto $scheme;
  }
}

Wichtig: In den WordPress-Einstellungen unter Einstellungen → Allgemein als WordPress-Adresse und Site-Adresse exakt dieselbe https://-URL eintragen – sonst brechen Assets und Links.

Erster Start & Einrichtung

docker compose up -d
docker compose ps

Öffne http://127.0.0.1:50555 (bzw. deine Proxy-Domain): Der WordPress-Installer fragt Sprache, Seitentitel und das Admin-Konto ab. Danach:

  • Permalinks setzen: Einstellungen → Permalinks → „Beitragsname“. Wichtig für saubere URLs – vor allem, wenn du den Proxy später ergänzt.
  • Zeitzone/Sprache anpassen (Standard ist UTC).
  • Updates einplanen: Kern, Themes und Plugins regelmäßig aktualisieren – das Container-Image allein reicht nicht.
  • Login absichern: starkes Admin-Passwort, Login-Limits per Plugin, ggf. 2FA.
Probe: docker compose logs -f wordpress zeigt dir im Fehlerfall sofort, ob PHP-Fehler, DB-Verbindungsprobleme oder Dateirechte die Ursache sind.

Backups & Updates

Dein komplettes Backup besteht aus zwei Teilen:

# 1. Datenbank-Dump
docker compose exec mysql mysqldump -u root -pDEIN_ROOT_PASSWORT site1 > site1_$(date +%F).sql

# 2. Dateien
tar czf wp0_$(date +%F).tar.gz wp0/

Beides auf einen anderen Rechner (oder in ein Backup-Ziel) kopieren – ein lokales Backup auf demselben Host ist kein Backup. Für automatisierte Dumps eignet sich ein Cron-Job oder ein dedizierter Backup-Container.

Updates laufen über die Container-Images, nicht über die WP-Admin-Oberfläche:

docker compose pull
docker compose up -d

WordPress-Dateien in wp0/ werden dabei nicht angefasst – Kern-Updates machst du am besten per WP-CLI, z. B. mit dem wordpress:cli-Image (getrennt vom Webserver-Container).

Häufige Probleme (FAQ)

ProblemLösung
„Error establishing a database connection“Erststart-Rennen: MariaDB-Healthcheck + condition: service_healthy einbauen, dann neu starten.
413 Request Entity Too Large bei Uploadsclient_max_body_size 64M; im Nginx-Proxy erhöhen. Wichtig für WordPress: Erstelle zusätzlich im WordPress-Ordner ./wp0 auf dem Host eine Datei namens .user.ini und trage dort upload_max_filesize = 64M und post_max_size = 64M ein. So hebst du das PHP-Standardlimit von 2 MB ohne Container-Umbau auf.
Dateien in ./wp0 nicht beschreibbarRechte prüfen: Das Image läuft als www-data – bei Bedarf chown -R www-data:www-data wp0 bzw. Gruppe anpassen.
phpMyAdmin will nicht ladenLogs prüfen (docker compose logs phpmyadmin); PMA_HOST muss exakt mysql heißen.
Port-Konflikt bei :50555/:8185docker ps prüfen und den linken Teil des Port-Mappings ändern.
Nach einem Image-Update startet nichtsdocker compose up -d --force-recreate ausführen und Logs ansehen – meist reicht ein Recreate nach dem Wechsel.

Fazit

Der Stack ist in wenigen Minuten lauffähig – die eigentliche Arbeit steckt wie immer im Betrieb: starke Passwörter (nicht in der Compose-Datei), kein öffentliches phpMyAdmin, regelmäßige Backups aus mysql/ + wp0/ und konsequente Updates.

Merksätze ① Daten liegen nur in den Bind-Mounts – ohne Volume ist nach dem Entfernen des Containers alles weg.
② phpMyAdmin gehört hinter localhost, nicht ins Internet.
③ Healthcheck + service_healthy behebt den Erststart-Fehler.
④ Backups = DB-Dump + wp0/-Ordner, auf einem anderen Rechner.
📝
....

Wir betreiben unsere komplette Infrastruktur selbst auf Open-Source-Software und schreiben nur über Tools, die wir im Alltag wirklich einsetzen. Fragen zum Artikel? Schreib uns.