Listmonk mit Docker installieren: Hochperformanter Self-hosted Newsletter-Server
Listmonk ist die schnellste selbst gehostete Newsletter-Lösung – eine DSGVO-konforme Mailchimp-Alternative ohne Subscriber-Gebühren. Diese Anleitung zeigt die Installation mit Docker Compose in 15 Minuten.

Wer Newsletter für Kunden oder Community-Mitglieder verschickt, zahlt bei Mailchimp, Brevo oder CleverReach monatlich für jeden weiteren Kontakt. Listmonk dreht dieses Modell um: Die Go-basierte Single-Binary-App läuft als schlanker Docker-Container (~14 MB Image), speichert alle Daten in PostgreSQL und kostet – abgesehen vom eigenen Server – nichts. Das moderne Dashboard unterstützt Massen-Newsletter, Transaktionsmails, Double-Opt-In, SQL-basierte Segmentierung und eine vollständige REST-API. Für KMU und Selfhoster in der DACH-Region ist Listmonk damit eine ernstzunehmende, DSGVO-konforme Alternative zu Cloud-Diensten.
Voraussetzungen
- Docker Engine 20.10+ mit Docker Compose Plugin v2 auf einem Linux-Host, einer VM oder einem NAS mit Docker-Support. Falls noch nicht installiert, hilft Docker und Docker Compose auf Linux installieren weiter.
- Arbeitsspeicher: Mindestens 512 MB RAM; für den Produktivbetrieb mit größeren Listen empfohlen: 1 GB+.
- Festplatte: Mindestens 2 GB freier Platz für PostgreSQL-Daten, Logs und Medien-Uploads.
- SMTP-Zugangsdaten eines Mailservers oder Transaktions-Mail-Dienstes (Amazon SES, Postmark, Mailgun) – werden nach dem ersten Start in der Web-UI hinterlegt.
- Optional: Eigene Domain und Reverse-Proxy (Caddy, Traefik oder Nginx) für HTTPS. Listmonk selbst hat keinen eingebauten TLS-Terminator.
Eckdaten auf einen Blick
| Eigenschaft | Wert |
|---|---|
| Docker-Image | listmonk/listmonk:latest (aktuell v6.1.0, März 2026) |
| Nightly-Build | listmonk/listmonk:nightly |
| Architekturen | linux/amd64, linux/arm64, linux/arm/v7, linux/arm/v6 |
| Image-Größe | ~14 MB (komprimiert, amd64) |
| Web-UI / API-Port | 9000 (HTTP) |
| Datenbank | PostgreSQL 12+ (empfohlen: postgres:17-alpine) |
| Lizenz | AGPLv3 |
| Volume / Bind-Mount | Zweck |
|---|---|
| ./uploads:/listmonk/uploads:rw | Hochgeladene Medien und Bilder für Newsletter (Bind-Mount auf dem Host) |
| listmonk-data:/var/lib/postgresql/data | PostgreSQL-Datenbankdaten (benanntes Docker-Volume) |
| Umgebungsvariable | Pflicht | Beschreibung |
|---|---|---|
| LISTMONK_app__address | ja | Bind-Adresse und Port (0.0.0.0:9000) |
| LISTMONK_db__host | ja | Hostname des PostgreSQL-Containers im Compose-Netzwerk |
| LISTMONK_db__user / __password / __database | ja | Datenbankzugangsdaten – müssen mit POSTGRES_USER/PASSWORD/DB übereinstimmen |
| LISTMONK_ADMIN_USER / _PASSWORD | nur Erstinstallation | Admin-Login beim ersten --install; danach wirkungslos |
| TZ | nein | Zeitzone des Containers (Standard: Etc/UTC) |
Schritt 1: Projektordner und Verzeichnisstruktur anlegen
Leg einen dedizierten Ordner für den Listmonk-Stack an. Alle Dateien – compose.yaml, .env und das Uploads-Verzeichnis – landen hier.
mkdir -p /opt/listmonk/uploads
cd /opt/listmonkDas uploads-Unterverzeichnis ist wichtig: Listmonk speichert dort hochgeladene Bilder und Medien für Newsletter. Ohne diesen Bind-Mount gehen alle Uploads beim nächsten Container-Neustart verloren.
Verifizieren: ls -la /opt/listmonk/ sollte das Verzeichnis uploads/ auflisten. Ein fehlendes Verzeichnis führt später zu einem Fehler beim Container-Start.
Schritt 2: .env-Datei mit sicheren Passwörtern anlegen
Die .env-Datei hält alle Geheimnisse aus der compose.yaml heraus. Erstelle sie im Projektordner und ersetze die Platzhalter durch starke, zufällige Passwörter.
# /opt/listmonk/.env
# Passwörter vor dem ersten Start setzen – danach nicht mehr ändern,
# ohne auch das DB-Passwort in PostgreSQL zu aktualisieren!
# Datenbankpasswort (muss in App-Container und DB-Container identisch sein)
LISTMONK_DB_PASSWORD=HierEinStarkesPasswort123!
# Admin-Zugangsdaten (nur beim allerersten --install wirksam)
LISTMONK_ADMIN_USER=admin
LISTMONK_ADMIN_PASSWORD=HierEinAnderesSicheresPasswort456!Füge die Datei sofort zur .gitignore hinzu, falls du das Verzeichnis mit Git versionierst:
echo ".env" >> /opt/listmonk/.gitignoreAlle LISTMONK_*-Variablen unterstützen außerdem das *_FILE-Muster für Docker Secrets – z. B. LISTMONK_db__password_FILE=/run/secrets/db_pw – falls du mit Docker Swarm oder einem Secrets-Manager arbeitest.
Verifizieren: cat /opt/listmonk/.env zeigt die drei Variablen mit deinen Wunschpasswörtern. Achte darauf, dass keine Leerzeichen um das =-Zeichen stehen.
Schritt 3: compose.yaml erstellen
Die folgende compose.yaml folgt dem offiziellen Listmonk-Stack. Entscheidend ist das depends_on mit condition: service_healthy: Listmonk wartet damit auf den PostgreSQL-Healthcheck (pg_isready), bevor die App startet. Das verhindert den häufigsten Startfehler bei Docker-Compose-Stacks mit Datenbankabhängigkeit. Ebenso wichtig: Der command-Eintrag ruft --install --idempotent und --upgrade bei jedem Start auf – das Datenbankschema wird dadurch automatisch angelegt und bei Versions-Updates migriert.
# /opt/listmonk/compose.yaml
# Listmonk - Self-hosted Newsletter Manager
# Voraussetzung: .env-Datei im gleichen Verzeichnis mit LISTMONK_DB_PASSWORD,
# LISTMONK_ADMIN_USER und LISTMONK_ADMIN_PASSWORD
x-db-credentials: &db-credentials
POSTGRES_USER: &db-user listmonk
POSTGRES_PASSWORD: &db-password ${LISTMONK_DB_PASSWORD:-listmonk}
POSTGRES_DB: &db-name listmonk
services:
app:
image: listmonk/listmonk:latest
container_name: listmonk_app
restart: unless-stopped
ports:
- "9000:9000"
networks:
- listmonk
depends_on:
db:
condition: service_healthy
command: [sh, -c, "./listmonk --install --idempotent --yes --config '' && ./listmonk --upgrade --yes --config '' && ./listmonk --config ''"]
environment:
LISTMONK_app__address: 0.0.0.0:9000
LISTMONK_db__user: *db-user
LISTMONK_db__password: ${LISTMONK_DB_PASSWORD:-listmonk}
LISTMONK_db__database: *db-name
LISTMONK_db__host: db
LISTMONK_db__port: 5432
LISTMONK_db__ssl_mode: disable
LISTMONK_db__max_open: 25
LISTMONK_db__max_idle: 25
LISTMONK_db__max_lifetime: 300s
TZ: Europe/Berlin
LISTMONK_ADMIN_USER: ${LISTMONK_ADMIN_USER:-}
LISTMONK_ADMIN_PASSWORD: ${LISTMONK_ADMIN_PASSWORD:-}
volumes:
- ./uploads:/listmonk/uploads:rw
db:
image: postgres:17-alpine
container_name: listmonk_db
restart: unless-stopped
ports:
- "127.0.0.1:5432:5432"
networks:
- listmonk
environment:
<<: *db-credentials
healthcheck:
test: ["CMD-SHELL", "pg_isready -U listmonk"]
interval: 10s
timeout: 5s
retries: 6
volumes:
- type: volume
source: listmonk-data
target: /var/lib/postgresql/data
networks:
listmonk:
volumes:
listmonk-data:Zwei Details verdienen besondere Aufmerksamkeit: Der PostgreSQL-Port ist bewusst an 127.0.0.1:5432 gebunden – die Datenbank ist damit nur vom localhost aus erreichbar, nicht aus dem Internet. Und --config '' signalisiert Listmonk, ausschließlich Umgebungsvariablen zu verwenden, keine config.toml-Datei. Beide Methoden gleichzeitig zu nutzen führt zu einem Startfehler.
Verifizieren: cat /opt/listmonk/compose.yaml – die Datei muss fehlerfrei lesbar sein. Optional prüft docker compose config im Projektordner die Syntax und interpoliert die .env-Variablen, ohne den Stack zu starten.
Schritt 4: Stack starten
Starte den gesamten Stack mit einem einzigen Befehl:
cd /opt/listmonk
docker compose up -dDocker lädt beim ersten Start die Images für listmonk/listmonk:latest (~14 MB) und postgres:17-alpine herunter. Anschließend startet PostgreSQL, der Healthcheck prüft bis zu sechsmal (maximal 60 Sekunden), und erst wenn pg_isready erfolgreich ist, beginnt Listmonk mit --install --idempotent das Datenbankschema anzulegen.
Verifizieren:
# Beide Container müssen "running" oder "(healthy)" zeigen
docker compose ps
# Erwartete Ausgabe (gekürzt):
# NAME IMAGE STATUS
# listmonk_app listmonk/listmonk:latest Up X minutes
# listmonk_db postgres:17-alpine Up X minutes (healthy)
# Logs auf Fehler prüfen
docker compose logs --tail=50 appTaucht in den Logs Starting server on 0.0.0.0:9000 auf, läuft Listmonk korrekt. Ein abschließender HTTP-Check bestätigt die Erreichbarkeit:
curl -I http://localhost:9000
# HTTP/1.1 200 OK ← Erwartete AntwortSchritt 5: Erst-Einrichtung im Browser
Rufe http://<deine-server-IP>:9000 im Browser auf. Der Login-Dialog erscheint sofort – der Benutzername und das Passwort entsprechen den Werten aus LISTMONK_ADMIN_USER und LISTMONK_ADMIN_PASSWORD in der .env-Datei.
Nach dem Login führt dich Listmonk ins Dashboard. Bevor du die erste Kampagne erstellst, müssen SMTP-Zugangsdaten hinterlegt werden – das ist der einzige Setup-Schritt, der nicht per Umgebungsvariable konfiguriert wird:
- Navigiere zu Settings > SMTP.
- Trage Host, Port, TLS-Modus, Benutzername und Passwort deines Mailservers oder Transaktions-Dienstes ein.
- Klicke auf Save und danach auf Test, um eine Test-E-Mail zu versenden.
Anschließend legst du unter Lists deine erste Mailingliste an und importierst Abonnenten per CSV oder über die REST-API. Listmonk unterstützt Double-Opt-In ab sofort automatisch – die Bestätigungs-E-Mail-Vorlage ist unter Settings > Transactional anpassbar.
Verifizieren: Das Dashboard lädt ohne Fehler, unter Settings > SMTP liefert der Test-Button eine Erfolgsbestätigung, und der Health-Indikator im Dashboard zeigt alle Systemkomponenten als grün.
Schritt 6: HTTPS mit Reverse-Proxy einrichten (empfohlen)
Listmonk hat keinen eingebauten TLS-Terminator. Für den Produktivbetrieb – insbesondere wenn die UI öffentlich erreichbar sein soll – schaltest du einen Reverse-Proxy vor, der TLS übernimmt und Anfragen an Port 9000 weiterleitet. Bewährt haben sich Caddy als Reverse-Proxy mit automatischem HTTPS (besonders einfach durch automatisches Let's-Encrypt-Handling) oder Traefik als Docker-Reverse-Proxy für komplexere Multi-Container-Setups.
In der Praxis reicht für einen einzelnen Listmonk-Server ein minimales Caddyfile:
newsletter.deine-domain.de {
reverse_proxy localhost:9000
}Caddy bezieht und erneuert das Let's-Encrypt-Zertifikat vollautomatisch. Nach dem Umstieg auf HTTPS empfiehlt es sich, den Port-Eintrag in der compose.yaml von "9000:9000" auf "127.0.0.1:9000:9000" zu ändern, damit Listmonk nur noch über den Reverse-Proxy erreichbar ist.
Verifizieren: curl -I https://newsletter.deine-domain.de liefert HTTP/2 200. Das Browser-Schloss-Symbol zeigt ein gültiges Zertifikat. docker compose ps zeigt weiterhin beide Container als „Up".
Schritt 7: Updates und Backup
Update auf eine neue Listmonk-Version
Das Aktualisieren des Stacks ist besonders einfach, weil --upgrade --yes beim Start automatisch Datenbankmigrationen durchführt:
cd /opt/listmonk
docker compose pull
docker compose up -dFür Produktivumgebungen empfiehlt sich, in der compose.yaml einen konkreten Tag zu pinnen (z. B. listmonk/listmonk:v6.1.0) statt :latest zu verwenden. So vermeidest du unerwartete Breaking Changes. Automatische Container-Updates lassen sich mit Tools wie in Watchtower: Container automatisch aktualisieren beschrieben einrichten.
Backup
Zwei Komponenten müssen gesichert werden:
- PostgreSQL-Datenbank – per
pg_dumpaus dem laufenden Container:
docker exec listmonk_db pg_dump -U listmonk listmonk > /backup/listmonk_$(date +%Y%m%d).sql- Uploads-Verzeichnis – alle hochgeladenen Medien liegen auf dem Host unter
/opt/listmonk/uploads/und lassen sich mitrsyncoder einem Backup-Tool sichern.
Für eine vollständige, rotierte Backup-Strategie inkl. Cloud-Offsite-Kopie bietet sich PostgreSQL Backup automatisieren mit cron und rclone an.
Verifizieren: Die erzeugte .sql-Datei ist größer als 0 Byte (ls -lh /backup/listmonk_*.sql). Ein Restore-Test auf einem separaten System bestätigt die Integrität.
Troubleshooting / Typische Fehler
- App startet vor der DB (restart-loop): Altes
depends_onohnecondition: service_healthy. Die hier gezeigtecompose.yamlbehebt das Problem. Prüfe mitdocker compose ps, oblistmonk_dbden Status(healthy)zeigt. - „password authentication failed for user listmonk":
LISTMONK_DB_PASSWORDin der.envund das bereits gesetzte PostgreSQL-Passwort stimmen nicht überein. Beim ersten Start müssen beide Seiten gleich sein. Lösung: Stack mitdocker compose down -vkomplett entfernen (löscht das Volume!) und neu starten. - Admin-Login funktioniert nicht nach Erstinstallation:
LISTMONK_ADMIN_USERundLISTMONK_ADMIN_PASSWORDwurden nicht vor dem allerersten--install-Lauf gesetzt. Nach dem ersten Install sind diese Variablen wirkungslos. Passwort zurücksetzen per:docker compose run --rm app ./listmonk --reset-admin --yes --config '' - Port 9000 nicht erreichbar von außen:
LISTMONK_app__addressist auf127.0.0.1:9000statt0.0.0.0:9000gesetzt. Der Container lauscht dann nur intern. Wert in dercompose.yamlkorrigieren unddocker compose up -derneut ausführen. - „cannot use config file and env variables together": Eine
config.tomlwird als Volume eingebunden und gleichzeitig werdenLISTMONK_*-Umgebungsvariablen gesetzt. Nur eine Methode verwenden.--config ''imcommanddeaktiviert die Datei-Methode. - Uploads verschwinden nach Neustart: Das Bind-Mount
./uploads:/listmonk/uploads:rwfehlt in dercompose.yaml. Ohne dieses Volume speichert Listmonk Medien im Container-Dateisystem, das bei jedem Neustart zurückgesetzt wird. - Upgrade schlägt fehl nach Versionswechsel: Bei Sprüngen wie v5 → v6 muss das Datenbankschema migriert werden. Die offizielle
compose.yamlerledigt das automatisch über--upgrade --yesimcommand. Prüfe mitdocker compose logs app, ob die Migration erfolgreich durchgelaufen ist.
Häufige Fragen
Welche SMTP-Konfiguration brauche ich?
SMTP wird nicht per Umgebungsvariable konfiguriert, sondern nach dem ersten Login über Settings > SMTP in der Web-UI. Dort trägst du Hostname, Port, TLS-Modus sowie Benutzername und Passwort deines Mailservers ein. Für größere Versandvolumen empfehlen sich Transaktions-Mail-Dienste wie Amazon SES, Postmark oder Mailgun, die von Listmonk direkt als SMTP-Relay genutzt werden können. Wer DKIM und SPF für maximale Zustellbarkeit konfigurieren möchte, findet dazu die Anleitung OpenDKIM mit Postfix einrichten hilfreich.
Kann ich eine externe PostgreSQL-Datenbank nutzen?
Ja. Entferne einfach den db-Service aus der compose.yaml, setze LISTMONK_db__host auf deinen externen PostgreSQL-Host und passe Benutzer, Passwort und Datenbankname entsprechend an. Voraussetzung: PostgreSQL-Mindestversion 12, eine leere Datenbank und ein Benutzer mit allen Rechten auf diese Datenbank.
Wie skaliere ich Listmonk für große Versandvolumen?
Listmonk ist horizontal skalierbar. Die Parameter batch_size (wie viele Mails pro Batch) und concurrency (wie viele parallele Worker) lassen sich in den App-Settings konfigurieren. Für sehr hohe Volumen empfiehlt die offizielle Dokumentation einen dedizierten SMTP-Relay oder einen Transaktions-Mail-Dienst mit hohem Durchsatz.
Unterstützt Listmonk HTTPS direkt?
Nein. Listmonk hat keinen eingebauten TLS-Terminator. Für HTTPS muss ein Reverse-Proxy vorgeschaltet werden, der TLS terminiert und Anfragen an Port 9000 weiterleitet.
Wie schütze ich mich vor unbeabsichtigten Updates?
Ersetze :latest in der compose.yaml durch einen konkreten Tag wie listmonk/listmonk:v6.1.0. So ziehst du neue Versionen nur, wenn du aktiv docker compose pull mit dem gewünschten Tag aufrufst. Den aktuellen Stable-Tag findest du auf Docker Hub.
Wie funktioniert Double-Opt-In?
Listmonk unterstützt Double-Opt-In nativ. Bei der Listenkonfiguration kannst du für jede Liste den Opt-In-Typ auf „Double" setzen. Die Bestätigungs-E-Mail wird über die unter Settings > Transactional hinterlegte Vorlage verschickt. Der Subscriber ist erst nach dem Bestätigungs-Klick aktiv und erscheint im Dashboard mit dem Status „confirmed".
Fazit
Listmonk ist eine der ausgereiftesten Self-Hosted-Lösungen im Newsletter-Bereich. Die Kombination aus einer einzigen Go-Binary, einem schlanken Docker-Image und der automatischen Datenbankmigration beim Start macht den Betrieb und das Aktualisieren deutlich einfacher als bei vielen anderen Open-Source-Tools. Wer regelmäßig Newsletter verschickt und keine Lust mehr hat, pro Subscriber zu zahlen, findet hier eine leistungsfähige, datenschutzkonforme Alternative – vorausgesetzt, man bringt den Betrieb eines eigenen Servers und einer SMTP-Infrastruktur mit. Der DSGVO-Vorteil liegt auf der Hand: Alle Abonnentendaten liegen auf dem eigenen Server, kein Drittanbieter hat Zugriff.
Weiterführende Anleitungen und Quellen
- Docker und Docker Compose auf Linux installieren (Ubuntu/Debian): die Self-Hosting-Grundlage
- Caddy als Reverse-Proxy einrichten: Anfänger-Anleitung mit automatischem HTTPS
- Mailcow komplett aufsetzen – eigener Mailserver mit Docker
- OpenDKIM mit Postfix einrichten: DKIM-Signatur korrekt konfigurieren
- MySQL & PostgreSQL Backup automatisieren mit cron und rclone
- Watchtower mit Docker: Container automatisch aktualisieren
Offizielle Quellen: Listmonk Installationsdokumentation | Listmonk auf GitHub (knadh/listmonk) | Listmonk auf Docker Hub