| Filename | Latest commit message | Latest commit date |
|---|---|---|
| volumes | ||
| .env | ||
| .gitignore | ||
| AGENTS.md | ||
| CLAUDE.md | ||
| docker-compose.yml | ||
| LICENSE | ||
| Projekt.md | ||
| README.md | ||
Mastodon – WSC Standard Stack
Selbst gehosteter Mastodon-Server (Fediverse/ActivityPub) auf Basis der offiziellen
ghcr.io/mastodon/mastodon- und ghcr.io/mastodon/mastodon-streaming-Images, mit
PostgreSQL 17, Elasticsearch (Volltextsuche) und Redis. Für den persönlichen Gebrauch
schlank dimensioniert – Details siehe Projekt.md.
Template-Hinweis:
.enventhält nur Platzhalter (CHANGE_ME,example.*). Vor dem Einsatz anpassen, Secrets gemäß Anleitung unten erzeugen. Werte, die je in Git lagen, gelten als kompromittiert → rotieren.
1. Voraussetzungen
- Docker + Docker Compose v2
- Externes Netz
traefik_proxy_network(Traefik-Stack) - Zentrale Stacks für die Integrationen:
docker-autoheal-neustart,docker-ofelia-cronjobs,docker-docker-backup-backups - Backup-Verzeichnis
${BACKUP_PATH}(Default/Daten/Backups) muss auf dem Host existieren - Die Volume-Ordner unter
./volumes/existieren bereits nachgit clone/pull(leer bis auf.gitkeep) – derlocal-Volume-Treiber (o: bind, type: none) legt fehlende Zielverzeichnisse NICHT selbst an, ein frischer Deploy würde sonst beim erstendocker compose upmit „no such file or directory" abbrechen - Ein SMTP-Zugang für den Mail-Versand (Registrierungsbestätigung, Freigabe-Benachrichtigung, Passwort-Reset)
2. .env vorbereiten
2.1 Domain
HOSTRULE und PRIMARY_DOMAIN auf die eigene Domain setzen. Wichtig: PRIMARY_DOMAIN
wird als LOCAL_DOMAIN an Mastodon durchgereicht und kann nach dem ersten Produktivstart
nicht mehr sicher geändert werden (betrifft Federation und alle Account-Handles wie
@user@social.example.com) – vorher final festlegen.
2.2 Passwörter (Postgres, Redis, Elasticsearch)
openssl rand -hex 32
Für DB_PASSWORD, REDIS_PASSWORD, ES_PASSWORD je einmal ausführen und in die .env
eintragen.
2.3 Mastodon-Secrets
Diese drei Secret-Gruppen nicht selbst erfinden, sondern mit den offiziellen
Mastodon-Bordmitteln erzeugen. MASTODON_VERSION aus der .env übernehmen (Platzhalter
unten: v4.6.5 – ggf. an den aktuell in der .env gesetzten Wert anpassen):
# SECRET_KEY_BASE
docker run --rm ghcr.io/mastodon/mastodon:v4.6.5 bundle exec rails secret
# VAPID_PRIVATE_KEY / VAPID_PUBLIC_KEY (Web Push)
docker run --rm ghcr.io/mastodon/mastodon:v4.6.5 \
bundle exec rails mastodon:webpush:generate_vapid_key
# ACTIVE_RECORD_ENCRYPTION_DETERMINISTIC_KEY / _KEY_DERIVATION_SALT / _PRIMARY_KEY
docker run --rm ghcr.io/mastodon/mastodon:v4.6.5 bin/rails db:encryption:init
Jeweiligen Output in die passenden .env-Variablen eintragen.
⚠️
ACTIVE_RECORD_ENCRYPTION_*nach dem ersten Produktivstart nie mehr ändern – sonst sind verschlüsselte Bestandsdaten (u. a. 2FA-Secrets) nicht mehr lesbar.
2.4 SMTP
SMTP_SERVER, SMTP_PORT, SMTP_LOGIN, SMTP_PASSWORD, SMTP_FROM_ADDRESS eintragen.
Ohne funktionierendes SMTP können sich neue Nutzer nicht bestätigen und du bekommst keine
Freigabe-Benachrichtigungen (siehe Abschnitt 5).
3. Starten
Wichtig:
docker composeimmer aus diesem Stack-Verzeichnis starten (${PWD}-Mounts).
3.1 Volume-Berechtigungen setzen (einmalig, VOR dem ersten Start!)
Docker legt ./volumes/<name> beim allerersten docker compose up automatisch an – und zwar
als root:root. postgres und redis fixen das selbst (ihr Entrypoint startet als root,
chownt sein Datenverzeichnis und wechselt danach per gosu zum eigentlichen Prozess-User) –
elasticsearch und das offizielle Mastodon-Image (web/sidekiq) tun das NICHT, sie
laufen von Anfang an fest als nicht-root-User und brechen sonst beim Start ab (siehe
Abschnitt 7, „Volume-Berechtigungen"). Deshalb diese beiden vorher chownen:
mkdir -p volumes/postgres volumes/redis volumes/elasticsearch volumes/public-system
sudo chown -R 1000:0 volumes/elasticsearch # docker.elastic.co/elasticsearch/elasticsearch
sudo chown -R 991:991 volumes/public-system # ghcr.io/mastodon/mastodon (web/sidekiq)
# volumes/postgres und volumes/redis NICHT manuell chownen - die offiziellen
# Postgres-/Redis-Images korrigieren die Rechte beim Start selbst.
3.2 Stack starten
# Basis-Dienste zuerst hochfahren (Datenbank muss für den Setup-Schritt bereitstehen)
docker compose up -d db redis elasticsearch
# Datenbank-Schema einmalig anlegen (nur beim allerersten Start, NICHT bei Updates!)
docker compose run --rm web bundle exec rails db:setup
# Restlichen Stack starten
docker compose up -d
4. Ersten Admin-Account anlegen
docker compose exec web bin/tootctl accounts create \
DEIN_BENUTZERNAME \
--email deine@email.de \
--confirmed \
--role Owner
Gibt ein zufälliges Passwort in der Konsolenausgabe aus (danach im Profil ändern).
5. Registrierung auf „Freigabe erforderlich" stellen
Der Registrierungsmodus ist eine Laufzeit-Einstellung in der Datenbank, keine
.env-Variable – deshalb einmalig nach dem Erststart per tootctl setzen:
docker compose exec web bin/tootctl settings registrations approved
Damit sind öffentliche Anmeldungen sichtbar, neue Konten werden aber erst nach deiner
manuellen Freigabe im Admin-Bereich (/admin/pending_accounts) aktiv. Mastodon schaltet
offene Registrierungen zusätzlich automatisch auf „Freigabe erforderlich" zurück, sobald
eine Woche lang keine moderierende Person aktiv war (DISABLE_AUTOMATIC_SWITCHING_TO_ APPROVED_REGISTRATIONS=true in der .env setzen, falls unerwünscht).
Um die Registrierung ganz zu schließen (nur du selbst als Nutzer):
docker compose exec web bin/tootctl settings registrations none
6. Integrationen
Autoheal (Self-Healing)
autoheal=true auf db, redis, elasticsearch, web, streaming, sidekiq (alle mit
Healthcheck).
⚠️ Der
web-Healthcheck prüfthttp://localhost:3000/health. Liefert die App dauerhaft Fehler (z. B.db:setupvergessen), kann Autoheal den Container in eine Restart-Schleife bringen → beim ersten Setup beobachten.
CronJobs (Ofelia) – Schedule ist 6-Feld (mit Sekunden!)
Auf web (als ${CRONJOB_USER}, Standard mastodon): zwischengespeicherte Remote-Medien
entfernen (media remove --days=${MEDIA_RETENTION_DAYS}), verwaiste Medien-Anhänge
(media remove-orphans), alte Link-Vorschaubilder (preview_cards remove), wöchentlich
verwaiste Remote-Account-Daten (accounts cull).
DB-Backup (shyim/docker-backup) – Schedule 5-Feld
Label auf db: type=postgres, BACKUP_SCHEDULE (Default alle 6 h), Retention
BACKUP_RETENTION. Landet unter /Daten/Backups/<db-container>/db/….
Datei-Backup (Ofelia + backup-helper)
Da shyim nur named volumes sichert (Medien sind bind-backed), läuft das Datei-Backup über
den idle backup-helper: Ofelia job-exec packt täglich 05:00 ein tar.gz von
/mastodon/public/system nach ${BACKUP_PATH}/<projekt>/files/ (Retention 5 Tage).
7. Hinweise / Stolpersteine
- Volume-Berechtigungen (UID/GID) beim allerersten Start – live gegentestet:
Docker legt
./volumes/<name>beim erstendocker compose upalsroot:rootan.db(postgres:17-alpine) undredis(redis:7-alpine) fixen das selbst: ihr Entrypoint startet als root, chownt sein Datenverzeichnis und wechselt danach pergosuzum eigentlichen Prozess-User – kein manuelleschownnötig.elasticsearch(docker.elastic.co/elasticsearch/elasticsearch) läuft von Anfang an fest als1000:0ohne Selbstkorrektur. Ohne vorherigenchownbricht der Start mitfailed to obtain node locks ... maybe these locations are not writableab (java.io.IOException, siehedocker compose logs elasticsearch).web/sidekiq(ghcr.io/mastodon/mastodon) laufen ebenfalls fest als991:991ohne Selbstkorrektur (nurUSER mastodonim Image, kein chown-Entrypoint) – betrifft denpublic-system-Mount (Medien). Live aufgetretenes Symptom: Login und normale Nutzung funktionieren, aber ein Bild-Upload schlägt mit HTTP 500 fehl, weilweb/sidekiqnicht in/mastodon/public/systemschreiben können. Check:docker compose exec web id(erwartetuid=991) gegenls -ln volumes/public-system(Owner-UID) vergleichen; bei Mismatchsudo chown -R 991:991 volumes/public-systemund Stack neu starten.- Reihenfolge/Befehle siehe Abschnitt 3.1. Falls trotzdem vergessen: erst
docker compose down, betroffenesvolumes/<name>komplett neu anlegen (nicht nur leeren – ein abgebrochener Init hinterlässt oft Reste) und chownen, dann neu starten. - Postgres-Sonderfall, live aufgetreten: Nach mehreren abgebrochenen Start
versuchen blieb im
volumes/postgres-Verzeichnis eine versteckte (Punkt-)Datei zurück →initdb: error: directory ... exists but is not empty/contains a dot-prefixed/invisible file.rm -rf volumes/postgres/*reicht dafür nicht, weil die Shell-Glob*keine Dot-Dateien erfasst – das Verzeichnis muss komplett gelöscht und neu angelegt werden (rm -rf volumes/postgres && mkdir -p volumes/postgres).
- Ofelia vs. shyim Cron-Format: Ofelia = 6-Feld (
0 5 0 * * *), shyim = 5-Feld (5 0 * * *). Nicht verwechseln. - Redis: nur eine Instanz für Cache + Sidekiq-Queue + Streaming –
maxmemory-policy noevictionist Pflicht, sonst gehen unter Speicherdruck stillschweigend Hintergrund-Jobs verloren. - Elasticsearch:
xpack.securityist deaktiviert (wie in der offiziellen Mastodon-Vorlage), der Service ist rein intern (kein Traefik-Label).ES_PASSWORDwird aktuell nicht erzwungen, ist aber für eine spätere Härtung schon vorbereitet. - Streaming-Routing: Traefik-Router für
streaminghatpriority=100(fest gesetzt), derweb-Routerpriority=1– so geht/api/v1/streamingimmer anstreaming, alles andere anweb. Bei eigenen Anpassungen an den Traefik-Labels diese Priorität beibehalten. LOCAL_DOMAIN/PRIMARY_DOMAINvor dem ersten Produktivstart final festlegen (siehe Abschnitt 2.1).- Updates: bei einem neuen
MASTODON_VERSIONimmer zuerst den offiziellen Changelog auf Breaking Changes/Migrationen prüfen, danndocker compose pull && docker compose run --rm web bundle exec rails db:migrate && docker compose up -d.
8. Nützliche Befehle
docker compose config -q # Konfiguration validieren
docker compose up -d
docker compose logs -f web
docker compose exec web bin/tootctl <command>
docker compose down
9. Mastodon-Befehle (Auswahl)
# Ausstehende Registrierungen anzeigen / freigeben
docker compose exec web bin/tootctl accounts approve --number 1
docker compose exec web bin/tootctl accounts approve --all
# Speicherplatz-Nutzung prüfen
docker compose exec web bin/tootctl media usage
# Passwort eines Accounts zurücksetzen
docker compose exec web bin/tootctl accounts modify BENUTZERNAME --reset-password
10. Weiterführende Doku
https://docs.joinmastodon.org/admin/config/ https://docs.joinmastodon.org/admin/tootctl/ https://docs.joinmastodon.org/admin/upgrading/
Lizenz
Copyright (C) 2026 Christian Säum – web-seo-consulting.eu
Dieses Projekt steht unter der GNU Affero General Public License, Version 3
oder (nach deiner Wahl) einer späteren Version (AGPL-3.0-or-later). Der
vollständige Lizenztext steht in der Datei LICENSE.
Du darfst die Software nutzen, weitergeben und – auch kommerziell – verkaufen.
Gibst du eine veränderte Fassung weiter oder betreibst du sie über ein
Netzwerk (z. B. als Dienst), muss deren vollständiger Quellcode ebenfalls
unter der AGPL-3.0-or-later verfügbar sein. Der Copyright-Hinweis und die
Nennung des ursprünglichen Autors dürfen nicht entfernt werden.
Diese Angabe betrifft nur die in diesem Repository enthaltenen eigenen Dateien (Konfiguration, Skripte, Anpassungen). Eingebundene Fremdsoftware und Container-Images unterliegen weiterhin ihren eigenen Lizenzen.