Lokales CI/CD für den Raspberry Pi: Automatisierung von Docker-Compose-Deployments
Teil 3 von 7 der Reihe Homelab auf dem Raspberry Pi

Inhalt
Die Verwaltung von Container-Infrastrukturen auf Edge-Geräten wie einem Raspberry Pi ist im Alltag oft mit wiederholten manuellen SSH-Sitzungen, händischen Image-Downloads und dem direkten Ausführen von Docker-Compose-Befehlen verbunden. Der Wechsel zu einer lokalen CI/CD-Pipeline automatisiert den gesamten Lebenszyklus selbstgehosteter Anwendungen – darunter Home Assistant, Nextcloud und maßgeschneiderte Python-Skripte – mit Git-Repositories, Webhook-Listenern und lokalen Deploy-Agenten.
1. Architektur von lokalem GitOps auf Edge-Hardware
Bei GitOps ist das zentrale Git-Repository für die gesamte Infrastrukturkonfiguration maßgeblich. Die Bereitstellungsdefinitionen werden in der Versionskontrolle gepflegt, statt YAML-Dateien direkt auf dem Server zu ändern.
- Deklarative Konfiguration: Sämtliche Dienste, Netzwerkdefinitionen und persistenten Speicherinhalte werden klar strukturiert in
docker-compose.yml-Dateien innerhalb eines Git-Repositories abgebildet. - Nachvollziehbarkeit & Rollbacks: Die Versionskontrolle bietet eine lückenlose Historie aller Konfigurationsänderungen, was im Fehlerfall ein sofortiges Zurücksetzen auf einen stabilen vorherigen Zustand ermöglicht.
- Wartungsfreier Betrieb: Der Edge-Server übernimmt den Stand des Repositorys automatisch. Bei normalen Updates sind deshalb keine interaktiven Shell-Eingriffe nötig.
2. Automatisierte Deployments über Webhook-Listener
Um manuelle SSH-Befehle vollständig abzulösen, können ressourcenschonende Webhook-Server (wie adnanh/webhook oder spezialisierte Automatisierungsdienstprogramme) direkt auf dem Raspberry Pi installiert werden. Diese Dienste empfangen eingehende HTTP-POST-Signale, die durch Push-Ereignisse im Code-Repository oder durch externe CI/CD-Systeme ausgelöst werden.
Ablauf der Webhook-Ausführung
- Validierung der Anfragen: Eingehende Webhook-Aufrufe werden kryptografisch über HMAC-Schlüssel auf Authentizität geprüft, um unbefugten Zugriff zu verhindern.
- Automatischer Git-Abruf: Nach erfolgreicher Prüfung startet der Hintergrunddienst einen Aktualisierungsprozess, um das lokale Arbeitsverzeichnis auf den Stand des externen Hauptbranches zu bringen.
- Rekonvergenz der Container: Der Ausführungsdienst startet den Befehl
docker compose up -d --remove-orphans, sodass ausschließlich veränderte Container-Images oder angepasste Konfigurationen neu erstellt werden.
3. Lebenszyklus-Management ohne SSH
Architekturen für die kontinuierliche Bereitstellung im Self-Hosting-Bereich profitieren von lokalen Agenten, die Container-Registries überwachen und die Erneuerung von Containern vollautomatisch durchführen.
- Automatisierte Überwachung von Image-Registries: Werkzeuge aus der Watchtower-Familie prüfen regelmäßig Image-Digests aktiver Container und starten Dienste automatisch neu, sobald aktuelle Builds bereitstehen. Das ursprüngliche Repository
containrrr/watchtowerwurde im Dezember 2025 archiviert, das ältere Projekt Ouroboros wird nicht mehr entwickelt; gepflegte Community-Forks wienickfedor/watchtowerlassen sich als direkter Ersatz einsetzen. - Lokale Build-Automatisierung: Für eigene Python-Anwendungen oder Mikroservices können Build-Prozesse per Dockerfile direkt auf der ARM-Architektur ausgeführt werden, um komplexe Cross-Compilation-Schritte zu vermeiden.
- Health Checks & automatische Wiederherstellung: Die Integration nativer Docker-Health-Checks stellt sicher, dass fehlerhafte Aktualisierungen umgehend identifiziert werden, ohne dass manuelle Log-Analysen erforderlich sind.
4. Sicherheitsaspekte für lokale CI/CD-Pipelines
Die Bereitstellung automatisierter Endpunkte im lokalen Netzwerk erfordert eine strikte Rechteentflechtung. Webhook-Dienste sollten niemals mit Root-Berechtigungen ausgeführt werden; stattdessen ist der Zugriff auf den Docker-Socket über dedizierte Gruppenrechte oder schreibgeschützte Socket-Proxys zu reglementieren. Zudem schützt ein restriktives Management-VLAN die Automatisierungs-Dienste vor unautorisierten Zugriffen aus anderen Subnetzen.
5. Push statt Pull: GitHub Actions und zeitgesteuerte Image-Updates
Beim Webhook-Muster holt sich der Raspberry Pi seinen Stand selbst. Die Gegenrichtung, bei der ein CI-Workflow das Deployment anstößt, ist mit GitHub Actions verbreitet und kommt in zwei Varianten vor. Die beiden unterscheiden sich vor allem darin, wie erreichbar das Gerät sein muss.
- Von GitHub gehosteter Runner über SSH: Der Workflow läuft in der Cloud von GitHub und baut eine SSH-Verbindung zum Pi auf. Dazu muss der SSH-Port des Pi aus dem Internet erreichbar sein, was einem rein lokalen Aufbau widerspricht.
- Selbst gehosteter Runner auf dem Pi: GitHub unterstützt selbst gehostete Runner unter Linux für ARM64 und ARM32. Der Runner braucht nur ausgehende HTTPS-Verbindungen über Port 443, ein eingehender Port muss nicht geöffnet werden. GitHub empfiehlt selbst gehostete Runner nur für private Repositories, weil ein Pull Request aus dem Fork eines öffentlichen Repositorys seinen Code auf dem Runner ausführen kann.
Den Deployment-Schlüssel beschränken
Bleibt die SSH-Variante im Einsatz, lässt sich der als Repository-Secret hinterlegte Schlüssel in der Datei ~/.ssh/authorized_keys eines eigenen Deployment-Benutzers auf eine einzige Aktion festlegen:
restrict,command="/usr/local/bin/deploy-stack" ssh-ed25519 AAAA… ci-deploycommand= führt bei jeder Anmeldung mit diesem Schlüssel das hinterlegte Skript aus und ignoriert den Befehl, den der Client mitschickt; die ursprüngliche Anfrage erreicht das Skript nur über die Umgebungsvariable SSH_ORIGINAL_COMMAND. restrict schaltet Port-, Agent- und X11-Weiterleitung ab, ebenso die PTY-Zuteilung und die Ausführung von ~/.ssh/rc. Ein abgeflossenes Secret kann dann ein Deployment auslösen und sonst nichts. Die Mitgliedschaft in der Gruppe docker ist dabei keine Beschränkung: Laut Docker-Dokumentation verleiht diese Gruppe Rechte auf Root-Niveau.
Watchtower-Updates zeitlich festlegen
Ohne weitere Angabe sucht Watchtower alle 86.400 Sekunden (24 Stunden) nach neuen Images. Ein festes Wartungsfenster setzt WATCHTOWER_SCHEDULE, ein Cron-Ausdruck mit sechs Feldern, dessen erstes Feld die Sekunden angibt; 0 0 4 * * * läuft täglich um 4:00 Uhr. Der Zeitplan braucht eine Zeitzone über TZ oder eine eingebundene /etc/localtime und lässt sich nicht mit WATCHTOWER_POLL_INTERVAL kombinieren. WATCHTOWER_CLEANUP=true entfernt nach einem Update die alten Images, damit SD-Karte oder SSD nicht mit der Zeit volllaufen. Mit WATCHTOWER_LABEL_ENABLE=true aktualisiert Watchtower nur Container mit dem Label com.centurylinklabs.watchtower.enable=true; damit entscheidet die Compose-Datei, welche Dienste Watchtower überhaupt anfasst.
Fragen und Antworten
Was macht docker compose up -d –remove-orphans mit Containern, deren Image sich nicht geändert hat, und mit den Volumes?
Compose vergleicht die gewünschte Beschreibung mit dem laufenden Zustand und fasst nur an, was abweicht. Gleiches Image und gleiche Definition heißt: Der Container läuft weiter, ohne Neustart. Volumes bleiben ohnehin bestehen, weil sie nicht Teil des Container-Lebenszyklus sind; entfernt werden sie nur auf ausdrücklichen Befehl, etwa mit docker compose down --volumes, und der kommt in diesem Ablauf nicht vor.
Aufmerksamkeit verdient --remove-orphans. Es entfernt Container, die dieses Compose-Projekt einmal angelegt hat und die in der Datei nicht mehr stehen. Das ist gewollt, solange der Projektname stabil ist. Ändert er sich – ein umbenanntes Verzeichnis genügt –, gelten die alten Container als fremd und bleiben stehen, während die neuen daneben starten.
Der umgekehrte Fall ist der teurere: Liegen zwei Projekte im selben Verzeichnis oder teilen sie sich versehentlich einen Namen, räumt der Lauf die Container des jeweils anderen ab. Deshalb ist der Projektname ausdrücklich zu setzen (name: in der Compose-Datei oder -p im Aufruf) und nicht dem Verzeichnisnamen zu überlassen.
Quellen
- Docker Compose documentation
- GitHub Docs: Self-hosted runners reference
- GitHub Docs: Adding self-hosted runners
- OpenBSD manual: sshd(8), AUTHORIZED_KEYS FILE FORMAT
- Docker: Linux post-installation steps for Docker Engine
- Watchtower (nicholas-fedor): Arguments
- robfig/cron v1.2.0: CRON Expression Format
Homelab auf dem Raspberry Pi
- Raspberry Pi 4 und 5: Vom SSD-Laufwerk booten über den Bootloader
- Unterbrechungsfreie Stromversorgung (USV) für Raspberry Pi 4 und 5: Die Top 5 Lösungen für anspruchsvolle Setups
- Lokales CI/CD für den Raspberry Pi: Automatisierung von Docker-Compose-Deployments
- Autonomer Docker-Compose-Heimserver mit Traefik, SSL und Watchtower
- Verzögerungsfreier lokaler Medienserver: Jellyfin auf Raspberry Pi 5 mit Hardware-Beschleunigung
- Raspberry Pi als gehärtetes WireGuard-VPN-Gateway mit Split-Tunneling
- Bare-Metal Performance Diagnostics: Prometheus & Grafana Dashboard für Raspberry Pi