Ghost (Docker Compose): Datenbankpasswörter sicher ändern

Ghost (Docker Compose): Datenbankpasswörter sicher ändern
Photo by Towfiqu barbhuiya / Unsplash
Voraussetzungen: Ghost läuft als Docker-Compose-Stack mit MySQL. Konfiguration erfolgt über eine .env-Datei. Kein Datenverlust, da Volumes erhalten bleiben.

Überblick

Beim Ändern des Datenbankpassworts müssen zwei Stellen konsistent gehalten werden:

  1. Der MySQL-Datenbankbenutzer (in der laufenden Datenbank)
  2. Die .env-Datei (Konfiguration für Ghost und MySQL)

Werden diese nicht synchron geändert, verweigert Ghost den Verbindungsaufbau mit Access denied.

Die Reihenfolge ist entscheidend – dieser Artikel führt Schritt für Schritt durch den gesamten Prozess.


Schritt 1: Neue Passwörter generieren

Zuerst werden sichere neue Passwörter generiert. Dafür wird pwgen verwendet.

Installation (falls noch nicht vorhanden):

# Debian/Ubuntu
apt install pwgen

# Alpine (z.B. im Container)
apk add pwgen

# macOS
brew install pwgen

Passwörter generieren:

# Empfehlung: 32 Zeichen, sicher, ohne verwechselbare Zeichen (0/O, l/1)
pwgen -s -B 32 1

Beispielausgabe:

k9mRvT3nXqFhWpLcYdA8sJeGbNuZiQ2w

Für den Stack werden zwei Passwörter benötigt:

# Ghost-Benutzer-Passwort
pwgen -s -B 32 1

# MySQL Root-Passwort
pwgen -s -B 32 1
Hinweis: Sonderzeichen (-y) werden hier bewusst weggelassen, da manche Shell-Umgebungen und YAML-Dateien diese ohne Anführungszeichen falsch interpretieren. 32 zufällige alphanumerische Zeichen sind kryptografisch vollkommen ausreichend.

Die generierten Passwörter sicher notieren – sie werden in den nächsten Schritten benötigt.


Schritt 2: Stack stoppen

Der gesamte Docker-Compose-Stack wird gestoppt. Kein --volumes – die Daten bleiben erhalten.

cd /opt/ghost   # Pfad zum Compose-Verzeichnis anpassen

docker compose down

Überprüfen, dass alle Container gestoppt sind:

docker compose ps
# Erwartete Ausgabe: leere Liste oder alle im Status "exited"

Schritt 3: .env-Datei sichern und aktualisieren

Vor der Änderung ein Backup anlegen:

cp .env .env.backup

Aktuelle .env öffnen:

nano .env

Die relevanten Zeilen auf die neuen Passwörter aktualisieren:

# Vorher:
MYSQL_PASSWORD=altesPasswortGhost
MYSQL_ROOT_PASSWORD=altesRootPasswort

# Nachher – neue Werte eintragen:
MYSQL_PASSWORD=k9mRvT3nXqFhWpLcYdA8sJeGbNuZiQ2w
MYSQL_ROOT_PASSWORD=m4PzXsNqWbLhTcAeKdRvYuFjGiOn7E2x

Datei speichern (Ctrl+O, dann Ctrl+X bei nano).

Wichtig: Zu diesem Zeitpunkt kennt die Datenbank noch das alte Passwort. Die .env enthält bereits das neue. Deshalb wird im nächsten Schritt nur die Datenbank gestartet und das Passwort dort manuell geändert.

Schritt 4: Nur die Datenbank starten (mit altem Passwort)

Da das MySQL-Volume persistent ist, kennt MySQL noch das alte Passwort. Der DB-Container wird isoliert gestartet:

docker compose up -d db

Warten bis MySQL vollständig hochgefahren ist (ca. 5–10 Sekunden), dann prüfen:

docker compose logs db | tail -5
# Erwartete Meldung: "ready for connections"

Schritt 5: In den MySQL-Container verbinden und Passwörter ändern

Mit dem alten Root-Passwort in den Container einloggen:

docker compose exec db mysql -u root -p
# Eingabe: altes Root-Passwort

Nach dem Login in der MySQL-Shell:

-- Ghost-Benutzer-Passwort ändern
ALTER USER 'ghost'@'%' IDENTIFIED BY 'k9mRvT3nXqFhWpLcYdA8sJeGbNuZiQ2w';

-- Root-Passwort ändern
ALTER USER 'root'@'localhost' IDENTIFIED BY 'm4PzXsNqWbLhTcAeKdRvYuFjGiOn7E2x';

-- Änderungen sofort wirksam machen
FLUSH PRIVILEGES;

EXIT;
Kontrolle: Nach jedem ALTER USER erscheint Query OK, 0 rows affected (0.01 sec). Das ist korrekt und zeigt Erfolg an.

Schritt 6: Verbindung mit neuem Passwort testen

Kurze Überprüfung, ob die neuen Zugangsdaten funktionieren:

docker compose exec db mysql -u ghost -p'k9mRvT3nXqFhWpLcYdA8sJeGbNuZiQ2w' ghost -e "SELECT 1;"

Erwartete Ausgabe:

+---+
| 1 |
+---+
| 1 |
+---+

Schlägt dies fehl, wurde das Passwort nicht korrekt übernommen. In diesem Fall Schritt 5 wiederholen.


Schritt 7: Gesamten Stack starten

Jetzt läuft die Datenbank mit den neuen Passwörtern und die .env enthält dieselben Werte – der Stack kann vollständig gestartet werden:

docker compose up -d

Schritt 8: Ghost-Start überprüfen

Logs beobachten:

docker compose logs -f ghost

Erwartete Erfolgsmeldung:

Ghost is running in production...
Listening on: http://:::2368
Url configured as: https://deine-domain.de

Erscheint stattdessen Access denied for user 'ghost'@'...', stimmt das Passwort in der .env nicht mit dem in MySQL überein. Dann:

  1. Passwörter in .env und Schritt 5 vergleichen
  2. docker compose down → zurück zu Schritt 4

Schritt 9: Aufräumen

Wenn alles funktioniert, das Backup der alten .env entfernen:

rm .env.backup

Referenz: docker-compose.yml mit .env-Variablen

Zur Vollständigkeit – so sollte die docker-compose.yml aussehen, damit die .env-Variablen korrekt übergeben werden:

services:
  ghost:
    image: ghost:6-alpine
    restart: always
    ports:
      - "2368:2368"
    environment:
      url: ${GHOST_URL}
      database__client: mysql
      database__connection__host: db
      database__connection__user: ghost
      database__connection__password: ${MYSQL_PASSWORD}
      database__connection__database: ghost
    volumes:
      - ghost-data:/var/lib/ghost/content
    depends_on:
      - db

  db:
    image: mysql:8.0
    restart: always
    environment:
      MYSQL_ROOT_PASSWORD: ${MYSQL_ROOT_PASSWORD}
      MYSQL_DATABASE: ghost
      MYSQL_USER: ghost
      MYSQL_PASSWORD: ${MYSQL_PASSWORD}
    volumes:
      - ghost-db:/var/lib/mysql

volumes:
  ghost-data:
  ghost-db:

.env:

GHOST_URL=https://deine-domain.de
MYSQL_PASSWORD=k9mRvT3nXqFhWpLcYdA8sJeGbNuZiQ2w
MYSQL_ROOT_PASSWORD=m4PzXsNqWbLhTcAeKdRvYuFjGiOn7E2x

Zusammenfassung

Schritt Aktion
1 Neue Passwörter mit pwgen -s -B 32 1 generieren
2 docker compose down (ohne --volumes)
3 .env sichern, neue Passwörter eintragen
4 Nur DB starten: docker compose up -d db
5 In MySQL: ALTER USER mit neuem Passwort ausführen
6 Neue Verbindung testen
7 docker compose up -d – gesamten Stack starten
8 docker compose logs -f ghost – Erfolg bestätigen
9 .env.backup löschen
Datenverlust-Schutz: Zu keinem Zeitpunkt wird --volumes verwendet. MySQL- und Ghost-Content-Volumes bleiben während des gesamten Prozesses unberührt.