Mastodon mit Docker installieren: Dezentrales Fediverse-Mikroblogging auf eigenem Server
Mastodon ist die meistgenutzte Fediverse-Plattform und ermöglicht eine eigene, werbefreie Twitter/X-Alternative. Diese Anleitung zeigt das vollständige Docker-Compose-Setup mit PostgreSQL, Redis, Puma und Sidekiq.

Mastodon ist das meistgenutzte Open-Source-Netzwerk im Fediverse: über 50.000 GitHub-Stars, aktiv gepflegt und bereit für Organisationen, Vereine, Bildungseinrichtungen oder KMUs, die eine datenschutzkonforme Social-Media-Instanz auf eigener Infrastruktur betreiben wollen. Statt Algorithmen, Werbung und Datenweitergabe an Dritte bekommt man volle Kontrolle – und durch das ActivityPub-Protokoll (W3C-Standard) trotzdem Verbindung zu Millionen Nutzerinnen und Nutzern auf anderen Instanzen (Pixelfed, PeerTube, Misskey u.v.m.). Diese Anleitung zeigt den generischen Docker-Compose-Weg auf einem Linux-Host: plattformneutral, ohne GUI-Abhängigkeit, mit Verifikation nach jedem Schritt.
Voraussetzungen
- Docker Engine ≥ 24.x mit integriertem Compose-Plugin v2 (
docker compose, nichtdocker-compose). Falls noch nicht installiert: Docker und Docker Compose auf Linux installieren. - Linux-Host, VM oder leistungsfähiges NAS – mindestens 2 CPU-Kerne, 2 GB RAM, 20 GB SSD-Speicher. Für mehr als 50 gleichzeitige Nutzer werden 4 GB RAM empfohlen.
- Eigene Domain (z. B.
mastodon.example.com) mit DNS-A-Eintrag auf den Server. Diese Domain ist nach dem ersten Start nicht mehr änderbar. - Öffentliche IPv4-Adresse und eingehende Ports 80/443 erreichbar.
- SMTP-Mailserver oder -Dienst (z. B. Mailgun, Postmark, eigener Postfix) – ohne SMTP keine Account-Verifikation.
- Reverse Proxy mit TLS (nginx, Caddy oder Traefik). HTTPS ist Pflicht für ActivityPub-Federation. Caddy eignet sich für Einsteiger gut: Caddy als Reverse Proxy mit automatischem HTTPS einrichten.
- Optional für große Instanzen (>100 Nutzer): S3-kompatibler Objektspeicher für Medien-Uploads.
Eckdaten auf einen Blick
| Parameter | Wert |
|---|---|
| Image (Web + Sidekiq) | ghcr.io/mastodon/mastodon:v4.5.11 |
| Image (Streaming) | ghcr.io/mastodon/mastodon-streaming:v4.5.11 |
| Aktuelle Stable-Version | v4.5.11 (Juni 2025) |
| Port Web (Puma/Rails) | 127.0.0.1:3000:3000 – nur lokal, per Reverse Proxy nach außen |
| Port Streaming (Node.js/WS) | 127.0.0.1:4000:4000 – nur lokal, per Reverse Proxy nach außen |
| Volume Medien | ./public/system → /mastodon/public/system (UID/GID 991) |
| Volume Datenbank | Named Volume postgres14 |
| Volume Redis | Named Volume redis |
| Architektur | linux/amd64 und linux/arm64 |
| Mindest-RAM | 2 GB (4 GB empfohlen ab ~100 Nutzern) |
Schritt 1: Projektordner anlegen und Verzeichnisstruktur vorbereiten
Lege einen dedizierten Ordner für die Mastodon-Instanz an. Alle Daten, Konfigurationsdateien und Compose-Definitionen landen hier.
sudo mkdir -p /opt/mastodon/public/system
sudo chown -R 991:991 /opt/mastodon/public/system
cd /opt/mastodonDer chown-Befehl ist entscheidend: Alle Mastodon-Prozesse laufen als UID/GID 991. Fehlen die Berechtigungen, schlägt der Web-Container beim Schreiben von Medien-Uploads mit Permission denied fehl.
Verifizieren: Prüfe die Eigentümerschaft des Verzeichnisses:
ls -ld /opt/mastodon/public/system
# Erwartete Ausgabe:
# drwxr-xr-x ... 991 991 ... /opt/mastodon/public/systemSchritt 2: Kryptoschlüssel generieren und .env.production anlegen
Mastodon benötigt vor dem ersten Start sechs Schlüssel, die nach der Inbetriebnahme nicht mehr geändert werden dürfen – Änderungen invalidieren alle Sessions, zerstören 2FA-Tokens und machen verschlüsselte Datenbankfelder unlesbar. Starte dafür temporär den Web-Container ohne laufende Datenbank:
# Im Ordner /opt/mastodon
# SECRET_KEY_BASE generieren
docker run --rm ghcr.io/mastodon/mastodon:v4.5.11 bundle exec rails secret
# OTP_SECRET generieren (zweiter Aufruf, anderer Wert!)
docker run --rm ghcr.io/mastodon/mastodon:v4.5.11 bundle exec rails secret
# ACTIVE_RECORD_ENCRYPTION-Schluessel generieren (alle drei auf einmal)
docker run --rm ghcr.io/mastodon/mastodon:v4.5.11 bundle exec rails db:encryption:init
# VAPID-Schluesselpaar generieren
docker run --rm ghcr.io/mastodon/mastodon:v4.5.11 bundle exec rake mastodon:webpush:generate_vapid_keyNotiere alle Ausgaben. Erstelle dann die Konfigurationsdatei /opt/mastodon/.env.production und ersetze alle Platzhalter durch die generierten Werte:
# ===== Instanz-Domain (NICHT aenderbar nach erstem Start!) =====
LOCAL_DOMAIN=mastodon.example.com
# ===== Rails-Umgebung =====
RAILS_ENV=production
# ===== Kryptoschluessel (generierte Werte eintragen) =====
SECRET_KEY_BASE=HIER_GENERIERTEN_WERT_EINTRAGEN
OTP_SECRET=HIER_GENERIERTEN_WERT_EINTRAGEN
# Active Record Encryption (aus bundle exec rails db:encryption:init)
ACTIVE_RECORD_ENCRYPTION_DETERMINISTIC_KEY=HIER_EINTRAGEN
ACTIVE_RECORD_ENCRYPTION_KEY_DERIVATION_SALT=HIER_EINTRAGEN
ACTIVE_RECORD_ENCRYPTION_PRIMARY_KEY=HIER_EINTRAGEN
# VAPID Web-Push-Schluessel
VAPID_PRIVATE_KEY=HIER_EINTRAGEN
VAPID_PUBLIC_KEY=HIER_EINTRAGEN
# ===== Datenbank =====
DB_HOST=db
DB_PORT=5432
DB_NAME=mastodon_production
DB_USER=mastodon
DB_PASS=sicheres_db_passwort_hier_aendern
# ===== Redis =====
REDIS_HOST=redis
REDIS_PORT=6379
# ===== SMTP (Pflicht fuer Account-Verifikation) =====
SMTP_SERVER=smtp.example.com
SMTP_PORT=587
SMTP_LOGIN=
SMTP_PASSWORD=
SMTP_FROM_ADDRESS=notifications@mastodon.example.com
# ===== Optionale Einstellungen =====
S3_ENABLED=false
ES_ENABLED=falseSetze außerdem das PostgreSQL-Passwort als separate Variable für docker compose:
echo "POSTGRES_PASSWORD=sicheres_db_passwort_hier_aendern" > /opt/mastodon/.envBeide Passwörter (DB_PASS in .env.production und POSTGRES_PASSWORD in .env) müssen identisch sein.
Verifizieren: Stichprobe der gesetzten Werte:
grep -c "HIER_" /opt/mastodon/.env.production
# Muss 0 zurueckgeben – alle Platzhalter muessen ersetzt seinSchritt 3: compose.yaml erstellen
Lege die Datei /opt/mastodon/compose.yaml an. Das interne Netzwerk internal_network ist mit internal: true isoliert, sodass PostgreSQL und Redis nicht vom Host erreichbar sind. Die Web- und Streaming-Ports werden nur auf 127.0.0.1 gebunden – der Reverse Proxy übernimmt die TLS-Terminierung.
services:
db:
image: postgres:14-alpine
restart: always
shm_size: 256mb
networks:
- internal_network
healthcheck:
test: ["CMD", "pg_isready", "-U", "mastodon"]
interval: 10s
timeout: 5s
retries: 5
volumes:
- postgres14:/var/lib/postgresql/data
environment:
POSTGRES_DB: mastodon_production
POSTGRES_USER: mastodon
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
redis:
image: redis:7-alpine
restart: always
networks:
- internal_network
healthcheck:
test: ["CMD", "redis-cli", "ping"]
interval: 10s
timeout: 5s
retries: 5
volumes:
- redis:/data
web:
image: ghcr.io/mastodon/mastodon:v4.5.11
restart: always
env_file: .env.production
command: bundle exec puma -C config/puma.rb
networks:
- external_network
- internal_network
healthcheck:
test: ["CMD-SHELL", "curl -fs localhost:3000/health || exit 1"]
interval: 30s
timeout: 10s
retries: 3
ports:
- "127.0.0.1:3000:3000"
depends_on:
db:
condition: service_healthy
redis:
condition: service_healthy
volumes:
- system:/mastodon/public/system
streaming:
image: ghcr.io/mastodon/mastodon-streaming:v4.5.11
restart: always
env_file: .env.production
command: node ./streaming/index.js
networks:
- external_network
- internal_network
healthcheck:
test: ["CMD-SHELL", "curl -fs localhost:4000/api/v1/streaming/health || exit 1"]
interval: 30s
timeout: 10s
retries: 3
ports:
- "127.0.0.1:4000:4000"
depends_on:
db:
condition: service_healthy
redis:
condition: service_healthy
sidekiq:
image: ghcr.io/mastodon/mastodon:v4.5.11
restart: always
env_file: .env.production
command: bundle exec sidekiq
networks:
- external_network
- internal_network
depends_on:
db:
condition: service_healthy
redis:
condition: service_healthy
volumes:
- system:/mastodon/public/system
healthcheck:
test: ["CMD-SHELL", "ps aux | grep '[s]idekiq 6' || exit 1"]
interval: 30s
timeout: 10s
retries: 3
networks:
external_network:
internal_network:
internal: true
volumes:
postgres14:
redis:
system:Verifizieren: Syntax prüfen, bevor irgendwas gestartet wird:
docker compose -f /opt/mastodon/compose.yaml config --quiet
# Kein Output = keine SyntaxfehlerSchritt 4: Datenbank initialisieren und Instanz einrichten
Starte zunächst nur die Datenbank-Services, damit der Setup-Wizard eine Verbindung aufbauen kann:
cd /opt/mastodon
docker compose up -d db redisWarte, bis beide Container den Status healthy haben (dauert ca. 15–30 Sekunden), dann führe den interaktiven Setup-Wizard aus:
docker compose run --rm web bundle exec rake mastodon:setupDer Wizard fragt schrittweise nach Domain, Datenbankverbindung, Redis, SMTP und ob ein Admin-Konto erstellt werden soll. Da alle Werte bereits in .env.production stehen, kannst du die meisten Abfragen mit Enter bestätigen. Am Ende führt der Wizard db:migrate sowie assets:precompile durch. Das dauert je nach Hardware 3–10 Minuten – besonders die Asset-Kompilierung ist RAM-intensiv (mindestens 2 GB RAM notwendig).
Falls du den Admin-Account lieber manuell anlegen möchtest:
docker compose exec web tootctl accounts create meinadmin \
--email admin@mastodon.example.com \
--confirmed \
--role OwnerVerifizieren:
docker compose ps db redis
# BEIDE Container muessen Status "healthy" zeigen:
# NAME IMAGE STATUS
# ...db postgres:14-alpine Up X minutes (healthy)
# ...redis redis:7-alpine Up X minutes (healthy)
docker compose logs db --tail=10
# Letzte Zeile sollte enthalten: "database system is ready to accept connections"Schritt 5: Alle Container starten
Starte jetzt den vollständigen Stack:
cd /opt/mastodon
docker compose up -dAlle fünf Container – db, redis, web, streaming und sidekiq – sollten hochfahren. Web und Streaming starten erst, wenn db und redis healthy melden (dank depends_on: condition: service_healthy).
Verifizieren:
docker compose ps
# Alle 5 Container: Status "Up" oder "Up (healthy)"
# NAME STATUS
# mastodon-db-1 Up X minutes (healthy)
# mastodon-redis-1 Up X minutes (healthy)
# mastodon-web-1 Up X minutes (healthy)
# mastodon-streaming-1 Up X minutes (healthy)
# mastodon-sidekiq-1 Up X minutes (healthy)
# Web-Endpunkt lokal testen
curl -I http://localhost:3000/health
# HTTP/1.1 200 OK
# Streaming-Endpunkt testen
curl -I http://localhost:4000/api/v1/streaming/health
# HTTP/1.1 200 OKZeigen einzelne Container Fehler, schau in die Logs:
docker compose logs web --tail=30
docker compose logs sidekiq --tail=30Schritt 6: Reverse Proxy und HTTPS einrichten
Mastodon bindet Web (3000) und Streaming (4000) ausschließlich an 127.0.0.1 – ein Reverse Proxy mit TLS-Terminierung ist Pflicht. Ohne HTTPS funktioniert ActivityPub-Federation grundsätzlich nicht. Für eine ausführliche Einrichtung von Caddy mit automatischem Let's-Encrypt-Zertifikat: Caddy als Reverse Proxy mit automatischem HTTPS einrichten. Alternativ eignet sich Traefik als Docker-Reverse-Proxy für komplexere Setups mit mehreren Diensten.
Minimale nginx-Konfiguration für Mastodon (TLS-Block ergänzen):
server {
listen 443 ssl http2;
server_name mastodon.example.com;
location / {
proxy_pass http://127.0.0.1:3000;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto https;
}
location /api/v1/streaming {
proxy_pass http://127.0.0.1:4000;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
}
}Verifizieren: Rufe https://mastodon.example.com im Browser auf. Die Mastodon-Startseite muss erscheinen. Im Browser-Entwicklertools-Tab „Netzwerk" sollte beim Laden der Timeline eine WS-Verbindung zu /api/v1/streaming aufgebaut werden.
curl -I https://mastodon.example.com
# HTTP/2 200Schritt 7: Updates durchführen
Vor jedem Update die Datenbankmigrationen nicht vergessen – fehlende Migrationen führen zu Missing columns or tables-Fehlern. Für ein automatisches Monitoring von neuen Image-Versionen eignet sich Diun oder WUD als Watchtower-Nachfolger.
cd /opt/mastodon
# 1. Neue Image-Tags in compose.yaml setzen (z. B. v4.5.11 -> v4.6.x)
# 2. Images ziehen
docker compose pull
# 3. Migrationen ausfuehren (VOR dem Restart!)
docker compose run --rm web bundle exec rails db:migrate
# 4. Container neu starten
docker compose up -dBei Major-Updates Mastodon-Changelogs auf SKIP_POST_DEPLOYMENT_MIGRATIONS-Hinweise prüfen – bei großen Migrationen wird ein zweistufiges Rollout empfohlen.
Verifizieren:
docker compose ps
# Alle Container: "Up (healthy)"
docker compose logs web --tail=20
# Keine "Missing columns"-FehlerSchritt 8: Backup einrichten
Eine vollständige Mastodon-Sicherung besteht aus drei Teilen:
# PostgreSQL-Dump
docker compose exec db pg_dump -U mastodon mastodon_production \
> /backup/mastodon-db-$(date +%F).sql
# Medien-Uploads
rsync -av /opt/mastodon/public/system/ /backup/mastodon-media/
# Konfigurationsdatei (enthaelt alle Secrets!)
cp /opt/mastodon/.env.production /backup/mastodon-env.production.bakFür automatisierte Backups mit Rotation und Cloud-Sync: MySQL & PostgreSQL Backup automatisieren mit cron. Die .env.production sicher und verschlüsselt aufbewahren – sie enthält alle Schlüssel.
Verifizieren:
ls -lh /backup/mastodon-db-*.sql
# Datei vorhanden, Groesse > 0 BytesTroubleshooting / Typische Fehler
could not connect to server: Connection refused(DB):DB_HOSTin.env.productionmussdbsein (Docker-Compose-Service-Name), nichtlocalhostoder/var/run/postgresql. Prüfen:grep DB_HOST .env.production.Permission denied @ rb_sysopenoderRead-only file system: Das Verzeichnis./public/systemgehört nicht UID 991. Fix:sudo chown -R 991:991 /opt/mastodon/public/system.- Asset-Kompilierung schlägt fehl (Out-of-Memory): Weniger als 2 GB RAM. Swap-Space einrichten:
fallocate -l 2G /swapfile && chmod 600 /swapfile && mkswap /swapfile && swapon /swapfile. - Keine Echtzeit-Updates im Browser (Timeline friert ein): Streaming-Container abgestürzt oder Reverse Proxy leitet
/api/v1/streamingnicht als WebSocket weiter. Prüfen:docker compose logs streaming --tail=30und Proxy-Konfiguration aufUpgrade-Header. - Keine Federation mit anderen Instanzen: Häufigste Ursachen: (1) Kein gültiges HTTPS-Zertifikat, (2)
LOCAL_DOMAINstimmt nicht mit der tatsächlichen erreichbaren Domain überein, (3) Port 443 nicht von außen erreichbar, (4) Sidekiq-Container läuft nicht (Federation läuft asynchron über Sidekiq). Prüfen:docker compose ps sidekiq. Failed to release advisory lockbei Migrationen: PgBouncer-Konflikt.DB_PORT=5432explizit in.env.productionsetzen und sicherstellen, dass Migrationen direkt gegen PostgreSQL laufen.Missing columns or tablesnach Update: Datenbankmigrationen wurden nicht ausgeführt. Vor dem nächstendocker compose up -dimmer:docker compose run --rm web bundle exec rails db:migrate.- Instanz nicht erreichbar nach
LOCAL_DOMAIN-Änderung: Alle Nutzer-IDs, Medien-URLs und Federationsbeziehungen sind ungültig.LOCAL_DOMAINmuss vor dem allerersten Start korrekt gesetzt und danach nie geändert werden.
Häufige Fragen
Kann ich Mastodon auf ARM64 (Raspberry Pi, Apple Silicon) betreiben?
Ja. Die offiziellen Images auf ghcr.io unterstützen sowohl linux/amd64 als auch linux/arm64. Auf einem Raspberry Pi 4 mit 4 GB RAM und SSD läuft eine kleine Instanz mit weniger als 20 Nutzern problemlos.
Warum darf ich nicht das alte tootsuite/mastodon-Image von Docker Hub verwenden?
Das tootsuite/mastodon-Image auf Docker Hub ist seit v4.x nicht mehr offiziell gepflegt und erhält keine Security-Updates. Die aktuellen Images sind ausschließlich auf der GitHub Container Registry verfügbar: ghcr.io/mastodon/mastodon und ghcr.io/mastodon/mastodon-streaming.
Wie erstelle ich nach dem ersten Start einen weiteren Admin-Account?
Mit dem integrierten Verwaltungswerkzeug tootctl, das im Web-Container enthalten ist:
docker compose exec web tootctl accounts create BENUTZERNAME \
--email admin@mastodon.example.com \
--confirmed \
--role OwnerAb wann sollte ich auf S3-Objektspeicher umstellen?
Ab etwa 100 aktiven Nutzern wächst das Medien-Volume (public/system) schnell auf viele Gigabyte an. S3-kompatibler Objektspeicher (AWS S3, Hetzner Object Storage, MinIO, Wasabi) entlastet den Server erheblich. Für ein MinIO-Setup auf eigenem Server: MinIO S3 selbst hosten mit Docker. In .env.production genügt dann S3_ENABLED=true plus Zugangsdaten.
Kann Mastodon mit anderen Fediverse-Plattformen kommunizieren?
Ja – das ist der Kerngedanke von ActivityPub. Mastodon-Nutzer können Accounts auf Pixelfed (Bilder), PeerTube (Videos), Misskey, Pleroma/Akkoma oder Friendica folgen und mit ihnen interagieren, sofern die eigene Instanz über HTTPS erreichbar ist und die Remote-Instanz nicht blockiert wurde.
Wie aktiviere ich die Volltextsuche?
Mastodon unterstützt optional Elasticsearch (oder den Fork OpenSearch) für die Suche über eigene Posts und favorisierte Beiträge. Einen separaten Elasticsearch-Container in compose.yaml ergänzen und ES_ENABLED=true sowie ES_HOST=elasticsearch in .env.production setzen. Danach Index aufbauen: docker compose run --rm web tootctl search deploy.
Fazit
Mastodon ist technisch anspruchsvoller als typische Single-Container-Anwendungen – fünf Services, strenge Secret-Generierung, ein unveränderlicher Domain-Name und ein zwingend notwendiger Reverse Proxy mit TLS. Der Aufwand lohnt sich für Organisationen, Vereine und Communities, die eine vollständig selbst kontrollierte, datenschutzkonforme Social-Media-Plattform betreiben wollen. Wer die initiale Konfiguration sauber aufgesetzt hat, profitiert von aktiver Weiterentwicklung, regelmäßigen Security-Releases und der Verbindung zu Millionen Fediverse-Nutzern weltweit. Für eine robuste Produktivumgebung sollte man neben dem Stack selbst auch den Backup-Rhythmus (täglicher DB-Dump + Medien-Sync) und ein Monitoring einplanen, das alle fünf Container überwacht.
Weiterführende Anleitungen und Quellen
- Docker und Docker Compose auf Linux installieren – Grundlage für alle Docker-Setups
- Caddy als Reverse Proxy mit automatischem HTTPS einrichten – einfachster Weg zu TLS
- MinIO S3 selbst hosten mit Docker – für Medien-Uploads bei wachsenden Instanzen
- MySQL & PostgreSQL Backup automatisieren mit cron – Datenbankbackup mit Rotation
- Traefik als Docker-Reverse-Proxy mit automatischem HTTPS
- NetBird mit Docker: Zero-Trust-Netzwerk – sicherer Zugriff auf die Admin-Oberfläche
Quellen: Mastodon Offizielle Dokumentation – Admin-Konfiguration | mastodon/mastodon – Offizielles GitHub-Repository (docker-compose.yml) | Mastodon Troubleshooting – Offizielle Dokumentation