Zum Hauptinhalt springen
S-EDV news
← Alle Anleitungen
📘 Anleitung E-Mail / Mailserver 04.08.2026 · 10 min Lesezeit

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.

Illustration zur Installation von Listmonk mit Docker auf einem Linux Server. Die Grafik zeigt einen selbst gehosteten Newsletter Server mit Docker Container, Server Infrastruktur, Datenbank, Admin Dashboard, E Mail Kampagnen, Sicherheitsfunktionen und sk KI-generiert

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

  1. 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.
  2. Arbeitsspeicher: Mindestens 512 MB RAM; für den Produktivbetrieb mit größeren Listen empfohlen: 1 GB+.
  3. Festplatte: Mindestens 2 GB freier Platz für PostgreSQL-Daten, Logs und Medien-Uploads.
  4. SMTP-Zugangsdaten eines Mailservers oder Transaktions-Mail-Dienstes (Amazon SES, Postmark, Mailgun) – werden nach dem ersten Start in der Web-UI hinterlegt.
  5. Optional: Eigene Domain und Reverse-Proxy (Caddy, Traefik oder Nginx) für HTTPS. Listmonk selbst hat keinen eingebauten TLS-Terminator.

Eckdaten auf einen Blick

EigenschaftWert
Docker-Imagelistmonk/listmonk:latest (aktuell v6.1.0, März 2026)
Nightly-Buildlistmonk/listmonk:nightly
Architekturenlinux/amd64, linux/arm64, linux/arm/v7, linux/arm/v6
Image-Größe~14 MB (komprimiert, amd64)
Web-UI / API-Port9000 (HTTP)
DatenbankPostgreSQL 12+ (empfohlen: postgres:17-alpine)
LizenzAGPLv3
Volume / Bind-MountZweck
./uploads:/listmonk/uploads:rwHochgeladene Medien und Bilder für Newsletter (Bind-Mount auf dem Host)
listmonk-data:/var/lib/postgresql/dataPostgreSQL-Datenbankdaten (benanntes Docker-Volume)
UmgebungsvariablePflichtBeschreibung
LISTMONK_app__addressjaBind-Adresse und Port (0.0.0.0:9000)
LISTMONK_db__hostjaHostname des PostgreSQL-Containers im Compose-Netzwerk
LISTMONK_db__user / __password / __databasejaDatenbankzugangsdaten – müssen mit POSTGRES_USER/PASSWORD/DB übereinstimmen
LISTMONK_ADMIN_USER / _PASSWORDnur ErstinstallationAdmin-Login beim ersten --install; danach wirkungslos
TZneinZeitzone 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/listmonk

Das 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/.gitignore

Alle 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 -d

Docker 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 app

Taucht 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 Antwort

Schritt 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:

  1. Navigiere zu Settings > SMTP.
  2. Trage Host, Port, TLS-Modus, Benutzername und Passwort deines Mailservers oder Transaktions-Dienstes ein.
  3. 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 -d

Fü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:

  1. PostgreSQL-Datenbank – per pg_dump aus dem laufenden Container:
docker exec listmonk_db pg_dump -U listmonk listmonk > /backup/listmonk_$(date +%Y%m%d).sql
  1. Uploads-Verzeichnis – alle hochgeladenen Medien liegen auf dem Host unter /opt/listmonk/uploads/ und lassen sich mit rsync oder 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

  1. App startet vor der DB (restart-loop): Altes depends_on ohne condition: service_healthy. Die hier gezeigte compose.yaml behebt das Problem. Prüfe mit docker compose ps, ob listmonk_db den Status (healthy) zeigt.
  2. „password authentication failed for user listmonk": LISTMONK_DB_PASSWORD in der .env und das bereits gesetzte PostgreSQL-Passwort stimmen nicht überein. Beim ersten Start müssen beide Seiten gleich sein. Lösung: Stack mit docker compose down -v komplett entfernen (löscht das Volume!) und neu starten.
  3. Admin-Login funktioniert nicht nach Erstinstallation: LISTMONK_ADMIN_USER und LISTMONK_ADMIN_PASSWORD wurden 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 ''
  4. Port 9000 nicht erreichbar von außen: LISTMONK_app__address ist auf 127.0.0.1:9000 statt 0.0.0.0:9000 gesetzt. Der Container lauscht dann nur intern. Wert in der compose.yaml korrigieren und docker compose up -d erneut ausführen.
  5. „cannot use config file and env variables together": Eine config.toml wird als Volume eingebunden und gleichzeitig werden LISTMONK_*-Umgebungsvariablen gesetzt. Nur eine Methode verwenden. --config '' im command deaktiviert die Datei-Methode.
  6. Uploads verschwinden nach Neustart: Das Bind-Mount ./uploads:/listmonk/uploads:rw fehlt in der compose.yaml. Ohne dieses Volume speichert Listmonk Medien im Container-Dateisystem, das bei jedem Neustart zurückgesetzt wird.
  7. Upgrade schlägt fehl nach Versionswechsel: Bei Sprüngen wie v5 → v6 muss das Datenbankschema migriert werden. Die offizielle compose.yaml erledigt das automatisch über --upgrade --yes im command. Prüfe mit docker 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

  1. Docker und Docker Compose auf Linux installieren (Ubuntu/Debian): die Self-Hosting-Grundlage
  2. Caddy als Reverse-Proxy einrichten: Anfänger-Anleitung mit automatischem HTTPS
  3. Mailcow komplett aufsetzen – eigener Mailserver mit Docker
  4. OpenDKIM mit Postfix einrichten: DKIM-Signatur korrekt konfigurieren
  5. MySQL & PostgreSQL Backup automatisieren mit cron und rclone
  6. Watchtower mit Docker: Container automatisch aktualisieren

Offizielle Quellen: Listmonk Installationsdokumentation | Listmonk auf GitHub (knadh/listmonk) | Listmonk auf Docker Hub