Skip to main content

How to Self-Host Woodpecker CI: Lightweight CI/CD Pipelines 2026

·OSSAlt Team
woodpecker-cidroneci-cdpipelinesself-hostingdockergitea2026

TL;DR

Woodpecker CI (Apache 2.0, ~4K GitHub stars, Go) is a community fork of Drone CI that remained open source after Drone moved to a commercial model. It pairs perfectly with Gitea or Forgejo for a complete self-hosted Git + CI/CD stack. Woodpecker uses Docker containers for every pipeline step — no agent installation needed on runners. Clean YAML syntax, extensive plugin library, minimal resource usage (~50MB RAM).

Key Takeaways

  • Woodpecker CI: Apache 2.0, community Drone fork — 100% open source, no limitations
  • Docker-based: Every pipeline step runs in an ephemeral Docker container
  • SCM integrations: Gitea, Forgejo, GitHub, GitLab, Bitbucket, Gitea
  • Plugins: Drone/Woodpecker plugin ecosystem (Docker build, SSH deploy, Slack notify, etc.)
  • RAM: ~50MB server + ~50MB per agent — extremely lightweight
  • Multi-arch: Runs pipelines on x86_64 and ARM

Woodpecker vs Drone vs Gitness vs GitHub Actions

FeatureWoodpecker CIDrone CEGitnessGitHub Actions
LicenseApache 2.0Apache 2.0Apache 2.0Proprietary
GitHub Stars~4K~11K~32K
Git hostingNo (separate)No (separate)Yes (built-in)GitHub only
Plugin ecosystem100+ (Drone compat)100+Limited20,000+
Multi-archYesYesPartialYes
Matrix buildsYesYesNoYes
RAM~50MB~50MB~100MB

Part 1: Docker Setup with Gitea

Woodpecker needs an OAuth2 app on your Git server.

Create OAuth2 App in Gitea

  1. Gitea → User Settings → Applications → Manage OAuth2 Applications
  2. Create application:
    • Name: Woodpecker CI
    • Redirect URI: https://ci.yourdomain.com/authorize
  3. Copy Client ID and Client Secret

Docker Compose

# docker-compose.yml
services:
  woodpecker-server:
    image: woodpeckerci/woodpecker-server:latest
    container_name: woodpecker-server
    restart: unless-stopped
    ports:
      - "8000:8000"     # Web UI + API
      - "9000:9000"     # Agent GRPC
    volumes:
      - woodpecker_data:/var/lib/woodpecker
    environment:
      WOODPECKER_OPEN: "false"    # Disable open registration
      WOODPECKER_ADMIN: "your-gitea-username"
      WOODPECKER_HOST: "https://ci.yourdomain.com"
      WOODPECKER_GITEA: "true"
      WOODPECKER_GITEA_URL: "https://git.yourdomain.com"
      WOODPECKER_GITEA_CLIENT: "${GITEA_CLIENT_ID}"
      WOODPECKER_GITEA_SECRET: "${GITEA_CLIENT_SECRET}"
      WOODPECKER_AGENT_SECRET: "${AGENT_SECRET}"    # openssl rand -hex 32

  woodpecker-agent:
    image: woodpeckerci/woodpecker-agent:latest
    container_name: woodpecker-agent
    restart: unless-stopped
    depends_on:
      - woodpecker-server
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock
    environment:
      WOODPECKER_SERVER: "woodpecker-server:9000"
      WOODPECKER_AGENT_SECRET: "${AGENT_SECRET}"
      WOODPECKER_MAX_WORKFLOWS: "4"    # Concurrent pipelines
      DOCKER_HOST: "unix:///var/run/docker.sock"

volumes:
  woodpecker_data:
# Generate agent secret:
openssl rand -hex 32

docker compose up -d

Part 2: HTTPS with Caddy

ci.yourdomain.com {
    reverse_proxy localhost:8000
}

Visit https://ci.yourdomain.com → log in with your Gitea account.


Part 3: GitHub Setup (Alternative SCM)

To use GitHub instead of Gitea:

environment:
  WOODPECKER_GITHUB: "true"
  WOODPECKER_GITHUB_CLIENT: "${GITHUB_CLIENT_ID}"
  WOODPECKER_GITHUB_SECRET: "${GITHUB_CLIENT_SECRET}"
  # Unset WOODPECKER_GITEA variables

Create GitHub OAuth App at github.com/settings/applications/new:

  • Homepage URL: https://ci.yourdomain.com
  • Authorization callback: https://ci.yourdomain.com/authorize

Part 4: Your First Pipeline

Add .woodpecker.yaml to your repository:

Node.js Pipeline

# .woodpecker.yaml
steps:
  - name: install
    image: node:20-alpine
    commands:
      - npm ci

  - name: lint
    image: node:20-alpine
    commands:
      - npm run lint
    depends_on: [install]

  - name: test
    image: node:20-alpine
    commands:
      - npm test
    depends_on: [install]

  - name: build
    image: node:20-alpine
    commands:
      - npm run build
    depends_on: [lint, test]
    when:
      branch: main

Python with Postgres

services:
  postgres:
    image: postgres:16-alpine
    environment:
      - POSTGRES_DB=testdb
      - POSTGRES_USER=test
      - POSTGRES_PASSWORD=test

steps:
  - name: test
    image: python:3.12-slim
    environment:
      DATABASE_URL: postgresql://test:test@postgres/testdb
    commands:
      - pip install -r requirements.txt
      - pytest --cov=app tests/

Docker Build and Push

steps:
  - name: docker-publish
    image: plugins/docker
    settings:
      registry: git.yourdomain.com
      repo: git.yourdomain.com/${CI_REPO}
      tags:
        - latest
        - ${CI_COMMIT_SHA}
      username:
        from_secret: registry_username
      password:
        from_secret: registry_password
    when:
      branch: main
      event: push

SSH Deploy

steps:
  - name: deploy
    image: appleboy/drone-ssh
    settings:
      host: prod.yourdomain.com
      username: deploy
      key:
        from_secret: ssh_private_key
      script:
        - cd /opt/myapp
        - docker compose pull api
        - docker compose up -d api
    when:
      branch: main
      event: push

Part 5: Matrix Builds

Run the same pipeline with multiple configurations:

matrix:
  NODE_VERSION:
    - 18
    - 20
    - 22
  OS:
    - alpine
    - bookworm

steps:
  - name: test
    image: node:${NODE_VERSION}-${OS}
    commands:
      - node --version
      - npm ci
      - npm test

This runs 6 parallel pipelines (3 node versions × 2 OS).


Part 6: Secrets Management

  1. Repository → Settings → Secrets → Add Secret
  2. Name: ssh_private_key, registry_password, etc.
  3. Value: stored encrypted

Reference in pipeline:

settings:
  password:
    from_secret: registry_password

Organization-level secrets: Admin → Orgs → select org → Secrets — available to all repos in the org.


Part 7: Plugin Ecosystem

Woodpecker is compatible with Drone plugins. Popular ones:

PluginImageUse
Docker Buildplugins/dockerBuild and push Docker images
SSHappleboy/drone-sshDeploy via SSH
Slackplugins/slackSlack notifications
Telegramappleboy/drone-telegramTelegram notifications
S3plugins/s3Upload to S3
Git Pushappleboy/drone-git-pushPush to another repo
Helmpelotech/drone-helm3Deploy with Helm

Part 8: Cron Schedules

Run pipelines on a schedule:

  1. Repository → Settings → Crons → Add Cron
  2. Name: nightly-tests
  3. Schedule: 0 2 * * * (2am daily)
  4. Branch: main

External Agents

Run agents on dedicated build servers:

# On build server:
docker run -d \
  --name woodpecker-agent \
  -v /var/run/docker.sock:/var/run/docker.sock \
  -e WOODPECKER_SERVER=ci.yourdomain.com:9000 \
  -e WOODPECKER_AGENT_SECRET=your-agent-secret \
  woodpeckerci/woodpecker-agent:latest

Maintenance

# Update:
docker compose pull
docker compose up -d

# Backup:
tar -czf woodpecker-backup-$(date +%Y%m%d).tar.gz \
  $(docker volume inspect woodpecker_woodpecker_data --format '{{.Mountpoint}}')

# Logs:
docker compose logs -f woodpecker-server woodpecker-agent

See all open source CI/CD tools at OSSAlt.com/categories/devops.

Comments