Zum Hauptinhalt springen
S-EDV news
← Alle Anleitungen
📘 Anleitung IT-Branche 05.07.2026 · 10 min Lesezeit Geprüft 15.10.2026

Mautic mit Docker installieren: Open-Source-Marketing-Automation als HubSpot-Alternative

Mautic 7.2.1 mit dem offiziellen Docker-Compose-Beispiel einrichten: Installer, SMTP-Test mit Mailpit, Cron und Worker, Backup und Wiederherstellung. Alle Schritte auf Docker CE nachgestellt.

Werbung Mit * 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

Titelbild: Mautic mit Docker installieren, Kampagnen, Mails, Kontakte

Mautic ist eine Open-Source-Plattform für Marketing-Automation: Kontakte und Segmente, E-Mail-Kampagnen, Formulare, Landingpages und mehrstufige Kampagnenabläufe. Wer HubSpot oder ActiveCampaign ersetzen und die Kontaktdaten im eigenen Rechenzentrum halten will, findet hier den Docker-Weg. Die Anleitung folgt dem offiziellen Beispiel examples/basic aus dem Repository mautic/docker-mautic, legt die Versionen fest, ergänzt Mailpit als SMTP-Testziel und zeigt Sicherung und Wiederherstellung. Die Anleitung bezieht sich auf Mautic 7.2.1 und MySQL 8.4 auf Docker CE.

Voraussetzungen

  • Linux-Server oder VM mit Docker Engine und Compose-Plugin v2, Architektur amd64 oder arm64 (das Image mautic/mautic:7.2.1-apache gibt es für beide). Einrichtung: Docker und Docker Compose auf Linux installieren.
  • 2 CPU-Kerne und 4 GB RAM. Im Test belegte der Stack direkt nach der Installation rund 1 GB RAM (MySQL etwa 590 MB, Worker 230 MB, Web 180 MB); mit wachsender Kontaktzahl und Kampagnen steigt der Bedarf.
  • Rund 5 GB freier Speicher für die Images (Mautic etwa 3,1 GB, MySQL) plus Platz für Datenbank, Medien und Backups.
  • Für den Produktivbetrieb eine eigene Domain mit HTTPS über einen Reverse Proxy und ein SMTP-Zugang (eigener Mailserver oder Versanddienst) mit SPF, DKIM und DMARC für die Absenderdomain.
  • Rechtlich: Für Newsletter brauchen Sie eine nachweisbare Einwilligung (Double-Opt-in) und einen Vertrag zur Auftragsverarbeitung mit Ihrem Versanddienst. Dieser Beitrag ist keine Rechtsberatung.

Eckdaten

DienstImageAufgabeDaten
dbmysql:8.4.11DatenbankVolume mysql-data
mautic_webmautic/mautic:7.2.1-apacheWeboberfläche, Port 8080 → 80./mautic/config, logs, media
mautic_cronwie Web, DOCKER_MAUTIC_ROLE=mautic_cronSegmente, Kampagnen, Trigger alle 15 Minutenwie Web
mautic_workerwie Web, DOCKER_MAUTIC_ROLE=mautic_workerWarteschlangen für E-Mails und Seitenaufrufewie Web
mailpitaxllent/mailpit:v1.31.5SMTP-Testziel, Weboberfläche 8025keine

Schritt 1: Projektordner und Umgebungsdateien anlegen

$ sudo mkdir -p /opt/mautic/mautic/{config,logs,media/files,media/images}
$ cd /opt/mautic
$ openssl rand -hex 24   # zweimal ausführen: Datenbank- und Root-Passwort

Die Datei .env liest Docker Compose für die Platzhalter in der compose.yaml:

# /opt/mautic/.env
COMPOSE_PROJECT_NAME=mautic
MYSQL_DATABASE=mautic
MYSQL_USER=mautic
MYSQL_PASSWORD=ERSTES-ZUFALLSPASSWORT
MYSQL_ROOT_PASSWORD=ZWEITES-ZUFALLSPASSWORT
DOCKER_MAUTIC_LOAD_TEST_DATA=false

Die Datei .mautic_env geht als env_file an alle drei Mautic-Container. Compose ersetzt darin die ${…}-Verweise mit den Werten aus der .env. Als Host steht mysql, der Alias, den die compose.yaml über links: db:mysql vergibt.

# /opt/mautic/.mautic_env
MAUTIC_DB_HOST="mysql"
MAUTIC_DB_PORT="3306"
MAUTIC_DB_DATABASE="${MYSQL_DATABASE}"
MAUTIC_DB_USER="${MYSQL_USER}"
MAUTIC_DB_PASSWORD="${MYSQL_PASSWORD}"
MAUTIC_MESSENGER_DSN_EMAIL="doctrine://default"
MAUTIC_MESSENGER_DSN_HIT="doctrine://default"
PHP_INI_VALUE_DATE_TIMEZONE="Europe/Berlin"

doctrine://default legt die Warteschlangen in die MySQL-Datenbank; ein RabbitMQ ist damit nicht nötig. Ein chown der Ordner war im Test nicht nötig: Das Image legte config/local.php beim Start selbst mit dem Besitzer www-data an.

ls -a /opt/mautic zeigt .env, .mautic_env und den Ordner mautic, grep -c ZUFALLSPASSWORT .env ergibt 0 (alle Platzhalter ersetzt).

Schritt 2: compose.yaml erstellen

Gegenüber dem offiziellen Beispiel sind die Image-Tags festgelegt (dort mysql:lts und mautic/mautic:latest), Neustartregeln ergänzt und Mailpit hinzugefügt. Mailpit lauscht nur auf 127.0.0.1 und dient ausschließlich dem Test.

x-mautic-volumes:
  &mautic-volumes
  - ./mautic/config:/var/www/html/config:z
  - ./mautic/logs:/var/www/html/var/logs:z
  - ./mautic/media/files:/var/www/html/docroot/media/files:z
  - ./mautic/media/images:/var/www/html/docroot/media/images:z

services:
  db:
    image: mysql:8.4.11
    environment:
      - MYSQL_ROOT_PASSWORD=${MYSQL_ROOT_PASSWORD}
      - MYSQL_DATABASE=${MYSQL_DATABASE}
      - MYSQL_USER=${MYSQL_USER}
      - MYSQL_PASSWORD=${MYSQL_PASSWORD}
    volumes:
      - mysql-data:/var/lib/mysql
    healthcheck:
      test: mysqladmin --user=$$MYSQL_USER --password=$$MYSQL_PASSWORD ping
      start_period: 5s
      interval: 5s
      timeout: 5s
      retries: 10
    restart: unless-stopped

  mautic_web:
    image: mautic/mautic:7.2.1-apache
    links:
      - db:mysql
    ports:
      - 8080:80
    volumes: *mautic-volumes
    environment:
      - DOCKER_MAUTIC_LOAD_TEST_DATA=${DOCKER_MAUTIC_LOAD_TEST_DATA}
    env_file:
      - .mautic_env
    healthcheck:
      test: curl http://localhost
      start_period: 5s
      interval: 5s
      timeout: 5s
      retries: 100
    depends_on:
      db:
        condition: service_healthy
    restart: unless-stopped

  mautic_cron:
    image: mautic/mautic:7.2.1-apache
    links:
      - db:mysql
    volumes: *mautic-volumes
    environment:
      - DOCKER_MAUTIC_ROLE=mautic_cron
    env_file:
      - .mautic_env
    depends_on:
      mautic_web:
        condition: service_healthy
    restart: unless-stopped

  mautic_worker:
    image: mautic/mautic:7.2.1-apache
    links:
      - db:mysql
    volumes: *mautic-volumes
    environment:
      - DOCKER_MAUTIC_ROLE=mautic_worker
    env_file:
      - .mautic_env
    depends_on:
      mautic_web:
        condition: service_healthy
    restart: unless-stopped

  mailpit:
    image: axllent/mailpit:v1.31.5
    ports:
      - "127.0.0.1:8025:8025"
    restart: unless-stopped

volumes:
  mysql-data:

Hinter einem Reverse Proxy auf demselben Server ändern Sie die Portzeile in 127.0.0.1:8080:80, damit Mautic nur über den Proxy erreichbar ist.

docker compose config --quiet endet ohne Ausgabe. docker compose config | grep MAUTIC_DB_PASSWORD zeigt Ihr Passwort statt eines leeren Werts.

Schritt 3: Stack starten

$ docker compose pull
$ docker compose up -d
$ docker compose ps

Der Web-Container startet erst, wenn MySQL gesund ist; Cron und Worker warten wiederum auf den Web-Container. Vor der Installation schreibt der Cron-Container regelmäßig Waiting for Mautic to be installed ins Log. Das ist erwartet.

Nach etwa einer Minute zeigt docker compose ps db und mautic_web als healthy, mautic_cron, mautic_worker und mailpit als Up. curl -sI http://localhost:8080/ | grep -i location verweist auf /index.php/installer.

Schritt 4: Installationsassistent durchlaufen

Öffnen Sie http://SERVER-IP:8080. Der Assistent prüft zuerst die Umgebung und meldet „Ready to Install!“. Im Datenbankschritt sind Treiber, Host mysql, Port, Datenbankname und Benutzer bereits aus .mautic_env vorbelegt. Das Passwortfeld bleibt leer: Ohne Eingabe bricht die Installation im Test mit Access denied for user 'mautic'@… (using password: NO) ab. Tragen Sie dort MYSQL_PASSWORD aus der .env ein.

Mautic-Installationsassistent, Schritt Datenbank: Treiber MySQL PDO, Host mysql, Port 3306, Datenbank und Benutzer mautic, Passwortfeld leer
Datenbankschritt: Host, Port, Name und Benutzer kommen aus der Umgebung, das Passwort tragen Sie selbst ein.

Im zweiten Schritt legen Sie den Administrator an. Verwenden Sie einen eigenen Benutzernamen statt admin und ein langes Passwort. Der Datenbankschritt dauert beim Anlegen der Tabellen ein bis zwei Minuten; danach leitet Mautic auf die Anmeldeseite.

Mautic-Installationsassistent, Schritt Administrator: Benutzername, Passwort, Vorname, Nachname und E-Mail-Adresse
Administratorschritt: Benutzername, Passwort, Name und E-Mail-Adresse des ersten Kontos.

Ohne Browser geht es auch per Konsole, wie im README des Docker-Repositorys beschrieben: docker compose exec --user www-data --workdir /var/www/html mautic_web php ./bin/console mautic:install https://mautic.example.de --admin_email=… --admin_password=….

Die Anmeldung mit dem neuen Konto öffnet das Dashboard, unten links steht v7.2.1. docker compose exec --user www-data mautic_web php ./bin/console --version meldet Mautic 7.2.1.

Schritt 5: E-Mail-Versand mit Mailpit prüfen

Testen Sie den Versand zunächst gegen Mailpit, damit keine Testmail an echte Kontakte geht. Öffnen Sie über das Zahnrad Configuration → Email Settings. Tragen Sie unter Email DSN Schema smtp, Host mailpit und Port 1025 ein, darüber Absendername und Absenderadresse. Klicken Sie auf Save und danach auf Send test email: Der Test nutzt immer die gespeicherte DSN, nicht die gerade eingetippten Werte.

Mautic-Einstellungen, Bereich Email DSN: Schema smtp, Host mailpit, Port 1025, darunter die gespeicherte DSN smtp://mailpit:1025
Email DSN mit Mailpit als Ziel; die Zeile unter der Schaltfläche zeigt die gespeicherte Verbindung.

Die Testmail geht an die Adresse des angemeldeten Benutzers. Sie sehen sie in der Mailpit-Oberfläche unter http://127.0.0.1:8025 auf dem Server (per SSH-Tunnel: ssh -L 8025:127.0.0.1:8025 server). Für den Echtbetrieb ersetzen Sie Host, Port, Benutzer und Passwort durch die Daten Ihres Versanddienstes (meist Port 587 mit STARTTLS) und entfernen den Dienst mailpit aus der compose.yaml. Hintergrund zu Mailpit: Mailpit per Docker Compose einrichten.

curl -s http://127.0.0.1:8025/api/v1/messages | grep -o '"Subject":"[^"]*"' liefert "Subject":"Mautic test email". Im Test kam die Mail mit dem eingetragenen Absender an.

Schritt 6: Cron und Worker prüfen

Ohne Cron aktualisiert Mautic keine Segmente und führt keine Kampagnen aus. Der Cron-Container bringt einen vorkonfigurierten Zeitplan mit: Segmente zu den Minuten 0, 15, 30 und 45, Kampagnen 5 Minuten später, Trigger weitere 5 Minuten später. Der Worker-Container verarbeitet die Warteschlangen für E-Mails, Seitenaufrufe und fehlgeschlagene Nachrichten mit je zwei Prozessen (DOCKER_MAUTIC_WORKERS_CONSUME_*).

$ docker compose exec mautic_cron cat /opt/mautic/cron/mautic
$ docker compose logs --tail 5 mautic_worker
$ docker compose exec --user www-data --workdir /var/www/html mautic_web php ./bin/console mautic:segments:update

Eigene Zeitpläne binden Sie über einen Ordner ./cron nach /opt/mautic/cron ein (im offiziellen Beispiel als auskommentierte Zeile vorbereitet).

Der erste Befehl zeigt die Crontab mit mautic:segments:update, mautic:campaigns:update und mautic:campaigns:trigger. Das Worker-Log enthält Quit the worker with CONTROL-C, und der manuelle Segmentlauf endet mit Exit-Code 0.

Schritt 7: Backup und Wiederherstellung

Zu sichern sind die Datenbank (Kontakte, Kampagnen, E-Mails) und der Ordner mautic (config/local.php mit Zugangsdaten und Geheimschlüssel, hochgeladene Medien) samt .env, .mautic_env und compose.yaml:

$ cd /opt/mautic && mkdir -p backup
$ docker compose exec -T db sh -c 'exec mysqldump --single-transaction --no-tablespaces -u"$MYSQL_USER" -p"$MYSQL_PASSWORD" "$MYSQL_DATABASE"' | gzip > backup/mautic-db-$(date +%F).sql.gz
$ sudo tar czf backup/mautic-files-$(date +%F).tar.gz mautic .env .mautic_env compose.yaml

Wiederherstellung auf einem leeren Host: Dateiarchiv entpacken, nur die Datenbank starten, den Dump einspielen, dann den Rest starten. Warten Sie nach dem ersten Start von MySQL, bis im Log ready for connections mit port: 3306 steht. Im Test schlug ein Import direkt nach dem ersten healthy fehl, weil MySQL zu diesem Zeitpunkt noch seine Erstinitialisierung abschloss; der zweite Versuch lief sauber durch.

$ sudo tar xzf backup/mautic-files-DATUM.tar.gz
$ docker compose up -d db
$ docker compose logs db | grep "port: 3306"
$ gunzip -c backup/mautic-db-DATUM.sql.gz | docker compose exec -T db sh -c 'exec mysql -u"$MYSQL_USER" -p"$MYSQL_PASSWORD" "$MYSQL_DATABASE"'
$ docker compose up -d

Im Test wurde ein Kontakt mit einer Marker-Adresse angelegt, gesichert, der gesamte Stack samt Volume und Ordner mautic gelöscht und wiederhergestellt. Danach startete Mautic direkt ins Dashboard statt in den Installer, die Anmeldung funktionierte, der Kontakt war in der Kontaktliste vorhanden und die SMTP-Einstellung erhalten.

gunzip -c backup/mautic-db-DATUM.sql.gz | grep -c "INSERT INTO \`leads\`" ist größer null. Nach einer Wiederherstellung leitet curl -sI http://localhost:8080/ auf /s/dashboard statt auf den Installer, und die Kontaktsuche findet einen bekannten Kontakt. Mehr zur Ablage außerhalb des Servers: 3-2-1-Backup-Strategie praktisch umsetzen.

Schritt 8: Updates einspielen

Sichern Sie wie in Schritt 7. Ändern Sie dann den Tag aller drei Mautic-Dienste in der compose.yaml auf die neue Version aus den Release Notes, etwa mit sed -i 's/7.2.1-apache/NEUE-VERSION-apache/' compose.yaml, und starten Sie neu:

$ docker compose pull
$ docker compose up -d
$ docker compose logs mautic_web | grep -i migration

Laut Startskript des Images führt der Web-Container bei einer bestehenden Installation beim Start doctrine:migrations:migrate aus. Springen Sie nicht über Hauptversionen hinweg, sondern folgen Sie dem Upgrade-Pfad in der Mautic-Dokumentation. MySQL aktualisieren Sie getrennt und nur innerhalb von 8.4.

docker compose exec --user www-data mautic_web php ./bin/console --version zeigt die neue Version, alle Dienste laufen, und das Log enthält keine Fehler aus den Migrationen.

Typische Fehler

„Access denied … (using password: NO)“ im Installer

Das Passwortfeld im Datenbankschritt wurde leer gelassen. Passwort aus der .env eintragen.

Testmail kommt nicht an

Die Einstellungen wurden vor dem Test nicht gespeichert; der Test nutzt die gespeicherte DSN, die unter der Schaltfläche angezeigt wird.

Cron-Log meldet dauerhaft „Waiting for Mautic to be installed“

Die Installation ist nicht abgeschlossen oder config/local.php fehlt im eingebundenen Ordner.

„mkfifo: cannot create fifo '/tmp/stdout': File exists“

nach einem Neustart des Cron-Containers: Im Test blieb Cron trotzdem aktiv (cron -f lief). Bei Zweifeln den Container mit docker compose up -d --force-recreate mautic_cron neu erzeugen.

Restore: Importfehler direkt nach dem Start

MySQL war noch in der Erstinitialisierung. Auf port: 3306 im Log warten und erneut importieren.

Kampagnen laufen nicht

mautic_cron oder mautic_worker gestoppt. docker compose ps prüfen.

Häufige Fragen

Warum drei Mautic-Container?

Alle nutzen dasselbe Image, die Variable DOCKER_MAUTIC_ROLE legt die Aufgabe fest. So lassen sich Web, Zeitplan und Warteschlangen getrennt neu starten und skalieren.

Geht PostgreSQL oder MariaDB?

Das offizielle Docker-Beispiel und der Installer setzen auf MySQL (Treiber MySQL PDO). Diese Anleitung bleibt deshalb bei MySQL 8.4; andere Datenbanken prüfen Sie vorab gegen die Systemanforderungen der eingesetzten Mautic-Version.

Brauche ich RabbitMQ?

Nein. Mit doctrine://default liegen die Warteschlangen in MySQL. Für sehr große Versandmengen gibt es im Repository das Beispiel rabbitmq-worker.

Wie binde ich HTTPS an?

Über einen Reverse Proxy wie Traefik mit automatischem HTTPS oder Nginx Proxy Manager. Tragen Sie danach unter Configuration → System Settings die öffentliche HTTPS-Adresse als Site URL ein, sonst enthalten Tracking-Links und E-Mails die interne Adresse.

Lassen sich Demodaten laden?

Ja, mit DOCKER_MAUTIC_LOAD_TEST_DATA=true vor der ersten Installation. Das Startskript installiert dann eine Demo-Instanz mit Beispieldaten; für den Produktivbetrieb bleibt der Wert auf false.

Fazit

Mit dem offiziellen Compose-Beispiel, festgelegten Tags und den drei Rollen-Containern steht Mautic in rund einer halben Stunde. Zwei Stellen kosten im Alltag Zeit: das leere Datenbankpasswort im Installer und der Mailtest, der nur gespeicherte Einstellungen nutzt. Wer den Versand erst gegen Mailpit prüft, regelmäßig Dump und Ordner sichert und die Wiederherstellung einmal durchspielt, betreibt die Marketing-Automation mit überschaubarem Aufwand selbst. Für einfache Newsletter ohne Kampagnenlogik ist Listmonk die schlankere Alternative.

Weiterführende Anleitungen und Quellen

Passende Anleitungen auf S-EDV

  1. netcup Local Block Storage bestellen, einrichten und unter Linux einbinden