How to Self-Host Woodpecker CI: Lightweight CI/CD Pipelines 2026
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
| Feature | Woodpecker CI | Drone CE | Gitness | GitHub Actions |
|---|---|---|---|---|
| License | Apache 2.0 | Apache 2.0 | Apache 2.0 | Proprietary |
| GitHub Stars | ~4K | ~11K | ~32K | — |
| Git hosting | No (separate) | No (separate) | Yes (built-in) | GitHub only |
| Plugin ecosystem | 100+ (Drone compat) | 100+ | Limited | 20,000+ |
| Multi-arch | Yes | Yes | Partial | Yes |
| Matrix builds | Yes | Yes | No | Yes |
| RAM | ~50MB | ~50MB | ~100MB | — |
Part 1: Docker Setup with Gitea
Woodpecker needs an OAuth2 app on your Git server.
Create OAuth2 App in Gitea
- Gitea → User Settings → Applications → Manage OAuth2 Applications
- Create application:
- Name: Woodpecker CI
- Redirect URI:
https://ci.yourdomain.com/authorize
- 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
- Repository → Settings → Secrets → Add Secret
- Name:
ssh_private_key,registry_password, etc. - 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:
| Plugin | Image | Use |
|---|---|---|
| Docker Build | plugins/docker | Build and push Docker images |
| SSH | appleboy/drone-ssh | Deploy via SSH |
| Slack | plugins/slack | Slack notifications |
| Telegram | appleboy/drone-telegram | Telegram notifications |
| S3 | plugins/s3 | Upload to S3 |
| Git Push | appleboy/drone-git-push | Push to another repo |
| Helm | pelotech/drone-helm3 | Deploy with Helm |
Part 8: Cron Schedules
Run pipelines on a schedule:
- Repository → Settings → Crons → Add Cron
- Name:
nightly-tests - Schedule:
0 2 * * *(2am daily) - 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.