Zum Hauptinhalt springen
S-EDV news
← Alle Anleitungen
📘 Anleitung Server & Netzwerk 14.08.2026 · 10 min Lesezeit Geprüft 11.10.2026

Mastodon mit Docker installieren: Dezentrales Fediverse-Mikroblogging auf eigenem Server

Mastodon ist das meistgenutzte Fediverse-Netzwerk. Die Anleitung zeigt das Docker-Compose-Setup mit PostgreSQL, Redis, Web, Streaming und Sidekiq, Reverse Proxy, Update, Backup und typische Fehler.

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: Mastodon mit Docker installieren, Fediverse, Beiträge, Server

Mastodon ist das meistgenutzte Open-Source-Netzwerk im Fediverse. Organisationen, Vereine und KMUs betreiben damit eine Social-Media-Instanz auf eigener Infrastruktur, ohne Werbung und Datenweitergabe. Über das ActivityPub-Protokoll (W3C-Standard) bleibt die Instanz mit anderen Plattformen wie Pixelfed, PeerTube oder Misskey verbunden.

Voraussetzungen

  • Docker Engine ≥ 24.x mit Compose-Plugin v2 (docker compose). Installation: Docker und Docker Compose auf Linux installieren.
  • Linux-Host oder VM (x86-64 oder ARM64): mindestens 2 CPU-Kerne, 2 GB RAM, 20 GB SSD. Die fünf Container belegen im Leerlauf rund 1 GB RAM, ab etwa 50 gleichzeitigen Nutzern sind 4 GB ratsam.
  • Eigene Domain (z. B. mastodon.example.com) mit DNS-A-Eintrag auf den Server, nach dem ersten Start nicht mehr änderbar.
  • Öffentliche IPv4-Adresse, Ports 80/443 erreichbar.
  • SMTP-Dienst (z. B. Mailgun, Postmark, eigener Postfix), sonst keine Account-Verifikation.
  • Reverse Proxy mit TLS (nginx, Caddy oder Traefik), Pflicht für die Federation, z. B. Caddy als Reverse Proxy mit automatischem HTTPS einrichten.

Eckdaten auf einen Blick

ParameterWert
Image (Web + Sidekiq)ghcr.io/mastodon/mastodon:v4.7.3
Image (Streaming)ghcr.io/mastodon/mastodon-streaming:v4.7.3
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 MedienBind-Mount ./public/system → /mastodon/public/system (UID/GID 991)
Volumes Datenbank, RedisNamed Volumes postgres14, redis
Architekturlinux/amd64 und linux/arm64

Schritt 1: Projektordner anlegen und Verzeichnisstruktur vorbereiten

Legen Sie den Projektordner an.

$ sudo mkdir -p /opt/mastodon/public/system
$ sudo chown -R 991:991 /opt/mastodon/public/system
$ cd /opt/mastodon

Alle Mastodon-Prozesse laufen als UID/GID 991, sonst scheitern Medien-Uploads mit Permission denied.

ls -ldn /opt/mastodon/public/system zeigt als Besitzer und Gruppe 991 991.

Schritt 2: Kryptoschlüssel generieren und .env.production anlegen

Mastodon braucht vor dem ersten Start sechs Schlüssel: SECRET_KEY_BASE, drei Active-Record-Encryption-Schlüssel und das VAPID-Schlüsselpaar. Sie dürfen später nicht mehr geändert werden, sonst werden Sessions ungültig und verschlüsselte Datenbankfelder samt 2FA-Tokens unbrauchbar. Erzeugen Sie sie mit kurzlebigen Containern, ohne Datenbank. OTP_SECRET entfällt seit Mastodon 4.4.

# Im Ordner /opt/mastodon
# SECRET_KEY_BASE
$ docker run --rm ghcr.io/mastodon/mastodon:v4.7.3 bundle exec rails secret
# ACTIVE_RECORD_ENCRYPTION (alle drei Schlüssel)
$ docker run --rm ghcr.io/mastodon/mastodon:v4.7.3 bundle exec rails db:encryption:init
# VAPID-Schlüsselpaar
$ docker run --rm ghcr.io/mastodon/mastodon:v4.7.3 bundle exec rake mastodon:webpush:generate_vapid_key

Legen Sie /opt/mastodon/.env.production an und ersetzen Sie die Platzhalter durch die erzeugten Werte:

# Instanz-Domain, nach dem ersten Start nicht änderbar
LOCAL_DOMAIN=mastodon.example.com

RAILS_ENV=production

# Kryptoschlüssel (erzeugte Werte eintragen)
SECRET_KEY_BASE=HIER_GENERIERTEN_WERT_EINTRAGEN

# Active Record Encryption
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
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 für die Account-Verifikation)
SMTP_SERVER=smtp.example.com
SMTP_PORT=587
SMTP_LOGIN=
SMTP_PASSWORD=
SMTP_FROM_ADDRESS=notifications@mastodon.example.com

# Optional
S3_ENABLED=false
ES_ENABLED=false

Das PostgreSQL-Passwort braucht Compose zusätzlich in der .env:

$ echo "POSTGRES_PASSWORD=sicheres_db_passwort_hier_aendern" > /opt/mastodon/.env

DB_PASS und POSTGRES_PASSWORD müssen identisch sein.

grep -c "HIER_" /opt/mastodon/.env.production gibt 0 aus, alle Platzhalter sind ersetzt.

Schritt 3: compose.yaml erstellen

Legen Sie /opt/mastodon/compose.yaml an. internal_network ist mit internal: true isoliert, PostgreSQL und Redis sind vom Host aus nicht erreichbar. Web und Streaming binden nur an 127.0.0.1, TLS terminiert der Reverse Proxy. Wie Healthchecks in Compose funktionieren, zeigt die Anleitung Docker Compose absichern: Secrets, Healthchecks, Non-Root und Read-Only.

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.7.3
    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:
      - ./public/system:/mastodon/public/system

  streaming:
    image: ghcr.io/mastodon/mastodon-streaming:v4.7.3
    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.7.3
    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:
      - ./public/system:/mastodon/public/system
    healthcheck:
      test: ["CMD-SHELL", "ps aux | grep '[s]idekiq 8' || exit 1"]
      interval: 30s
      timeout: 10s
      retries: 3

networks:
  external_network:
  internal_network:
    internal: true

volumes:
  postgres14:
  redis:

docker compose -f /opt/mastodon/compose.yaml config --quiet endet ohne Ausgabe.

Schritt 4: Datenbank initialisieren und Instanz einrichten

Starten Sie zuerst nur Datenbank und Redis:

$ cd /opt/mastodon
$ docker compose up -d db redis

Sobald beide healthy sind (nach etwa 15 Sekunden), starten Sie den Setup-Wizard:

$ docker compose run --rm web bundle exec rake mastodon:setup

Der Wizard fragt nach Domain, Docker-Betrieb (mit „Yes“ beantworten), Datenbank, Redis, SMTP und Admin-Konto. Bei Docker gibt er die Konfiguration nur aus, gleichen Sie sie mit Ihrer .env.production ab. Die Assets sind im Image vorkompiliert.

Alternativ legen Sie den Admin-Account manuell an. Die Mailadresse braucht eine auflösbare Domain, mit example.com endet der Befehl mit email unreachable:

docker compose exec web tootctl accounts create meinadmin \
  --email admin@mastodon.example.com \
  --confirmed \
  --role Owner

docker compose ps db redis zeigt beide Container als (healthy), und docker compose logs db --tail=10 endet mit database system is ready to accept connections.

Schritt 5: Alle Container starten

Starten Sie den vollständigen Stack:

$ cd /opt/mastodon
$ docker compose up -d

Web, Streaming und Sidekiq warten per depends_on auf healthy bei db und redis.

docker compose ps zeigt alle fünf Container als Up (healthy) (Web, Streaming und Sidekiq nach etwa 35 Sekunden). Beide Endpunkte antworten mit 200 OK:

$ curl -I http://localhost:3000/health
$ curl -I http://localhost:4000/api/v1/streaming/health

Bei Fehlern:

$ docker compose logs web --tail=30
$ docker compose logs sidekiq --tail=30

Schritt 6: Reverse Proxy und HTTPS einrichten

Web (3000) und Streaming (4000) lauschen nur auf 127.0.0.1, ein Reverse Proxy mit TLS ist Pflicht. Caddy mit automatischem Zertifikat beschreibt Caddy als Reverse Proxy mit automatischem HTTPS einrichten, Traefik Traefik als Docker-Reverse-Proxy.

Minimale nginx-Konfiguration (ergänzen Sie ssl_certificate und ssl_certificate_key):

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";
    }
}

curl -I https://mastodon.example.com liefert HTTP/2 200, und im Browser erscheint die Startseite. Im Netzwerk-Tab der Entwicklertools baut die Timeline eine WebSocket-Verbindung zu /api/v1/streaming auf.

Schritt 7: Updates durchführen

Führen Sie bei jedem Update die Datenbankmigrationen aus, sonst erscheint Missing columns or tables. Neue Versionen melden Diun oder WUD als Watchtower-Nachfolger.

$ cd /opt/mastodon
# 1. Image-Tags in compose.yaml anheben (z. B. v4.7.2 -> v4.7.3)
# 2. Images ziehen
$ docker compose pull
# 3. Migrationen ausführen, vor dem Neustart
$ docker compose run --rm web bundle exec rails db:migrate
# 4. Container neu starten
$ docker compose up -d

Lesen Sie vor Sprüngen über eine Nebenversion (etwa 4.5 auf 4.7) die Release Notes aller übersprungenen Versionen.

Bestehende Installation von 4.5 auf 4.7 aktualisieren

Die Release Notes von 4.6.0 und 4.7.0 beschreiben den Weg ab 4.5.11 bzw. 4.6.6. PostgreSQL 14 und Redis 7 aus dieser Anleitung genügen weiterhin. Gehen Sie in zwei Etappen vor, zuerst auf 4.6.9, dann auf 4.7.3. Große Migrationen teilt Mastodon in einen Vor- und einen Nachlauf:

$ cd /opt/mastodon
# 0. Datenbank sichern (siehe Schritt 8)
# 1. Tags in compose.yaml auf v4.6.9 setzen, dann:
$ docker compose pull
$ docker compose run --rm -e SKIP_POST_DEPLOYMENT_MIGRATIONS=true web bundle exec rails db:migrate
$ docker compose up -d
$ docker compose run --rm web bundle exec rails db:migrate
# 2. Tags auf v4.7.3 setzen und dieselben vier Befehle wiederholen

Beachten Sie dabei:

  • Die Migrationen von 4.7 dauern laut Projekt auf großen Instanzen bis zu einigen Stunden, planen Sie ein Wartungsfenster ein. Sitzungs-Cookies aus der Zeit vor 4.3 gelten nicht mehr, betroffene Nutzer melden sich neu an.
  • E-Mail-Abonnements für öffentliche Beiträge (seit 4.6) schaltet ein Administrator frei. DISABLE_EMAIL_SUBSCRIPTIONS=true begrenzt das Mailaufkommen.
  • Eigene Themes brauchen wegen des neuen Theming-Systems aus 4.6 meist Anpassungen. MASTODON_USE_LIBVIPS=false wird ignoriert.
  • Der Sidekiq-Healthcheck prüft auf sidekiq 8 statt sidekiq 6, sonst gilt der Container als „unhealthy“.
  • Ältere Fassungen dieser Anleitung nutzten ein Named Volume system. Behalten Sie es bei oder kopieren Sie die Dateien vorher nach ./public/system (Rechte 991:991), sonst fehlen Medien.

docker compose ps zeigt alle Container als Up (healthy), und docker compose logs web --tail=20 enthält keine Missing columns-Fehler.

Schritt 8: Backup einrichten

Sichern Sie diese drei Teile:

# 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 (enthält alle Secrets!)
cp /opt/mastodon/.env.production /backup/mastodon-env.production.bak

Automatisierte Backups mit Rotation: MySQL & PostgreSQL Backup automatisieren mit cron. Bewahren Sie die .env.production verschlüsselt auf.

ls -lh /backup/mastodon-db-*.sql zeigt eine Datei mit Größe über 0 Bytes (zum Beispiel rund 310 KB).

Troubleshooting / Typische Fehler

  1. could not connect to server: Connection refused (DB): DB_HOST muss der Service-Name db sein, nicht localhost. Prüfen: grep DB_HOST .env.production.
  2. Permission denied @ rb_sysopen oder Read-only file system: ./public/system gehört nicht UID 991. Abhilfe: sudo chown -R 991:991 /opt/mastodon/public/system.
  3. Container werden beendet (Out-of-Memory): weniger als 2 GB RAM. Swap einrichten: fallocate -l 2G /swapfile && chmod 600 /swapfile && mkswap /swapfile && swapon /swapfile.
  4. Timeline aktualisiert sich nicht: Streaming-Container abgestürzt oder /api/v1/streaming im Proxy nicht als WebSocket weitergeleitet. Prüfen: docker compose logs streaming --tail=30 und Upgrade-Header.
  5. Keine Federation mit anderen Instanzen: (1) kein gültiges HTTPS-Zertifikat, (2) LOCAL_DOMAIN weicht von der erreichbaren Domain ab, (3) Port 443 nicht erreichbar, (4) Sidekiq läuft nicht (docker compose ps sidekiq).
  6. Failed to release advisory lock bei Migrationen: PgBouncer-Konflikt. Setzen Sie DB_PORT=5432 und lassen Sie Migrationen direkt gegen PostgreSQL laufen.
  7. Missing columns or tables nach Update: Migrationen ausführen, siehe Schritt 7.
  8. Instanz nach LOCAL_DOMAIN-Änderung defekt: Nutzer-IDs, Medien-URLs und Federation sind ungültig, die Domain lässt sich nicht nachträglich wechseln.

Häufige Fragen

Kann ich Mastodon auf ARM64 (Raspberry Pi, Apple Silicon) betreiben?

Ja, die offiziellen Images gibt es für linux/amd64 und linux/arm64.

Warum darf ich nicht das alte tootsuite/mastodon-Image von Docker Hub verwenden?

Auf Docker Hub erscheinen unter tootsuite/mastodon weiterhin Images, das Projekt selbst nutzt aber die GitHub Container Registry, und nur dort gibt es das Streaming-Image. Verwenden Sie ghcr.io/mastodon/mastodon und ghcr.io/mastodon/mastodon-streaming.

Wie erstelle ich nach dem ersten Start einen weiteren Admin-Account?

Mit tootctl im Web-Container (die Mailadresse braucht eine auflösbare Domain):

docker compose exec web tootctl accounts create BENUTZERNAME \
  --email admin@mastodon.example.com \
  --confirmed \
  --role Owner

Ab wann sollte ich auf S3-Objektspeicher umstellen?

Ab etwa 100 aktiven Nutzern wächst public/system auf viele Gigabyte. S3-kompatibler Objektspeicher (AWS S3, Hetzner Object Storage, Wasabi) entlastet den Server. Selbst hosten können Sie ihn mit S3-Speicher selbst hosten: Garage und RustFS im Vergleich. In .env.production genügt dann S3_ENABLED=true plus Zugangsdaten.

Kann Mastodon mit anderen Fediverse-Plattformen kommunizieren?

Ja, das ist der Kern von ActivityPub. Nutzer folgen Accounts auf Pixelfed, PeerTube, Misskey, Pleroma/Akkoma oder Friendica, sofern die Instanz über HTTPS erreichbar ist.

Wie aktiviere ich die Volltextsuche?

Optional über Elasticsearch oder OpenSearch. Ergänzen Sie einen Elasticsearch-Container in der compose.yaml, setzen Sie ES_ENABLED=true und ES_HOST=elasticsearch in der .env.production und bauen Sie den Index auf: docker compose run --rm web tootctl search deploy.

Fazit

Mastodon ist aufwendiger als Single-Container-Anwendungen: fünf Services, unveränderliche Secrets und Domain, Reverse Proxy mit TLS. Planen Sie tägliche Backups und ein Monitoring aller fünf Container ein.

Weiterführende Anleitungen und Quellen

  1. Docker und Docker Compose auf Linux installieren
  2. Caddy als Reverse Proxy mit automatischem HTTPS einrichten
  3. S3-Speicher selbst hosten: Garage und RustFS im Vergleich
  4. MySQL & PostgreSQL Backup automatisieren mit cron
  5. Traefik als Docker-Reverse-Proxy mit automatischem HTTPS
  6. NetBird mit Docker: Zero-Trust-Netzwerk

Quellen: Admin-Konfiguration (Dokumentation) | docker-compose.yml im Repository | Troubleshooting (Dokumentation) | Release Notes 4.7.0 | Release Notes 4.6.0