Listmonk mit Docker installieren: Newsletter-Server selbst hosten
Listmonk 6.2.0 mit Docker Compose und PostgreSQL selbst hosten: SMTP, Root-URL, Double-Opt-In, HTTPS per Reverse Proxy sowie getestete Sicherung und Wiederherstellung.
Geprüft am 14.10.2026 · für listmonk 6.2.0
WerbelinksMit * markierte Links sind Werbelinks: Bei einem Kauf erhalten wir eine Provision, der Preis bleibt gleich. Mehr dazu

Listmonk ist ein selbst gehosteter Newsletter- und Mailinglisten-Manager. Die Anwendung verwaltet Listen, Abonnenten, Kampagnen, Double-Opt-In und Vorlagen und speichert alles in PostgreSQL. Den eigentlichen Versand übernimmt ein SMTP-Server, den Sie hinterlegen: ein eigener Mailserver oder ein Versanddienst. Die Abonnentendaten bleiben damit auf Ihrem Server. Diese Anleitung richtet Listmonk 6.2.0 mit Docker Compose ein, hinterlegt SMTP, prüft Double-Opt-In und Versand und zeigt Sicherung und Wiederherstellung. Alle Schritte wurden in einer Testumgebung mit einem SMTP-Testempfänger nachgestellt.
Voraussetzungen
- Linux-Server oder VPS mit Docker Engine und Compose-Plugin, siehe Docker und Docker Compose auf Linux installieren. Architektur x86_64 oder ARM.
- 1 CPU-Kern und 1 GB RAM. Im Test belegten Listmonk rund 30 MB und PostgreSQL rund 85 MB im Leerlauf.
- 2 GB freier Speicher für Images, Datenbank und Medien.
- SMTP-Zugang eines Mailservers oder Versanddienstes für die Absenderdomain, mit SPF, DKIM und DMARC. Ohne diese Einträge landen Newsletter häufig im Spam.
- Subdomain wie
newsletter.example.comund ein Reverse Proxy für HTTPS. Die Adresse steckt später in jedem Abmelde- und Bestätigungslink.
| Eckdaten | Wert |
|---|---|
| Images | listmonk/listmonk:v6.2.0, postgres:17-alpine |
| Port | 9000 (HTTP, Weboberfläche und API) |
| Daten | Volume für /var/lib/postgresql/data, Ordner ./uploads für Medien |
| Konfiguration | Umgebungsvariablen LISTMONK_… für Datenbank und Erstkonto, alles andere in der Weboberfläche |
| Lizenz | AGPLv3 |
Schritt 1: Projektordner und .env anlegen
sudo mkdir -p /opt/listmonk/uploads
cd /opt/listmonk
openssl rand -base64 24
Erzeugen Sie mit openssl zwei zufällige Passwörter und tragen Sie sie in die .env ein. Das Datenbankpasswort übernimmt PostgreSQL nur beim allerersten Start in das leere Volume; die Admin-Variablen wirken ebenfalls nur bei der Erstinstallation.
# /opt/listmonk/.env
LISTMONK_DB_PASSWORD=zufaelliges-db-passwort
LISTMONK_ADMIN_USER=admin
LISTMONK_ADMIN_PASSWORD=zufaelliges-admin-passwort
sudo chmod 600 /opt/listmonk/.env
Bleiben die Admin-Variablen leer, legen Sie das erste Konto laut offizieller Compose-Datei beim ersten Aufruf der Weboberfläche an. Für Docker-Secrets unterstützt das Image zu jeder LISTMONK_…-Variable eine Variante mit Endung _FILE.
Verifizieren: sudo cat /opt/listmonk/.env zeigt drei Zeilen NAME=Wert ohne Leerzeichen um das Gleichheitszeichen, ls -la /opt/listmonk zeigt den Ordner uploads.
Schritt 2: compose.yaml erstellen
Die Datei folgt der offiziellen docker-compose.yml aus dem Listmonk-Repository mit drei Änderungen: fester Tag statt latest, Port nur an 127.0.0.1, Passwort aus der .env statt des Beispielwerts listmonk. Zusätzlich wartet die Anwendung, bis PostgreSQL gesund ist, und der Datenbankport wird gar nicht veröffentlicht, weil Listmonk die Datenbank über das interne Compose-Netz erreicht.
x-db-credentials: &db-credentials
POSTGRES_USER: &db-user listmonk
POSTGRES_PASSWORD: &db-password ${LISTMONK_DB_PASSWORD:?LISTMONK_DB_PASSWORD fehlt}
POSTGRES_DB: &db-name listmonk
services:
app:
image: listmonk/listmonk:v6.2.0
container_name: listmonk_app
restart: unless-stopped
ports:
- "127.0.0.1:9000:9000"
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: *db-password
LISTMONK_db__database: *db-name
LISTMONK_db__host: db
LISTMONK_db__port: 5432
LISTMONK_db__ssl_mode: disable
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
environment:
<<: *db-credentials
healthcheck:
test: ["CMD-SHELL", "pg_isready -U listmonk"]
interval: 10s
timeout: 5s
retries: 6
volumes:
- listmonk-data:/var/lib/postgresql/data
volumes:
listmonk-data:
Der command stammt unverändert aus der offiziellen Datei: --install --idempotent legt das Schema nur in einer leeren Datenbank an, --upgrade migriert es nach einem Versionswechsel, --config '' sorgt dafür, dass Listmonk nur die Umgebungsvariablen liest. Bleiben Sie bei postgres:17-alpine: Ab PostgreSQL 18 erwartet das offizielle Image das Volume unter /var/lib/postgresql, und ein Wechsel der Hauptversion braucht ohnehin einen Dump und eine Wiederherstellung.
Verifizieren: docker compose config -q endet ohne Ausgabe. Fehlt LISTMONK_DB_PASSWORD in der .env, bricht der Befehl mit der Meldung LISTMONK_DB_PASSWORD fehlt ab.
Schritt 3: Stack starten
cd /opt/listmonk
docker compose up -d
docker compose ps
docker compose logs app
Im Test war PostgreSQL nach gut 20 Sekunden gesund, Listmonk startete direkt danach. Bei der Erstinstallation enthält das Log diese Zeilen:
** first time (idempotent) installation **
install.go:93: creating superadmin user 'admin'
install.go:116: setup complete
upgrade.go:74: no upgrades to run. Database is up to date.
⇨ http server started on [::]:9000
Für einen Erreichbarkeitstest nutzen Sie eine normale GET-Anfrage. curl -I sendet eine HEAD-Anfrage, die Listmonk 6.2.0 im Test mit 404 beantwortete, obwohl die Seite funktionierte:
curl -s -o /dev/null -w '%{http_code}\n' http://127.0.0.1:9000/admin/login
ss -tlnp | grep -E ':(9000|5432) '
Verifizieren: docker compose ps zeigt listmonk_db als (healthy) und listmonk_app als Up mit 127.0.0.1:9000->9000/tcp. Der curl-Aufruf liefert 200, ss zeigt nur 127.0.0.1:9000 und keinen Port 5432.
Schritt 4: Anmelden und Grundeinstellungen setzen
Ohne Reverse Proxy öffnen Sie einen SSH-Tunnel und rufen http://localhost:9000/admin auf. Die Oberfläche ist englisch.
ssh -L 9000:127.0.0.1:9000 benutzer@server
Melden Sie sich mit den Werten aus der .env an und öffnen Sie Settings. Jede Speicherung lädt Listmonk intern neu, die Oberfläche ist dann einige Sekunden nicht erreichbar.
- General: Root URL auf die öffentliche Adresse setzen, etwa
https://newsletter.example.comohne Schrägstrich am Ende. Diese Adresse steht in Bestätigungs- und Abmeldelinks. Default from email auf Ihren Absender setzen, etwaNewsletter Beispiel GmbH <newsletter@example.com>. - Media uploads: Upload-Pfad auf
/listmonk/uploadsändern. Voreingestellt istuploadsinnerhalb des Containers; ohne die Änderung landen Bilder nicht im eingebundenen Ordner und gehen beim Neuerstellen des Containers verloren.
Verifizieren: Nach dem Speichern zeigt Settings > General die neue Root-URL. Ein unter Campaigns > Media hochgeladenes Bild liegt auf dem Host in /opt/listmonk/uploads; im Test lagen dort das Bild und eine Vorschaudatei thumb_…, und die Adresse /uploads/DATEINAME lieferte Status 200.
Schritt 5: SMTP hinterlegen und Versand prüfen
Unter Settings > SMTP steht nach der Installation ein Beispieleintrag mit smtp.yoursite.com. Ersetzen Sie ihn durch Ihren Mailserver: Host, Port (meist 587 mit STARTTLS oder 465 mit TLS), Auth protocol (meist LOGIN oder PLAIN), Benutzername und Passwort. Die Links über den Feldern füllen typische Werte für bekannte Versanddienste vor.

Über die Schaltfläche Test am SMTP-Eintrag schickt Listmonk eine Testmail an eine Adresse Ihrer Wahl. Im Test kam sie mit dem Betreff Test connection und dem eingestellten Absender an. Für einen Probelauf ohne echte Zustellung eignet sich ein lokaler SMTP-Fänger wie Mailpit als zusätzlicher Dienst in der compose.yaml; er nimmt alle Mails an und zeigt sie in einer eigenen Weboberfläche. Für den Betrieb gehört dort der echte Mailserver hin.
Prüfen Sie danach den kompletten Ablauf mit Double-Opt-In:
- Lists > New: Liste anlegen, Typ
PublicoderPrivate, Opt-inDouble. - Subscribers > New: eine eigene Testadresse der Liste zuordnen. Listmonk verschickt sofort die Mail
Confirm subscription. - Den Bestätigungslink öffnen und bestätigen.
- Campaigns > New: Kampagne an die Liste anlegen und starten.

Verifizieren: Die Testmail kommt an. Nach dem Klick auf den Bestätigungslink steht der Abonnent in der Liste auf confirmed, die Kampagne wechselt auf Finished mit Sent 1 / 1, und die Mail enthält unten die Links Unsubscribe und View in browser mit Ihrer Root-URL.
Schritt 6: HTTPS über einen Reverse Proxy
Listmonk spricht nur HTTP. Ein Reverse Proxy auf demselben Host übernimmt das Zertifikat und leitet an 127.0.0.1:9000 weiter. Mit Caddy genügt ein Eintrag, das Zertifikat holt Caddy selbst, siehe Caddy als Reverse Proxy einrichten:
newsletter.example.com {
reverse_proxy 127.0.0.1:9000
}
Für mehrere Container auf einem Host eignet sich Traefik als Docker-Reverse-Proxy. Die öffentlichen Seiten für Abmeldung, Bestätigung und optional das Anmeldeformular müssen aus dem Internet erreichbar sein, sonst funktionieren die Links in den Mails nicht.
Verifizieren: curl -s -o /dev/null -w '%{http_code}' https://newsletter.example.com/admin/login liefert 200 mit gültigem Zertifikat, und ein Bestätigungslink aus einer neuen Testanmeldung öffnet sich ohne Zertifikatswarnung.
Schritt 7: Sichern und Wiederherstellen
Listmonk hält alles Wichtige in PostgreSQL: Abonnenten, Listen, Kampagnen, Vorlagen und Einstellungen samt SMTP-Zugangsdaten. Dazu kommen die Medien in uploads. pg_dump erzeugt im laufenden Betrieb einen konsistenten Stand:
cd /opt/listmonk
sudo mkdir -p /backup/listmonk
docker compose exec -T db pg_dump -U listmonk -Fc listmonk | sudo tee /backup/listmonk/listmonk-$(date +%F).dump >/dev/null
sudo tar -czf /backup/listmonk/uploads-$(date +%F).tar.gz uploads .env compose.yaml
Das Archiv enthält die .env mit Passwörtern, die Sicherung enthält die SMTP-Zugangsdaten. Lagern Sie beides geschützt. Zum Wiederherstellen stoppen Sie die Anwendung, spielen den Dump zurück und starten neu:
cd /opt/listmonk
docker compose stop app
docker compose exec -T db pg_restore -U listmonk -d listmonk --clean --if-exists < /backup/listmonk/listmonk-2026-10-14.dump
docker compose start app
Im Test wurde die versendete Kampagne nach der Sicherung gelöscht und per pg_restore zurückgeholt. Danach war sie wieder mit Status finished und einer versendeten Nachricht vorhanden, der Abonnent stand weiter auf confirmed. Für automatische Sicherungen mit Rotation siehe MySQL- und PostgreSQL-Backup automatisieren.
Verifizieren: Die Dump-Datei ist größer als 0 Byte (im Test rund 70 KB für eine kleine Installation), pg_restore -l auf die Datei listet die Tabellen subscribers, lists und campaigns. Nach einer Probe-Wiederherstellung zeigt docker compose logs app wieder http server started, und Ihre Kampagnen sind in der Oberfläche sichtbar.
Schritt 8: Updates einspielen
Lesen Sie die Release Notes auf GitHub, legen Sie eine Sicherung wie in Schritt 7 an und ändern Sie dann den Tag in der compose.yaml. Beim Start migriert --upgrade --yes das Datenbankschema.
cd /opt/listmonk
docker compose pull
docker compose up -d
docker compose logs app | grep -E "upgrade|migration|started"
Automatische Updates per Watchtower sind bei einem Newsletter-System mit Datenbankmigration riskant, weil die Sicherung vor dem Update fehlt.
Verifizieren: Das Log nennt die neue Versionsnummer und endet mit http server started, unter Settings steht die neue Version in der Überschrift, und Ihre Listen sind vollständig.
Typische Fehler
password authentication failed for user "listmonk":LISTMONK_DB_PASSWORDwurde nach dem ersten Start geändert. PostgreSQL behält das ursprüngliche Passwort. Entweder den alten Wert zurück in die.envoder das Passwort perdocker compose exec db psql -U listmonk -c "ALTER USER listmonk PASSWORD '…';"anpassen.- Admin-Passwort aus der .env greift nicht: Die Admin-Variablen wirken nur bei der Erstinstallation. Danach ändern Sie Passwörter unter Users oder nutzen auf der Login-Seite die Funktion zum Zurücksetzen, die funktionierendes SMTP voraussetzt.
- Bestätigungs- oder Abmeldelinks zeigen auf
localhost:9000: Die Root-URL steht noch auf dem Standardwerthttp://localhost:9000. In Settings > General korrigieren. - Testmail scheitert: Port und TLS-Modus passen nicht zusammen (587 mit STARTTLS, 465 mit TLS) oder der Server verlangt eine andere Authentifizierung. Die Fehlermeldung erscheint direkt nach dem Test.
- Bilder verschwinden nach einem Update: Upload-Pfad steht noch auf
uploadsstatt/listmonk/uploads, siehe Schritt 4. curl -Imeldet 404: Listmonk beantwortet HEAD-Anfragen nicht wie GET. Mit einer GET-Anfrage prüfen, siehe Schritt 3.- API antwortet mit 403: Die API akzeptiert keine normalen Anmeldedaten, sondern einen API-Benutzer mit Token. Diesen legen Sie unter Users an.
Häufige Fragen
Ersetzt Listmonk einen Mailserver?
Nein. Listmonk verwaltet Listen und Kampagnen und übergibt jede Nachricht an einen SMTP-Server. Für gute Zustellbarkeit brauchen Sie einen sauber eingerichteten Mailserver oder einen Versanddienst mit SPF, DKIM und DMARC, siehe DKIM mit OpenDKIM und Postfix einrichten oder Mailcow als Mailserver.
Kann ich eine vorhandene PostgreSQL-Datenbank nutzen?
Ja. Entfernen Sie den Dienst db, setzen Sie LISTMONK_db__host und die Zugangsdaten auf Ihren Datenbankserver und legen Sie dort eine leere Datenbank mit eigenem Benutzer an.
Wie binde ich ein Anmeldeformular auf der Website ein?
Listmonk erzeugt unter Lists > Forms HTML-Code für öffentliche Listen. Mit Double-Opt-In landen neue Adressen erst nach der Bestätigung als aktive Abonnenten in der Liste.
Fazit
Listmonk läuft mit zwei Containern auf einem kleinen Server und deckt Listen, Double-Opt-In und Kampagnen vollständig ab. Entscheidend für den Betrieb sind eine korrekte Root-URL, ein zuverlässiger SMTP-Server mit SPF, DKIM und DMARC, ein fester Image-Tag und eine geprüfte Sicherung der Datenbank vor jedem Update.
Weiterführende Anleitungen und Quellen
- Docker und Docker Compose auf Linux installieren
- Caddy als Reverse Proxy mit automatischem HTTPS
- MySQL- und PostgreSQL-Backup automatisieren
- DKIM mit OpenDKIM und Postfix einrichten
- Docker Compose absichern: Secrets, Healthchecks und Non-Root
- Docker-Container automatisch aktualisieren: Diun, WUD und Renovate
- Quellen: Listmonk-Dokumentation: Installation, offizielle docker-compose.yml (v6.2.0), Listmonk-API, listmonk/listmonk auf Docker Hub


