Zum Hauptinhalt springen
S-EDV news
← Alle Anleitungen
📘 Anleitung Cloud / Hosting 19.07.2026 · 11 min Lesezeit

Woodpecker CI mit Docker installieren: Schlanke CI/CD-Engine für Gitea und Forgejo

Woodpecker CI ist ein ressourcenschonender CI/CD-Fork von Drone, der nativ mit Gitea und Forgejo zusammenarbeitet. Jeder Pipeline-Schritt läuft in einem eigenen Docker-Container – Server und Agent zusammen unter 50 MB RAM. Diese Anleitung zeigt den kompletten Aufbau per Docker Compose.

Woodpecker CI mit Docker installieren: Schlanke CI/CD Pipeline für Gitea und Forgejo mit Git Repository, Build, Test, Deployment und Linux Server.

Wer bereits Gitea oder Forgejo selbst hostet, steht früher oder später vor der Frage: Wie automatisiere ich Tests, Builds und Deployments, ohne eine schwergewichtige CI/CD-Plattform à la Jenkins aufsetzen zu müssen? Woodpecker CI ist die Antwort für alle, die schlanke, transparente und Docker-native Pipelines suchen. Als Community-Fork von Drone CI läuft Woodpecker mit zwei winzigen Containern (Server + Agent), verbraucht zusammen unter 50 MB RAM im Leerlauf und ist in rund 25 Minuten einsatzbereit – auch auf einem kleinen VPS oder einem ARM-Board. Jeder Pipeline-Schritt wird in einem eigenen, isolierten Docker-Container ausgeführt; kein gemeinsamer Zustand, kein verstecktes Side-Effect. Diese Anleitung richtet sich an Linux-Admins und ambitionierte Selfhoster, die Gitea oder Forgejo bereits betreiben und eine saubere, wartungsarme CI/CD-Lösung ohne Kubernetes-Overhead benötigen.

Voraussetzungen

  1. Docker Engine >= 20.10 und Docker Compose Plugin v2 (docker compose) auf einem Linux-Host (Ubuntu/Debian empfohlen). Falls noch nicht installiert: Docker und Docker Compose auf Linux installieren.
  2. Laufende Gitea- oder Forgejo-Instanz mit Admin-Zugang zum Erstellen einer OAuth-App. Für den Gitea-Einstieg: Gitea mit Docker: eigener Git-Server als GitHub-Alternative.
  3. Öffentlich erreichbare Domain oder URL für den Woodpecker-Server – für Webhooks von Gitea/Forgejo zwingend erforderlich. Ohne erreichbare URL empfangen deine Repos keine Push-Events.
  4. Mindestens 512 MB RAM frei (Empfehlung: 1 GB), 1 vCPU, ca. 1 GB Speicherplatz. Läuft auch auf ARM64 (Raspberry Pi, Apple Silicon).
  5. openssl zum Generieren des Agent-Secrets (auf jedem modernen Linux vorinstalliert).
  6. Für Produktion: ein Reverse Proxy mit TLS. Eine schnelle Option ist Caddy als Reverse Proxy mit automatischem HTTPS.

Schritt 1: Projektordner und Secret anlegen

Lege zunächst ein dediziertes Verzeichnis für den Woodpecker-Stack an und generiere das Agent-Secret. Dieses Secret authentifiziert den Agent gegenüber dem Server – beide müssen exakt denselben Wert tragen.

mkdir -p /opt/woodpecker
cd /opt/woodpecker
openssl rand -hex 32

Den ausgegebenen Hex-String (64 Zeichen) kopierst du gleich in die .env-Datei. Behandle ihn wie ein Passwort: nicht in ein öffentliches Repository einchecken, nicht per Chat verschicken. Bei einem Leak musst du ihn auf Server und Agent gleichzeitig rotieren.

Verifizieren: Das Verzeichnis /opt/woodpecker existiert (ls -la /opt/woodpecker) und openssl hat einen 64-stelligen Hex-String ausgegeben. Falls openssl fehlt: apt install openssl -y.

Schritt 2: OAuth-App in Gitea oder Forgejo erstellen

Woodpecker benötigt eine OAuth2-App in deiner Forge, um Benutzer anzumelden und Repository-Webhooks einzurichten. Der Ablauf ist in Gitea und Forgejo identisch.

  1. Melde dich als Admin in Gitea/Forgejo an und öffne: Benutzer-Einstellungen → Anwendungen → OAuth2-Anwendungen verwalten → Neue OAuth2-Anwendung erstellen.
  2. Vergib einen Namen (z. B. Woodpecker CI).
  3. Setze die Redirect/Callback-URI exakt auf: https://ci.example.com/authorize – ersetze ci.example.com durch deine tatsächliche Woodpecker-Domain. Groß-/Kleinschreibung und das abschließende Slash sind relevant; jede Abweichung führt zu einem 400 Bad Request – redirect_uri does not match-Fehler.
  4. Bestätige und notiere Client-ID und Client-Secret – das Secret wird nur einmal angezeigt.

Verifizieren: In Gitea/Forgejo unter „OAuth2-Anwendungen" erscheint der neue Eintrag mit der korrekten Redirect-URI. Client-ID und Client-Secret sind notiert.

Schritt 3: .env-Datei anlegen

Alle variablen und geheimen Werte wandern in eine .env-Datei im Projektordner. Docker Compose liest diese Datei automatisch ein. Lege sie unter /opt/woodpecker/.env an:

# Pflicht-Konfiguration
WOODPECKER_HOST=https://ci.example.com
WOODPECKER_AGENT_SECRET=<dein-64-stelliger-hex-string>
WOODPECKER_ADMIN=meinusername

# Initiale Einrichtung: Registrierung offen lassen, danach auf false setzen
WOODPECKER_OPEN=true

# Gitea-Forge (fuer Forgejo: WOODPECKER_FORGEJO=true + WOODPECKER_FORGEJO_* Variablen)
WOODPECKER_GITEA=true
WOODPECKER_GITEA_URL=https://gitea.example.com
WOODPECKER_GITEA_CLIENT=<oauth-client-id-aus-gitea>
WOODPECKER_GITEA_SECRET=<oauth-client-secret-aus-gitea>

# Agent-Konfiguration
WOODPECKER_MAX_WORKFLOWS=2

# Optional
WOODPECKER_LOG_LEVEL=info

Wichtige Hinweise zur .env-Datei:

  1. WOODPECKER_HOST muss das Schema (https://) enthalten und darf keinen abschließenden Schrägstrich haben.
  2. Für Forgejo ersetzt du den Gitea-Block durch: WOODPECKER_FORGEJO=true, WOODPECKER_FORGEJO_URL, WOODPECKER_FORGEJO_CLIENT und WOODPECKER_FORGEJO_SECRET. In Woodpecker v3 gibt es einen dedizierten Forgejo-Driver – ältere Anleitungen, die Forgejo mit WOODPECKER_GITEA=true konfigurieren, sind veraltet.
  3. Setze WOODPECKER_OPEN=false, sobald der erste Admin-Account angelegt ist.
  4. Berechtigungen einschränken: chmod 600 /opt/woodpecker/.env.

Verifizieren: cat /opt/woodpecker/.env zeigt alle gesetzten Werte; keine Zeile enthält noch einen Platzhalter wie <...>. stat /opt/woodpecker/.env zeigt Berechtigungen 600.

Schritt 4: compose.yaml erstellen

Lege die Compose-Datei unter /opt/woodpecker/compose.yaml an. Der Agent startet erst, wenn der Server-Healthcheck erfolgreich ist (condition: service_healthy). Der gRPC-Port 9000 wird nicht in ports: gemappt – er ist nur innerhalb des internen Docker-Netzwerks erreichbar.

services:
  woodpecker-server:
    image: woodpeckerci/woodpecker-server:v3
    container_name: woodpecker-server
    restart: unless-stopped
    ports:
      - "8000:8000"
    volumes:
      - woodpecker-server-data:/var/lib/woodpecker/
    networks:
      - woodpecker
    environment:
      - WOODPECKER_OPEN=${WOODPECKER_OPEN:-false}
      - WOODPECKER_ADMIN=${WOODPECKER_ADMIN}
      - WOODPECKER_HOST=${WOODPECKER_HOST}
      - WOODPECKER_AGENT_SECRET=${WOODPECKER_AGENT_SECRET}
      - WOODPECKER_GITEA=${WOODPECKER_GITEA:-true}
      - WOODPECKER_GITEA_URL=${WOODPECKER_GITEA_URL}
      - WOODPECKER_GITEA_CLIENT=${WOODPECKER_GITEA_CLIENT}
      - WOODPECKER_GITEA_SECRET=${WOODPECKER_GITEA_SECRET}
      - WOODPECKER_LOG_LEVEL=${WOODPECKER_LOG_LEVEL:-info}
    healthcheck:
      test: ["CMD", "wget", "-qO-", "http://localhost:8000/healthz"]
      interval: 30s
      timeout: 10s
      retries: 5
      start_period: 30s

  woodpecker-agent:
    image: woodpeckerci/woodpecker-agent:v3
    container_name: woodpecker-agent
    restart: unless-stopped
    command: agent
    depends_on:
      woodpecker-server:
        condition: service_healthy
    volumes:
      - woodpecker-agent-config:/etc/woodpecker
      - /var/run/docker.sock:/var/run/docker.sock
    networks:
      - woodpecker
    environment:
      - WOODPECKER_SERVER=woodpecker-server:9000
      - WOODPECKER_AGENT_SECRET=${WOODPECKER_AGENT_SECRET}
      - WOODPECKER_MAX_WORKFLOWS=${WOODPECKER_MAX_WORKFLOWS:-2}
      - WOODPECKER_LOG_LEVEL=${WOODPECKER_LOG_LEVEL:-info}

volumes:
  woodpecker-server-data:
  woodpecker-agent-config:

networks:
  woodpecker:

Eckdaten auf einen Blick

ParameterWertHinweis
Image Serverwoodpeckerci/woodpecker-server:v3Aktuell v3.15.0 (Mai 2026); :latest ist deprecated
Image Agentwoodpeckerci/woodpecker-agent:v3Multi-Arch: amd64 + arm64
Port Web-UI8000HTTP; für Produktion Reverse Proxy vorschalten
Port gRPC9000Nur intern im Docker-Netz – niemals nach außen öffnen
Volume Serverwoodpecker-server-dataEnthält SQLite-DB woodpecker.sqlite
Volume Agent/var/run/docker.sockPflicht für Docker-Backend
RAM (Idle)< 50 MBServer + Agent zusammen
DatenbankSQLite (Standard)PostgreSQL/MySQL optional

Verifizieren: Schnellprüfung mit: docker compose -f /opt/woodpecker/compose.yaml config – das Kommando gibt die aufgelöste Compose-Konfiguration aus, ohne etwas zu starten. Fehlende Variablen werden hier sofort sichtbar.

Schritt 5: Stack starten

Wechsle in den Projektordner und starte den Stack:

cd /opt/woodpecker
docker compose up -d

Docker zieht zunächst beide Images (beim ersten Start je nach Verbindung 30–90 Sekunden). Der Agent wartet automatisch, bis der Server-Healthcheck erfolgreich ist.

Verifizieren:

docker compose ps

Erwartete Ausgabe (nach ca. 60 Sekunden):

NAME                  IMAGE                                STATUS
woodpecker-server     woodpeckerci/woodpecker-server:v3    Up (healthy)
woodpecker-agent      woodpeckerci/woodpecker-agent:v3     Up

Zeigt der Server Up (starting) oder Up (unhealthy), warte weitere 30 Sekunden und prüfe die Logs:

docker compose logs woodpecker-server --tail=50

Anschließend den Health-Endpunkt direkt testen:

curl -s http://localhost:8000/healthz

Erwartete Antwort: OK oder ein JSON-Body mit "status":"ok". Ein HTTP-200 bestätigt, dass der Server läuft.

Schritt 6: Reverse Proxy und HTTPS einrichten (Produktion)

Für den Produktivbetrieb muss Port 8000 hinter einem Reverse Proxy mit TLS liegen, da Gitea/Forgejo die Webhooks an WOODPECKER_HOST sendet – und dieser Wert mit https:// beginnen sollte. Eine ausführliche Anleitung für Caddy (mit automatischem Let's-Encrypt-Zertifikat) findest du unter Caddy als Reverse Proxy einrichten. Minimale Caddy-Konfiguration für Woodpecker:

ci.example.com {
    reverse_proxy localhost:8000
}

Nach dem Aktivieren des Reverse Proxys muss WOODPECKER_HOST in der .env exakt der HTTPS-Domain entsprechen. Danach den Stack neu starten:

cd /opt/woodpecker
docker compose up -d --force-recreate

Verifizieren:

curl -I https://ci.example.com/healthz

Erwartetes Ergebnis: HTTP/2 200 (oder HTTP/1.1 200) ohne Zertifikatsfehler. Im Browser sollte https://ci.example.com das Woodpecker-Login laden.

Schritt 7: Erste Anmeldung und Admin-Einrichtung

Öffne https://ci.example.com im Browser. Du wirst zu Gitea/Forgejo weitergeleitet und nach dem Autorisieren der OAuth-App zurückgeleitet. Der erste angemeldete Benutzer, dessen Username in WOODPECKER_ADMIN eingetragen ist, erhält automatisch Admin-Rechte.

  1. Melde dich über den OAuth-Flow an.
  2. Navigiere zu Admin → Agents – dort sollte der Agent mit Status connected erscheinen.
  3. Unter Repositories kannst du ein vorhandenes Gitea-Repository aktivieren. Woodpecker legt automatisch einen Webhook im Repo an.
  4. Setze nach dem ersten Login WOODPECKER_OPEN=false in der .env und starte den Server neu: docker compose up -d woodpecker-server. Damit verhinderst du, dass sich unbekannte Benutzer selbst registrieren können.

Verifizieren: In der Admin-Oberfläche unter https://ci.example.com/admin/agents ist mindestens ein Agent mit Status connected gelistet. Die Logs zeigen keine authentication failed-Meldungen:

docker compose logs woodpecker-agent --tail=20

Erwarteter Logeintrag: connecting to server gefolgt von successfully connected.

Schritt 8: Erste Pipeline anlegen

Pipelines werden als .woodpecker.yaml (oder .woodpecker.yml) im Root des Repositories definiert. Hier ein minimales Beispiel, das zeigt, wie jeder Schritt in einem eigenen Container läuft:

when:
  - event: push

steps:
  - name: test
    image: alpine
    commands:
      - echo "Build laeuft in eigenem Container"
      - uname -a

  - name: lint
    image: node:20-alpine
    commands:
      - npm ci
      - npm run lint

Committe und pushe diese Datei in ein in Woodpecker aktiviertes Repository. Der Push-Event löst über den Gitea/Forgejo-Webhook sofort eine Pipeline aus.

Verifizieren: In der Woodpecker-Web-UI unter Pipelines erscheint ein neuer Build-Eintrag. Klicke ihn an – jeder Step zeigt seine Container-Logs in Echtzeit. Alternativ per CLI:

docker compose logs woodpecker-agent --tail=30 --follow

Beim Ausführen einer Pipeline siehst du Logzeilen wie starting step test, pulling image alpine und step test done.

Schritt 9: Updates und Backup

Woodpecker erscheint etwa alle 4–6 Wochen mit neuen Releases, inkl. Security-Patches. Das Update-Verfahren ist schlank:

cd /opt/woodpecker
docker compose pull
docker compose up -d

Bei Major-Version-Wechseln (z. B. v2 → v3) immer zuerst die Migrations-Dokumentation lesen – dort werden Breaking Changes und notwendige Datenbankmigrationen dokumentiert.

Für das Backup reicht es, das Docker-Volume woodpecker-server-data zu sichern. Es enthält die SQLite-Datei woodpecker.sqlite mit allen Repositories, Pipelines, Secrets und Benutzerkonten. Einfaches Backup:

docker compose stop woodpecker-server
tar -czf /backup/woodpecker-$(date +%F).tar.gz \
  $(docker volume inspect woodpecker_woodpecker-server-data \
    --format '{{.Mountpoint}}')
docker compose start woodpecker-server

Eine ausführliche Backup-Strategie für Docker-Volumes beschreibt 3-2-1-Backup-Strategie umsetzen.

Verifizieren:

docker compose ps

Nach dem Update zeigt docker inspect woodpecker-server | grep Image den neuen Image-Digest. Alle vorherigen Pipelines und Repository-Einstellungen sind unverändert erhalten.

Troubleshooting / Typische Fehler

  1. Agent erscheint nicht in der Admin-UI / „authentication failed" in den Logs: Server und Agent verwenden unterschiedliche WOODPECKER_AGENT_SECRET-Werte. Ursache ist meist ein Tippfehler in der .env oder zwei verschiedene .env-Dateien. Prüfen: docker compose logs woodpecker-agent | grep -i auth. Fix: Wert in der .env vereinheitlichen, Stack neu starten.
  2. „400 Bad Request – redirect_uri does not match" beim Login: Die in Gitea/Forgejo eingetragene OAuth-Callback-URL stimmt nicht exakt mit https://<WOODPECKER_HOST>/authorize überein. Häufige Ursachen: http statt https, fehlender Pfad /authorize, abschließender Schrägstrich. In Gitea/Forgejo die OAuth-App bearbeiten und URL korrigieren.
  3. Webhooks werden nicht ausgelöst (Gitea/Forgejo und Woodpecker auf demselben Host): Fehler in Gitea-Logs: dial: connection refused. Fix: Beide Services in dasselbe Docker-Netzwerk bringen. In Gitea/Forgejo app.ini unter [webhook] den Wert ALLOWED_HOST_LIST=external,loopback setzen und Gitea/Forgejo neu starten.
  4. „permission denied while trying to connect to the Docker daemon socket": Der Agent-Container darf nicht auf /var/run/docker.sock zugreifen. Fix: Sicherstellen, dass der Socket korrekt in der compose.yaml gemountet ist und der Docker-Daemon auf dem Host läuft. Bei Berechtigungsproblemen: ls -la /var/run/docker.sock – der Socket muss der Gruppe docker gehören.
  5. Server bleibt dauerhaft „unhealthy": Häufig falsch gesetzte Umgebungsvariablen (z. B. WOODPECKER_GITEA_URL nicht erreichbar vom Container aus) oder Port 8000 bereits belegt. Prüfen: docker compose logs woodpecker-server --tail=100 und ss -tlnp | grep 8000.
  6. Datenbank geht bei jedem Neustart verloren: Das Volume woodpecker-server-data ist nicht persistent definiert oder wurde mit docker compose down -v gelöscht. Niemals -v beim Stoppen verwenden, wenn die Daten erhalten bleiben sollen.
  7. Unerwartetes Major-Version-Upgrade nach docker compose pull: Das :latest-Tag ist in Woodpecker offiziell deprecated und kann einen Sprung von v2 auf v3 auslösen, der Breaking Changes enthält. Immer explizite Tags wie v3 oder v3.15 verwenden – so wie in der obigen compose.yaml angegeben.

Häufige Fragen

Brauche ich zwingend Gitea oder Forgejo?

Nein. Woodpecker unterstützt auch GitHub, GitLab, Bitbucket und weitere Forges über ein Plugin-System. Es ist jedoch speziell für Gitea und Forgejo optimiert und wird in diesen Setups am häufigsten eingesetzt. Die Konfiguration für andere Forges folgt demselben Prinzip mit entsprechend anderen Umgebungsvariablen.

Kann ich mehrere Agents betreiben?

Ja. Du kannst beliebig viele Agents mit demselben Server verbinden – sie müssen lediglich das identische WOODPECKER_AGENT_SECRET verwenden und den Server über WOODPECKER_SERVER=woodpecker-server:9000 (oder die öffentliche IP/Domain mit Port 9000, wenn der Agent auf einem anderen Host läuft) erreichen. Damit lassen sich horizontale Build-Kapazitäten aufbauen, ohne den Server anzufassen.

Wo wird die Pipeline-Konfiguration abgelegt?

Im Repository-Root als .woodpecker.yaml (oder .woodpecker.yml). Alternativ kannst du mehrere Dateien im Verzeichnis .woodpecker/ ablegen – Woodpecker führt alle darin enthaltenen YAML-Dateien aus. Die Datei wird vom Agent beim Start einer Pipeline direkt aus dem Repository geklont; es gibt keine zentrale Pipeline-Konfiguration im Server.

Läuft Woodpecker auf einem Raspberry Pi oder Apple Silicon?

Ja. Beide Images (woodpeckerci/woodpecker-server und woodpeckerci/woodpecker-agent) sind Multi-Arch-Images und unterstützen linux/amd64 sowie linux/arm64. Docker zieht automatisch die passende Architektur. Woodpecker wird sogar offiziell auf dem Raspberry Pi getestet.

Wie führe ich das Update auf eine neue Minor-Version durch?

Mit dem Tag v3 bekommst du automatisch das neueste v3.x.x-Release beim nächsten docker compose pull. Willst du präziser pinnen, nutze v3.15 (minor-stable) oder v3.15.0 (exakter Commit). Bei Minor-Updates reicht docker compose pull && docker compose up -d. Bei einem Major-Update (v2 → v3) unbedingt vorher die Migrationsdokumentation lesen.

Kann ich den Docker-Socket vermeiden?

Für das Standard-Docker-Backend ist /var/run/docker.sock erforderlich. Alternativ gibt es ein Kubernetes-Backend (für K8s-Cluster) und ein lokales Backend. Wer den Socket-Zugriff einschränken möchte, sollte sich die offizielle Dokumentation zu alternativen Backends ansehen. Allgemeine Härtungsmaßnahmen für Docker-Compose-Stacks beschreibt Docker Compose absichern: Secrets, Healthchecks, Non-Root und Read-Only.

Fazit

Woodpecker CI ist eine der schlanksten CI/CD-Lösungen, die du selbst hosten kannst. Zwei kleine Container, eine SQLite-Datenbank, eine .env-Datei – und du hast eine vollwertige Pipeline-Engine, die auf jedem VPS ab 512 MB RAM läuft. Der Docker-native Ansatz (jeder Step = eigener Container) sorgt für saubere Isolation ohne magische Build-Environments. Besonders für Teams, die bereits Gitea oder Forgejo betreiben, ist Woodpecker der logische nächste Schritt: OAuth-Integration, automatische Webhooks und eine übersichtliche Web-UI sind in unter 30 Minuten einsatzbereit. Wer mehr Buildkapazität benötigt, hängt einfach weitere Agents an – ohne Änderung am Server. Das macht Woodpecker zu einem ernsthaften, wartungsarmen CI/CD-Companion für KMU-Setups und Homelab-Infrastrukturen.

Weiterführende Anleitungen und Quellen

  1. Gitea mit Docker: eigener Git-Server als GitHub-Alternative
  2. Caddy als Reverse Proxy einrichten: Anfänger-Anleitung mit automatischem HTTPS
  3. Docker Compose absichern: Secrets, Healthchecks, Non-Root und Read-Only für den Produktivbetrieb
  4. 3-2-1-Backup-Strategie umsetzen: Anleitung mit Restic, USB-Disk und S3-Cloud
  5. Docker und Docker Compose auf Linux installieren (Ubuntu/Debian): die Self-Hosting-Grundlage

Offizielle Quellen: Woodpecker CI – Docker Compose Installation (offizielle Doku, v3/next) · Woodpecker CI – Releases auf GitHub · Forgejo-Forge-Integration (offizielle Doku)