← Blog

Einen Mastodon-Server selbst hosten

Railyard

1. September 2026 · 7 Min. Lesezeit

Folge dem auf deinem eigenen Server. Kostenlos starten, keine Karte nötig

Eine eigene Ecke des Fediverse zu betreiben bedeutet, fünf Dinge zu betreiben, die sich alle untereinander einig sein müssen: einen Rails-Web-Prozess, einen separaten Streaming-Server für die Echtzeit-Timeline, einen Sidekiq-Worker für Federation und Benachrichtigungen, Postgres und Redis. Bekommst du die Domain in einem davon falsch, hast du eine Instanz, die halb lädt oder still aufhört zu federieren. Am Ende dieser Anleitung läuft eine echte Single-Server-Mastodon-Instanz mit allen fünf Diensten, hinter HTTPS, mit Backups und einem ehrlichen Blick darauf, was es wirklich kostet, sie gut zu betreiben.

Was du brauchst

  • Einen Server mit mindestens 4 vCPUs und 8 GB RAM für eine kleine Instanz; diese Anleitung betreibt alle fünf Dienste auf einer Box, was mehr ist, als der Web-Prozess allein braucht. Ein Hetzner CX33 (4 vCPU, 8 GB RAM, 80 GB NVMe, 8,99 €/Monat exkl. MwSt.) ist der realistische Startpunkt, sobald Postgres, Redis, der Streaming-Server und Sidekiq alle auf derselben Maschine um Ressourcen konkurrieren; wenn du über ein paar hundert aktive Nutzer wächst, lohnt es sich, Postgres auf eine eigene Box auszulagern.
  • Einen Domainnamen, auf den du einen A-Record zeigen lassen kannst. Mastodon bindet deine Account-Handles permanent an diese Domain; es gibt keinen unterstützten Weg, eine bestehende Instanz später auf eine neue Domain umzuziehen, ohne neu anzufangen.
  • Einen SMTP-Anbieter. Das ist nicht optional: Mastodon braucht funktionierenden E-Mail-Versand für Kontobestätigung, Passwort-Reset und Moderationshinweise, und liefert standardmäßig keinen konfiguriert. Jeder transaktionale E-Mail-Anbieter funktioniert.
  • SSH-Zugang zum Server.

Schritt 1: DNS setzen und die Firewall öffnen

ufw allow OpenSSH
ufw allow 80
ufw allow 443
ufw --force enable

Lege einen A-Record für deine Domain an, der auf die öffentliche IP des Servers zeigt. Gib ihm Zeit zu propagieren, bevor du zu Schritt 6 kommst.

Schritt 2: Jedes Secret vorab generieren

Mastodon braucht sechs eigenständige Secret-Werte, bevor es startet: eine Rails Secret Key Base, VAPID-Keys für Push-Benachrichtigungen und drei Active-Record-Verschlüsselungsschlüssel. Jeder hat seinen eigenen Generator-Befehl, ausgeführt über das Image selbst, sobald es gepullt ist:

mkdir -p /opt/mastodon && cd /opt/mastodon
docker pull ghcr.io/mastodon/mastodon:v4.7.3
docker run --rm ghcr.io/mastodon/mastodon:v4.7.3 bundle exec rails secret
docker run --rm ghcr.io/mastodon/mastodon:v4.7.3 bundle exec rake mastodon:webpush:generate_vapid_key
docker run --rm ghcr.io/mastodon/mastodon:v4.7.3 bundle exec rake db:encryption:init

Der erste Befehl gibt SECRET_KEY_BASE aus. Der zweite gibt ein VAPID_PRIVATE_KEY- und VAPID_PUBLIC_KEY-Paar aus. Der dritte gibt alle drei ACTIVE_RECORD_ENCRYPTION_*-Werte auf einmal aus. Speichere jeden davon:

cat > .env <<'EOF'
POSTGRES_PASSWORD=generate-with-openssl-rand-hex-24
REDIS_PASSWORD=generate-with-openssl-rand-hex-24
SECRET_KEY_BASE=paste-from-rails-secret
VAPID_PRIVATE_KEY=paste-from-generate-vapid-key
VAPID_PUBLIC_KEY=paste-from-generate-vapid-key
AR_ENCRYPTION_PRIMARY_KEY=paste-from-db-encryption-init
AR_ENCRYPTION_DETERMINISTIC_KEY=paste-from-db-encryption-init
AR_ENCRYPTION_KEY_DERIVATION_SALT=paste-from-db-encryption-init
SMTP_SERVER=smtp.your-provider.com
SMTP_LOGIN=your-smtp-username
SMTP_PASSWORD=your-smtp-password
EOF
chmod 600 .env

Schritt 3: Die Compose-Datei schreiben

services:
  mastodon:
    image: ghcr.io/mastodon/mastodon:v4.7.3
    restart: unless-stopped
    command: bash -c "bundle exec rails db:prepare && bundle exec puma -C config/puma.rb"
    env_file: .env
    depends_on:
      db:
        condition: service_healthy
      redis:
        condition: service_started
    environment:
      LOCAL_DOMAIN: social.yourdomain.com
      DB_HOST: db
      DB_PORT: "5432"
      DB_NAME: mastodon
      DB_USER: mastodon
      DB_PASS: ${POSTGRES_PASSWORD}
      REDIS_HOST: redis
      REDIS_PORT: "6379"
      REDIS_PASSWORD: ${REDIS_PASSWORD}
      STREAMING_API_BASE_URL: wss://social.yourdomain.com
      SMTP_FROM_ADDRESS: notifications@yourdomain.com
    volumes:
      - mastodon_system:/mastodon/public/system
    expose:
      - "3000"

  streaming:
    image: ghcr.io/mastodon/mastodon-streaming:v4.7.3
    restart: unless-stopped
    command: node ./streaming/index.js
    env_file: .env
    depends_on:
      db:
        condition: service_healthy
      redis:
        condition: service_started
    environment:
      PORT: "4000"
      LOCAL_DOMAIN: social.yourdomain.com
      DB_HOST: db
      DB_PORT: "5432"
      DB_NAME: mastodon
      DB_USER: mastodon
      DB_PASS: ${POSTGRES_PASSWORD}
      REDIS_HOST: redis
      REDIS_PORT: "6379"
      REDIS_PASSWORD: ${REDIS_PASSWORD}
    expose:
      - "4000"

  sidekiq:
    image: ghcr.io/mastodon/mastodon:v4.7.3
    restart: unless-stopped
    command: bundle exec sidekiq
    env_file: .env
    depends_on:
      db:
        condition: service_healthy
      redis:
        condition: service_started
    environment:
      LOCAL_DOMAIN: social.yourdomain.com
      DB_HOST: db
      DB_PORT: "5432"
      DB_NAME: mastodon
      DB_USER: mastodon
      DB_PASS: ${POSTGRES_PASSWORD}
      REDIS_HOST: redis
      REDIS_PORT: "6379"
      REDIS_PASSWORD: ${REDIS_PASSWORD}
      SMTP_FROM_ADDRESS: notifications@yourdomain.com
    volumes:
      - mastodon_system:/mastodon/public/system

  db:
    image: postgres:16.4-alpine
    restart: unless-stopped
    environment:
      POSTGRES_USER: mastodon
      POSTGRES_DB: mastodon
      POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
    volumes:
      - mastodon_db_data:/var/lib/postgresql/data
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U mastodon"]
      interval: 5s
      timeout: 5s
      retries: 10

  redis:
    image: redis:7.2.5-alpine
    restart: unless-stopped
    command: redis-server --requirepass ${REDIS_PASSWORD}
    env_file: .env
    volumes:
      - mastodon_redis_data:/data

  caddy:
    image: caddy:2
    restart: unless-stopped
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - ./Caddyfile:/etc/caddy/Caddyfile
      - caddy_data:/data
      - caddy_config:/config
    depends_on:
      - mastodon
      - streaming

volumes:
  mastodon_system:
  mastodon_db_data:
  mastodon_redis_data:
  caddy_data:
  caddy_config:

Diese Anleitung pinnt v4.7.3 für alle drei Mastodon-Images, das aktuelle stabile Release zum Zeitpunkt der Erstellung. Der Befehl des mastodon-Dienstes führt rails db:prepare aus, bevor Puma startet, dasselbe Muster, das Rails-Apps allgemein nutzen, damit das Schema-Setup automatisch beim ersten Start und bei jedem späteren Upgrade passiert. streaming ist ein wirklich eigenständiger Node.js-Prozess, kein Thread in der Rails-App, weshalb er sein eigenes Image und seine eigene STREAMING_API_BASE_URL hat, auf die der Hauptdienst zeigt. mastodon_system ist ein Volume, das speziell zwischen mastodon und sidekiq geteilt wird, da Sidekiq tatsächlich Medien-Uploads im Hintergrund verarbeitet; beide müssen die gleichen Dateien sehen.

Prüfen, ob die Datei korrekt parst:

docker compose config

Schritt 4: Caddy hinzufügen, einschließlich des Streaming-Pfads

social.yourdomain.com {
	@streaming path /api/v1/streaming*
	reverse_proxy @streaming streaming:4000

	reverse_proxy mastodon:3000
}

Mastodons Web-Client ruft /api/v1/streaming für seine Echtzeit-Timeline auf, was den separaten streaming-Dienst erreichen muss, nicht den Haupt-Rails-Prozess; alles andere geht an mastodon:3000. Validiere, dass die Caddyfile korrekt eingelesen wird, bevor du dich darauf verlässt:

docker run --rm -v "$PWD/Caddyfile:/etc/caddy/Caddyfile" caddy:2 caddy validate --config /etc/caddy/Caddyfile

Schritt 5: Den Stack starten und ein Admin-Konto erstellen

docker compose up -d
docker compose ps
curl -I https://social.yourdomain.com

Du solltest jeden Dienst als Up sehen (db als healthy) und curl sollte HTTP/2 200 zurückgeben. Erstelle das erste Konto von der Kommandozeile aus, mit Administrator-Rechten:

docker compose exec mastodon bin/tootctl accounts create admin \
  --email admin@yourdomain.com --confirmed --role Owner

Das gibt ein generiertes Passwort aus; melde dich damit an und ändere es sofort über das Web-UI.

Schritt 6: Postgres und das Medien-Volume sichern

cat > /opt/mastodon/backup.sh <<'EOF'
#!/bin/sh
cd /opt/mastodon
TIMESTAMP=$(date +%Y%m%d-%H%M%S)
docker compose exec -T db pg_dump -U mastodon mastodon | gzip > backups/db-$TIMESTAMP.sql.gz
find backups -mtime +14 -delete
EOF
mkdir -p /opt/mastodon/backups
chmod +x /opt/mastodon/backup.sh
(crontab -l 2>/dev/null; echo "0 3 * * * /opt/mastodon/backup.sh") | crontab -

Das mastodon_system-Volume enthält jeden hochgeladenen Medien-Anhang und wächst unbegrenzt, während Leute posten; sichere es separat mit welchem Full-Disk- oder Volume-Snapshot-Tooling auch immer dein Server-Anbieter bietet, ein pg_dump allein deckt das nicht ab. Führe das Skript einmal händisch aus und prüfe, dass ls -lh backups die heutige Datei zeigt.

Schritt 7: Upgrade

docker compose pull
docker compose up -d

db:prepare im Befehl des mastodon-Dienstes läuft bei jedem Neustart erneut und wendet an, was auch immer an Migration mit der neuen Version kam. Lies Mastodons eigene Release Notes, bevor du mehrere Versionen auf einmal überspringst; das Projekt weist gelegentlich auf eine erforderliche Zwischenversion für größere Schema-Änderungen hin.

Die Abkürzung: mit Railyard

Alles oben, Postgres und Redis an alle drei Rails-basierten Prozesse verdrahtet, der Streaming-Dienst mit eigenem Routing herausgetrennt, Sidekiq, das sich das Medien-Volume teilt, ist auch das, was Railyards Mastodon-Stack-Template für dich einrichtet. Verbinde einen Server und wähle dann Mastodon im Template-Katalog: app.railyard.run/deploy?stack=mastodon-stack. Railyard generiert jedes Secret aus Schritt 2 für dich und verdrahtet die Datenbank-, Redis- und Streaming-URLs automatisch; deinen eigenen SMTP-Anbieter fügst du danach trotzdem selbst hinzu, genau wie in Schritt 2 oben, Railyard konfiguriert dir ebenfalls keinen, und hochgeladene Medien landen standardmäßig weiterhin auf einem geteilten lokalen Volume, es sei denn, du zeigst selbst auf Object Storage.

Siehe mit einem Button deployen und Datenbanken und Backups für mehr darüber, wie Templates und persistente Daten auf der Plattform allgemein funktionieren.

Troubleshooting

Die Echtzeit-Timeline aktualisiert sich nie, aber Posten und Browsen funktionieren einwandfrei. Die Caddyfile routet /api/v1/streaming nicht zum streaming-Dienst; prüfe den Pfad-Match aus Schritt 4 gegen das, was der Netzwerk-Tab deines Browsers tatsächlich anfragt.

Keine Bestätigungs-E-Mail kommt bei einer neuen Registrierung an. SMTP ist nicht konfiguriert oder die Zugangsdaten sind falsch; prüfe docker compose logs mastodon auf einen SMTP-Fehler zum Zeitpunkt der versuchten Registrierung. Es gibt keinen Weg um diese Anforderung herum, Mastodon bestätigt kein Konto ohne das.

Sidekiq hängt nach, Posts von anderen Instanzen kommen verspätet an. Prüfe docker compose logs sidekiq auf wiederholte Job-Fehler oder einen Neustart wegen Speichermangel; auf einem CX33 ist das meist Ressourcenkonkurrenz mit Postgres und dem Web-Prozess während eines Traffic-Spikes, der Punkt, an dem sich das Auslagern von Postgres auf einen eigenen Server zu rentieren beginnt.

HTTPS wird nicht ausgestellt. Wenn docker compose logs caddy wiederholte ACME-Fehler zeigt, bedeutet das fast immer, dass der DNS-Record aus Schritt 1 noch nicht propagiert ist oder auf die falsche IP zeigt.

Festplatte läuft durch das Medien-Volume voll. Mastodon cacht entfernte Medien (Bilder und Videos anderer Instanzen) standardmäßig lokal; tootctl media remove --days 30 innerhalb des mastodon-Containers räumt gecachte entfernte Medien auf, die älter als ein gegebenes Fenster sind, ohne die Posts deiner eigenen Nutzer anzufassen.

FAQ

Kann ich die Domain meiner Instanz später ändern? Nein, Account-Handles sind permanent an die Domain gebunden; das ist eine fediverse-weite Einschränkung, die Mastodons eigene Konfiguration nicht umgehen kann, entscheide dich für die echte Domain, bevor du irgendjemanden einlädst.

Brauche ich wirklich 8 GB RAM für eine winzige Instanz mit einer Handvoll Nutzern? Die Untergrenze ist niedriger, aber sobald Postgres, Redis, der Streaming-Server und Sidekiq alle auf einer Box laufen, ist 8 GB der Punkt, an dem es nicht mehr eng wird; ein CX23 mit 4 GB bootet diesen Stack technisch, lässt aber sehr wenig Spielraum für Traffic-Spikes oder Federation-Stürme.

Was ist der Unterschied zwischen diesem Weg und dem Beitritt zu einer bestehenden Mastodon-Instanz? Eine eigene Instanz zu betreiben gibt dir deine eigene Moderationsrichtlinie, deine eigene Domain und volle Kontrolle über die Datenaufbewahrung; einer bestehenden Instanz beizutreten tauscht diese Kontrolle gegen null operativen Aufwand.

Kann Object Storage das lokale Medien-Volume ersetzen? Ja, Mastodon unterstützt S3-kompatiblen Speicher für Medien über seine eigene Konfiguration; diese Anleitung hält es für ein unkompliziertes erstes Setup lokal, es lohnt sich, das erneut zu betrachten, sobald das Medien-Volume zum Hauptverbraucher der Festplatte wird.

Ist Federation jemals standardmäßig deaktiviert? Nein, eine frische Instanz federiert mit dem breiteren Netzwerk, sobald sie über HTTPS erreichbar ist; es gibt einen separaten Allowlist-Modus, falls du stattdessen eine geschlossene Instanz willst, hier nicht behandelt.

Eine korrekt laufende Mastodon-Instanz sind fünf Dienste, die sich auf eine Domain und einen Satz Zugangsdaten einigen, nicht eine komplizierte Sache. Sobald DNS, die Streaming-Route und SMTP stimmen, ist die laufende Arbeit Backups, der gelegentliche Versions-Sprung und ein Auge auf Sidekiq unter echtem Traffic.

Warum Leute sich dafür Railyard entscheiden

  • Dein eigenes Cloud-Konto. Deploye auf DigitalOcean, Hetzner, Vultr, AWS, OVHcloud oder jedem Linux-Server per SSH, direkt von deinem Anbieter abgerechnet, ohne Aufschlag.
  • Ein fester Preis pro Team. Free ist für immer kostenlos: ein Server plus ein Datenbankserver, drei Apps, keine Karte. Pro kostet 19 $ im Monat für bis zu fünf Server und unbegrenzte Apps. Team kostet 49 $ im Monat mit unbegrenzten Servern. Keine Kosten pro App, pro Deployment, pro Sitz oder pro Gigabyte.
  • Templates mit einem Klick. Deploye Open-Source-Apps, wie Datenbanken, Analytics und Dev-Tools, mit einem Klick auf deinem eigenen Server.
  • Verwaltete Datenbanken. Postgres, MySQL und Redis mit geplanten, wiederherstellungsgeprüften Backups und Point-in-Time-Recovery.
  • Push-to-Deploy. Deploye von GitHub, GitLab oder der CLI, mit automatischem HTTPS und Releases ohne Ausfallzeit.

Mastodon mit einem Klick deployen

Wähle Mastodon in Railyards Template-Katalog und bekomme die Web-, Streaming- und Sidekiq-Prozesse schon fertig verdrahtet.

Kostenloser Plan, keine Kreditkarte, und 14 Tage Pro für jedes neue Team. Ein fester Preis pro Team.