Etherpad mit Docker Compose und PostgreSQL selbst hosten
Diese Anleitung zeigt, wie Sie Etherpad 3.3.8 mit Docker Compose und PostgreSQL betreiben, den Admin-Bereich und die HTTP-API nutzen, den API-Schlüssel über Updates hinweg behalten und Pads per pg_dump sichern sowie nachweislich wiederherstellen.
Mit KI erstellt – redaktionelle Prüfung ausstehend
WerbelinksMit * markierte Links sind Werbelinks: Bei einem Kauf erhalten wir eine Provision, der Preis bleibt gleich. Als Amazon-Partner verdiene ich an qualifizierten Verkäufen. Mehr dazu

Etherpad ist ein Editor, in dem mehrere Personen gleichzeitig am selben Text schreiben. Jede Änderung erscheint sofort bei allen anderen, jede Person bekommt eine eigene Farbe, und über den Zeitregler lässt sich jeder Bearbeitungsstand zurückverfolgen. Für Protokolle und Checklisten im Team ist das oft schneller als ein Office-Dokument. Diese Anleitung zeigt den Betrieb von Etherpad 3.3.8 mit Docker Compose und PostgreSQL, wie ihn das Projekt selbst vorsieht, ergänzt um drei Korrekturen, ohne die der Betrieb im Alltag hakt: einen Datenbank-Healthcheck, einen dauerhaften API-Schlüssel und eigene Secrets für den eingebauten OpenID-Connect-Anbieter.
Voraussetzungen
- Ein Linux-Server oder eine VM mit Docker Engine und Docker Compose v2, x86_64 oder ARM64.
- 2 CPU-Kerne und 2 GB RAM reichen für ein kleines Team. Im Test belegte Etherpad im Leerlauf rund 210 MB RAM, PostgreSQL rund 22 MB.
- Etwa 3 GB Speicherplatz: Das Etherpad-Image ist 1,14 GB groß, PostgreSQL 15 Alpine rund 420 MB, dazu kommen Daten und Backups.
- Für den Zugriff von außen ein Reverse Proxy mit TLS, zum Beispiel Caddy oder Nginx, und ein DNS-Name wie
pad.example.de. - Ein Speicherziel außerhalb des Servers für die Sicherungen.
Schritt 1: Projektordner und Geheimnisse anlegen
Etherpad liest seine Konfiguration aus Umgebungsvariablen, die das Image in eine vorbereitete settings.json einsetzt. Alle Geheimnisse gehören deshalb in eine .env neben der Compose-Datei. Legen Sie Ordner, Passwörter und den API-Schlüssel an:
mkdir -p /opt/etherpad && cd /opt/etherpad
cat > .env <<EOF
ETHERPAD_VERSION=3.3.8
ADMIN_PASSWORD=$(openssl rand -hex 16)
DB_PASS=$(openssl rand -hex 16)
ADMIN_SECRET=$(openssl rand -hex 24)
USER_SECRET=$(openssl rand -hex 24)
AUTHENTICATION_METHOD=apikey
EOF
openssl rand -hex 24 | tr -d '\n' > APIKEY.txt
chmod 600 .env && chmod 644 APIKEY.txt
Zur Rolle der Werte: ADMIN_PASSWORD ist das Passwort des Benutzers admin für /admin. ADMIN_SECRET und USER_SECRET ersetzen die Platzhalter-Secrets admin und user des eingebauten OpenID-Connect-Anbieters. Ohne sie schreibt Etherpad bei jedem Start SSO client(s) using a default/placeholder client_secret: admin_client, user_client als Fehler ins Log. AUTHENTICATION_METHOD=apikey schaltet die HTTP-API auf den klassischen statischen Schlüssel um, was für Skripte und Integrationen deutlich einfacher ist als OAuth. Seit Version 3 ist sso der Standard.
Die Datei APIKEY.txt muss vor dem ersten Start existieren. Fehlt sie, legt Docker beim Bind-Mount an ihrer Stelle ein leeres Verzeichnis an, das Sie anschließend erst wieder entfernen müssen.
Verifizieren: ls -l /opt/etherpad zeigt .env mit Rechten -rw------- und eine APIKEY.txt mit 48 Zeichen (wc -c APIKEY.txt).
Schritt 2: Compose-Datei schreiben
Die Grundlage ist die docker-compose.yml aus dem Etherpad-Repository. Gegenüber dem Original ist das Image auf eine Version festgeschrieben statt latest, PostgreSQL hat einen Healthcheck, und APIKEY.txt wird von außen eingebunden.
services:
app:
image: etherpad/etherpad:${ETHERPAD_VERSION:-3.3.8}
user: "5001:0"
tty: true
stdin_open: true
restart: unless-stopped
depends_on:
postgres:
condition: service_healthy
ports:
- "9001:9001"
volumes:
- plugins:/opt/etherpad-lite/src/plugin_packages
- etherpad-var:/opt/etherpad-lite/var
- ./APIKEY.txt:/opt/etherpad-lite/APIKEY.txt:ro
environment:
NODE_ENV: production
TITLE: "Team-Pad"
ADMIN_PASSWORD: "${ADMIN_PASSWORD:?ADMIN_PASSWORD in .env setzen}"
DB_TYPE: postgres
DB_HOST: postgres
DB_PORT: 5432
DB_NAME: etherpad
DB_USER: etherpad
DB_PASS: "${DB_PASS:?DB_PASS in .env setzen}"
DEFAULT_PAD_TEXT: " "
DISABLE_IP_LOGGING: "true"
TRUST_PROXY: "${TRUST_PROXY:-false}"
AUTHENTICATION_METHOD: "${AUTHENTICATION_METHOD:-sso}"
SOFFICE: "null"
ADMIN_SECRET: "${ADMIN_SECRET:?ADMIN_SECRET in .env setzen}"
USER_SECRET: "${USER_SECRET:?USER_SECRET in .env setzen}"
postgres:
image: postgres:15-alpine
restart: unless-stopped
environment:
POSTGRES_DB: etherpad
POSTGRES_USER: etherpad
POSTGRES_PASSWORD: "${DB_PASS:?DB_PASS in .env setzen}"
PGDATA: /var/lib/postgresql/data/pgdata
healthcheck:
test: ["CMD-SHELL", "pg_isready -U etherpad -d etherpad"]
interval: 5s
timeout: 5s
retries: 10
volumes:
- postgres_data:/var/lib/postgresql/data
volumes:
postgres_data:
plugins:
etherpad-var:
Warum der Healthcheck? Mit dem schlichten depends_on der Vorlage startet Etherpad, sobald der Datenbank-Container läuft, nicht erst, wenn PostgreSQL Verbindungen annimmt. Beim ersten Start sahen wir deshalb zweimal Error: connect ECONNREFUSED ...:5432 und einen Neustart des Containers, bevor Etherpad lief. Mit condition: service_healthy verschwindet das.
Warum APIKEY.txt als Datei von außen? Etherpad erzeugt den Schlüssel sonst selbst unter /opt/etherpad-lite/APIKEY.txt, also in der beschreibbaren Schicht des Containers und nicht im Volume. Beim Update auf eine neue Image-Version wurde der Container neu erstellt, der Schlüssel war ein anderer und alle Skripte mit dem alten Schlüssel bekamen HTTP 401. Mit dem Bind-Mount bleibt er über jedes Update gleich.
| Eckdaten | Wert |
|---|---|
| Image | etherpad/etherpad:3.3.8 (Spiegel: ghcr.io/ether/etherpad) |
| Datenbank | postgres:15-alpine, eine Tabelle store als Schlüssel-Wert-Speicher |
| Port | 9001 (HTTP und WebSocket) |
| Volumes | postgres_data, etherpad-var, plugins, Datei APIKEY.txt |
| Wichtige Variablen | ADMIN_PASSWORD, DB_*, AUTHENTICATION_METHOD, ADMIN_SECRET, USER_SECRET, TRUST_PROXY |
| Lizenz | Apache-2.0 |
Verifizieren: docker compose config --quiet endet ohne Ausgabe. Fehlt eine Pflichtvariable, bricht Compose mit einer klaren Meldung ab, etwa required variable ADMIN_PASSWORD is missing a value: ADMIN_PASSWORD in .env setzen.
Schritt 3: Etherpad starten
docker compose up -d
docker compose ps
curl -s localhost:9001/health
Im Test war die Instanz etwa 15 Sekunden nach dem Start bereit. Der Endpunkt /health antwortet mit der laufenden Version, das ist auch der schnellste Weg, nach einem Update die Version zu prüfen.

Verifizieren: docker compose ps zeigt beide Container mit (healthy), curl -s localhost:9001/health liefert {"status":"pass","releaseId":"3.3.8"}, und docker compose logs app | grep -E "Api key file read|Etherpad is running" zeigt Api key file read from: "/opt/etherpad-lite/APIKEY.txt" sowie Etherpad is running.
Schritt 4: Hinter den Reverse Proxy stellen
Port 9001 spricht nur HTTP. Für den Betrieb außerhalb des Testnetzes gehört ein TLS-Proxy davor, der auch WebSockets durchreicht, denn darüber laufen die Live-Änderungen. Bei Caddy genügt ein Block mit reverse_proxy 127.0.0.1:9001. Binden Sie den Port dann nur lokal, also "127.0.0.1:9001:9001", und setzen Sie TRUST_PROXY=true in der .env, damit Etherpad die X-Forwarded-*-Header des Proxys auswertet. Ohne Proxy sollte die Variable auf false bleiben, sonst könnte jeder Client diese Header fälschen.
Unsere Testinstanz lief über einen vorgeschalteten HTTPS-Proxy; Editor und Admin-Bereich funktionierten darüber ohne weitere Einstellungen.
Verifizieren: curl -sI https://pad.example.de/ liefert HTTP/2 200, und beim Tippen in einem Pad erscheinen die Änderungen ohne Neuladen in einem zweiten Browserfenster.
Schritt 5: Pads anlegen und den Admin-Bereich nutzen
Ein Pad entsteht, sobald jemand https://pad.example.de/p/NAME aufruft oder auf der Startseite einen Namen eingibt. Jede Person, die die Adresse kennt, kann mitschreiben. Für interne Notizen ist das bequem, für vertrauliche Inhalte nicht: Wählen Sie dann schwer erratbare Padnamen oder aktivieren Sie mit REQUIRE_AUTHENTICATION=true eine Anmeldung für alle Pads.

Der Admin-Bereich unter /admin verlangt den Benutzernamen admin und das ADMIN_PASSWORD. Ein falsches Passwort beantwortet der Server mit HTTP 401. Unter „Pads verwalten“ sehen Sie alle Pads mit Bearbeitungen und aktiven Nutzern, können leere Pads finden und Pads löschen. „Etherpad updates“ zeigt die laufende und die neueste Version samt Installationsart docker.

Für Skripte gibt es die HTTP-API, aktuell in Version 1.3.1. Der Schlüssel aus APIKEY.txt wird als Parameter apikey übergeben:
K=$(cat /opt/etherpad/APIKEY.txt)
curl -s "localhost:9001/api/1.3.1/createPad?apikey=$K&padID=protokoll&text=Erster%20Eintrag"
curl -s "localhost:9001/api/1.3.1/getText?apikey=$K&padID=protokoll"
curl -s "localhost:9001/api/1.3.1/listAllPads?apikey=$K"
Verifizieren: getText liefert {"code":0,"message":"ok","data":{"text":"Erster Eintrag\n"}}. Derselbe Aufruf mit falschem oder ohne Schlüssel endet mit HTTP 401 und {"code":4,"message":"no or wrong API Key","data":null}.
Schritt 6: Backup anlegen
Alle Pads, Revisionen, Autoren und Chats liegen in PostgreSQL, in einer einzigen Tabelle store. Das Volume etherpad-var enthält nur Laufzeitdaten wie den Plugin-Stand. Sichern Sie trotzdem beides, dazu .env und APIKEY.txt, sonst passen nach einem Neuaufbau Passwörter und Schlüssel nicht mehr.
cd /opt/etherpad
mkdir -p backup
docker compose exec -T postgres pg_dump -U etherpad -d etherpad -Fc \
> backup/etherpad-$(date +%F).dump
docker run --rm -v etherpad_etherpad-var:/data:ro -v "$PWD/backup":/backup \
alpine tar czf /backup/etherpad-var-$(date +%F).tgz -C /data .
cp .env APIKEY.txt compose.yaml backup/
Der Volume-Name setzt sich aus Projektordner und Volume zusammen, bei /opt/etherpad also etherpad_etherpad-var. docker volume ls zeigt den genauen Namen. Den Dump im Custom-Format (-Fc) können Sie im laufenden Betrieb ziehen; er ist komprimiert und lässt sich gezielt mit pg_restore zurückspielen. Kopieren Sie den Ordner danach auf ein anderes System.
Verifizieren: ls -l backup/ zeigt Dump und Archiv mit einer Größe über 0 Byte, und docker compose exec -T postgres pg_restore -l < backup/etherpad-$(date +%F).dump | grep store listet die Tabelle store.
Schritt 7: Wiederherstellen
Ein Backup ist erst etwas wert, wenn die Rücksicherung klappt. Im Test haben wir ein Pad mit einem Markertext angelegt, gesichert, das Pad per API gelöscht und danach die Datenbank komplett ersetzt. Gehen Sie im Ernstfall genauso vor:
docker compose stop app
docker compose exec -T postgres dropdb -U etherpad etherpad
docker compose exec -T postgres createdb -U etherpad -O etherpad etherpad
docker compose exec -T postgres pg_restore -U etherpad -d etherpad \
--exit-on-error < backup/etherpad-2026-10-18.dump
docker compose start app
Stoppen Sie Etherpad vorher, sonst blockieren offene Verbindungen das dropdb, und die Anwendung puffert Daten im Arbeitsspeicher, die nicht zum zurückgespielten Stand passen. Nach dem Löschen lieferte getText für das Testpad padID does not exist, die neu angelegte Datenbank enthielt null Tabellen. Nach pg_restore und dem Start war der Markertext unverändert wieder da.
Verifizieren: pg_restore endet mit Exit-Code 0, curl -s localhost:9001/health meldet pass, und getText beziehungsweise der Admin-Bereich zeigt Ihre Pads wieder an.
Schritt 8: Updates einspielen
Etherpad veröffentlicht häufig Patch-Versionen; die Tags 3.3.8, 3.3, 3 und latest zeigten am 18.10.2026 auf denselben Stand. Bleiben Sie beim exakten Versions-Tag und ändern Sie ihn bewusst. Der Ablauf, den wir von 3.3.7 auf 3.3.8 durchgespielt haben:
cd /opt/etherpad
# vorher Schritt 6 ausführen
sed -i 's/^ETHERPAD_VERSION=.*/ETHERPAD_VERSION=3.3.8/' .env
docker compose pull app
docker compose up -d
curl -s localhost:9001/health
Die bestehenden Pads blieben erhalten, das Log meldete check installed plugins for migration und kurz danach Etherpad is running. Lesen Sie vor größeren Sprüngen die Release Notes auf GitHub. Beachten Sie außerdem: Änderungen an settings.json über den Admin-Bereich landen ohne eigenen Mount in der Container-Schicht und sind nach einem Update weg. Steuern Sie die Konfiguration deshalb über Variablen in der Compose-Datei.
Verifizieren: /health liefert die neue releaseId, docker compose ps zeigt das neue Image, und ein API-Aufruf mit dem bisherigen Schlüssel aus APIKEY.txt funktioniert weiterhin.
Troubleshooting
Error: connect ECONNREFUSED ...:5432beim Start: Etherpad war schneller als PostgreSQL. Der Container startet zwar neu und kommt meist hoch, sauber ist der Healthcheck mitcondition: service_healthyaus Schritt 2.password authentication failed for user "etherpad":DB_PASSpasst nicht mehr zum Passwort, mit dem das Volume angelegt wurde. PostgreSQL übernimmtPOSTGRES_PASSWORDnur beim allerersten Start; ein späteres Ändern in der.envwirkt nicht auf die Datenbank. Altes Passwort zurücksetzen oder in der Datenbank perALTER USERändern.- API meldet nach einem Update
no or wrong API Key: Der Schlüssel lag im Container und wurde neu erzeugt. Den Bind-Mount fürAPIKEY.txtergänzen und Skripte auf den neuen Schlüssel umstellen. Bind for 0.0.0.0:9001 failed: port is already allocated: Ein anderer Dienst nutzt den Port.ss -ltnp | grep 9001zeigt welcher; den linken Teil der Portzuordnung ändern.
Häufige Fragen
Brauche ich PostgreSQL oder reicht die eingebaute Datenbank?
Ohne DB_TYPE nutzt Etherpad DirtyDB, eine einfache Datei, die das Projekt selbst nur für Tests empfiehlt. Für den Dauerbetrieb ist PostgreSQL die bessere Wahl, auch weil Sie dann mit den üblichen Werkzeugen sichern können.
Wie schütze ich Pads vor Fremden?
Standardmäßig ist jedes Pad für jeden lesbar, der die Adresse kennt. Abhilfe schaffen ein Zugriff nur aus dem internen Netz oder VPN, REQUIRE_AUTHENTICATION=true mit Benutzern aus der settings.json oder ein Authentifizierungs-Plugin wie LDAP oder OpenID Connect.
Kann ich Plugins installieren?
Ja, die Pluginverwaltung im Admin-Bereich listet über 160 Plugins aus dem npm-Katalog. Installierte Plugins landen im Volume plugins. Jedes Plugin muss zur Etherpad-Version passen.
Lässt sich Etherpad ganz ohne Verbindungen ins Internet betreiben?
Ja. Etherpad prüft regelmäßig auf neue Versionen und lädt den Plugin-Katalog. Mit UPDATES_TIER=off, PRIVACY_UPDATE_CHECK=false und PRIVACY_PLUGIN_CATALOG=false entfallen diese Abrufe.
Fazit
Etherpad ist schnell aufgesetzt und braucht wenig Ressourcen. Die offizielle Compose-Datei ist ein guter Ausgangspunkt, im Betrieb lohnen sich aber drei Ergänzungen: Healthcheck für PostgreSQL, ein außerhalb des Containers liegender API-Schlüssel und eigene OIDC-Secrets. Planen Sie vor dem Einsatz für vertrauliche Inhalte die Zugriffsfrage, denn ohne Anmeldung ist jedes Pad nur so geheim wie sein Name.
Weiterführende Anleitungen und Quellen
- HedgeDoc mit Docker Compose und PostgreSQL selbst hosten
- PostgreSQL mit pg_dump und pg_restore sichern und migrieren
- Caddy als Reverse Proxy mit automatischem HTTPS
- Docker Compose Grundlagen
- Etherpad auf GitHub
- Release Etherpad 3.3.8
- Etherpad-Dokumentation: Docker und Umgebungsvariablen
- Offizielle Compose-Datei
- Etherpad HTTP-API


