| Filename | Latest commit message | Latest commit date |
|---|---|---|
| .forgejo/workflows | ||
| .idea | ||
| configs/php | ||
| volumes | ||
| .env | ||
| .gitignore | ||
| AGENTS.md | ||
| CICD-Actions.md | ||
| CLAUDE.md | ||
| docker-compose.yml | ||
| LICENSE | ||
| Projekt.md | ||
| README.md | ||
Pixelfed – WSC Standard Stack
Selbst gehosteter Pixelfed-Server (föderiertes Foto-/Video-Sharing über
ActivityPub, kompatibel mit Mastodon & Co.). Eigenständiges Image auf Basis von
serversideup/php:8.4-fpm-nginx (kein offizielles Pixelfed-Docker-Base-Image verfügbar), mit
MariaDB, Redis, Horizon (Queue) und dauerhaftem Laravel-Scheduler.
Template-Hinweis:
.enventhält nur Platzhalter (CHANGE_ME,example.*). Vor dem Einsatz anpassen, Passwörter gemäß Anleitung im.env-Kopf erzeugen (openssl rand -hex 32). Werte, die je in Git lagen, gelten als kompromittiert → rotieren.
Details zu Architektur/Abweichungen: siehe Projekt.md.
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 Mail-/SMTP-Zugang für Transaktions-E-Mails (Registrierungsbestätigung, Passwort-Reset, Invite-Links)
- Die Bind-Mount-Verzeichnisse
./volumes/storageund./volumes/bootstrap-cachemüssen demwww-data-User im Container gehören (UID/GID 33, Debian-Standard imserversideup/php-Image). Vor dem ersten Start einmalig:
Ohne diesen Schritt schlagen Migration, Storage-Symlink und Uploads mit "Permission denied" fehl.sudo chown -R 33:33 volumes/storage volumes/bootstrap-cache
2. .env konfigurieren
Vor dem ersten Start mindestens folgende Platzhalter ersetzen:
| Variable | Zweck |
|---|---|
COMPOSE_PROJECT_NAME |
Präfix für Container-/Image-Namen |
HOSTRULE, PRIMARY_DOMAIN, APP_URL, APP_DOMAIN, ADMIN_DOMAIN, SESSION_DOMAIN |
eigene Domain |
REDIS_PASSWORD, SQL_PASSWORD, SQL_ROOT_PASSWORD |
openssl rand -hex 32 |
APP_KEY |
siehe Schritt 4 (kann erst nach dem ersten Build erzeugt werden) |
MAIL_* |
SMTP-Zugangsdaten für Transaktions-E-Mails |
BACKUP_PATH |
Host-Verzeichnis für Backups |
OPEN_REGISTRATION=false und PF_USER_INVITES=false sind bereits Standard – Registrierung
läuft ausschließlich über von dir erzeugte Invite-Links (siehe Schritt 5).
3. Image bauen
Wichtig:
docker composeimmer aus diesem Stack-Verzeichnis starten (${PWD}-Mounts).
docker compose build app
Klont dabei den dev-Branch von pixelfed/pixelfed frisch und installiert alle
Composer-Abhängigkeiten in das Image (dauert ein paar Minuten). horizon und scheduler
nutzen anschließend automatisch dasselbe Image (kein eigener Build nötig).
4. APP_KEY erzeugen
Pixelfed liest die Konfiguration ausschließlich aus Container-Umgebungsvariablen – es gibt kein
persistentes .env im Container, daher funktioniert artisan key:generate nicht wie gewohnt.
Stattdessen den Key einmalig generieren lassen und manuell eintragen:
docker compose run --rm app php artisan key:generate --show
Ausgabe (inkl. base64:-Prefix) in der .env bei APP_KEY eintragen.
5. Starten
docker compose up -d sql redis
# ~30 Sekunden warten, bis MariaDB durchgestartet ist
docker compose up -d
Beim ersten Start migriert app automatisch die Datenbank (AUTORUN_LARAVEL_MIGRATION=true)
und legt den storage-Symlink an.
Einmalige Setup-Kommandos
docker compose exec app php artisan instance:actor # Pflicht für Föderation (ActivityPub)
docker compose exec app php artisan import:cities # Standort-Funktion
docker compose exec app php artisan passport:keys # Pflicht, da OAUTH_ENABLED=true
Admin-Account + Invite-Link erzeugen
# Eigenen Admin-Account anlegen
docker compose exec app php artisan user:create
# Invite-Link für neue Nutzer erzeugen (Registrierung ist geschlossen,
# nur DU kannst über diesen Befehl neue Accounts freigeben)
docker compose exec app php artisan admin:invite
6. Integrationen
Autoheal (Self-Healing)
autoheal=true auf app, horizon, scheduler, redis, sql (alle mit Healthcheck).
⚠️ Der
app-Healthcheck prüfthttp://localhost:8080/api/v1/instance. Liefert die App dauerhaft 5xx (z. B. ausstehende Migration beim Erst-Deploy), kann Autoheal denapp-Container in eine Restart-Schleife bringen → beim ersten Setup beobachten, ggf.autoheal-Label temporär entfernen.
Scheduler statt Cronjob
scheduler läuft dauerhaft mit php artisan schedule:work – ersetzt den sonst nötigen
Minuten-Cronjob (* * * * * artisan schedule:run). Kein Ofelia-Job dafür nötig.
DB-Backup (shyim/docker-backup) – Schedule 5-Feld
Label auf sql: type=mysql (MariaDB ist mysql-kompatibel), BACKUP_SCHEDULE
(Default alle 6 h), Retention BACKUP_RETENTION. Landet unter
/Daten/Backups/<sql-container>/db/….
Datei-Backup (Ofelia + backup-helper)
Da shyim nur named volumes sichert, läuft das Datei-Backup über den idle backup-helper:
Ofelia job-exec packt täglich 05:00 ein tar.gz von storage/ + bootstrap/cache/ nach
${BACKUP_PATH}/<projekt>/files/ (Retention 5 Tage).
7. Updates
Da der Anwendungscode im Image gebaut wird (kein Bind-Mount des kompletten Codes, siehe
Projekt.md), erfolgt ein Update über einen Rebuild statt git pull:
docker compose build --no-cache app
docker compose up -d
docker compose exec app php artisan migrate --force
8. Hinweise / Stolpersteine
- Ofelia vs. shyim Cron-Format: Ofelia = 6-Feld (
0 5 0 * * *), shyim = 5-Feld (5 0 * * *). Nicht verwechseln. - Kein
git pullim Container: Anwendungscode ist im Image gebacken, siehe Abschnitt 7. APP_KEYkann nicht perartisan key:generatepersistiert werden – manuell in.enveintragen (Schritt 4).- Registrierung:
OPEN_REGISTRATION=false+PF_USER_INVITES=false– einziger Weg für neue Accounts istphp artisan admin:invite. - Upload-Limits:
PHP_UPLOAD_LIMIT(Standard200M) steuertpost_max_sizeundupload_max_filesizegemeinsam;max_execution_time/max_file_uploadsliegen fest inconfigs/php/pixelfed.ini. - Bind-Mount-Rechte:
volumes/storage+volumes/bootstrap-cachemüssen UID/GID 33 gehören (siehe Abschnitt 1) – sonst "Permission denied" bei Migration/Uploads.
9. Nützliche Befehle
docker compose config -q # Konfiguration validieren
docker compose up -d
docker compose logs -f app
docker compose exec app php artisan <command>
docker compose exec app php artisan horizon:status
docker compose down
10. CI/CD
Die Pipeline (.forgejo/workflows/ci.yml) läuft self-hosted auf Forgejo, ganz ohne externe
Actions (Checkout per git clone). Es gibt keinen Code-Mirror nach Codeberg:
validate(jeder Push/PR aufmain+ Tags):docker compose config -q,yamllint(relaxed) undgitleaks(nur Working-Tree).release(nur Tagv*): baut ein schlankes Deploy-ZIP und hängt es an ein Forgejo-Release.
Release auslösen:
git tag v1.1
git push origin v1.1
Details siehe CICD-Actions.md.
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.