Woodpecker CI Installation and Configuration
Woodpecker CI is a container-native CI/CD system that integrates tightly with Gitea and Forgejo, running every pipeline step in Docker containers. This guide covers deploying the Woodpecker server and agents, writing pipeline YAML, managing secrets, and running multi-platform builds.
Prerequisites
- Docker and Docker Compose installed
- A running Gitea or Forgejo instance
- A domain for Woodpecker (e.g.,
ci.yourdomain.com) - An OAuth2 application registered in Gitea/Forgejo
Installing Woodpecker Server
First, create an OAuth2 application in Gitea:
- Go to User Settings > Applications > Manage OAuth2 Applications
- Name:
Woodpecker CI - Redirect URI:
https://ci.yourdomain.com/authorize - Save the Client ID and Secret
Create the server configuration:
mkdir -p /opt/woodpecker
cd /opt/woodpecker
# Generate a strong agent secret
AGENT_SECRET=$(openssl rand -hex 32)
echo "Agent secret: $AGENT_SECRET"
cat > /opt/woodpecker/.env << EOF
# Server settings
WOODPECKER_HOST=https://ci.yourdomain.com
WOODPECKER_OPEN=false # Disable open registration
WOODPECKER_ADMIN=your_gitea_username
# Agent communication
WOODPECKER_AGENT_SECRET=${AGENT_SECRET}
# Gitea OAuth2
WOODPECKER_GITEA=true
WOODPECKER_GITEA_URL=https://git.yourdomain.com
WOODPECKER_GITEA_CLIENT=YOUR_OAUTH_CLIENT_ID
WOODPECKER_GITEA_SECRET=YOUR_OAUTH_CLIENT_SECRET
# Database (use SQLite for small setups, PostgreSQL for production)
WOODPECKER_DATABASE_DRIVER=sqlite3
WOODPECKER_DATABASE_DATASOURCE=/var/lib/woodpecker/woodpecker.sqlite
EOF
Deploying Woodpecker Agents
Agents execute pipeline jobs. Deploy at least one agent:
cat > /opt/woodpecker/docker-compose.yml << 'EOF'
version: '3.8'
services:
woodpecker-server:
image: woodpeckerci/woodpecker-server:latest
container_name: woodpecker-server
ports:
- "127.0.0.1:8000:8000"
- "127.0.0.1:9000:9000" # gRPC for agents
volumes:
- ./data:/var/lib/woodpecker
env_file: .env
restart: unless-stopped
woodpecker-agent:
image: woodpeckerci/woodpecker-agent:latest
container_name: woodpecker-agent
command: 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=${WOODPECKER_AGENT_SECRET}
- WOODPECKER_MAX_WORKFLOWS=4
- WOODPECKER_BACKEND=docker
EOF
docker compose up -d
docker compose logs -f
Gitea and Forgejo Integration
Nginx reverse proxy for the Woodpecker UI:
server {
listen 443 ssl;
server_name ci.yourdomain.com;
ssl_certificate /etc/letsencrypt/live/ci.yourdomain.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/ci.yourdomain.com/privkey.pem;
location / {
proxy_pass http://127.0.0.1:8000;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}
After deploying, visit https://ci.yourdomain.com and log in via Gitea OAuth. Activate repositories from the Woodpecker UI by clicking the + button next to each repo.
Woodpecker automatically registers a webhook in Gitea when you activate a repository.
Pipeline Syntax
Create .woodpecker.yml (or .woodpecker/*.yml) in your repository root:
# .woodpecker.yml
steps:
- name: test
image: golang:1.21
commands:
- go test ./...
- name: build
image: golang:1.21
commands:
- go build -o app .
when:
branch: main
- name: docker-build
image: plugins/docker
settings:
repo: registry.yourdomain.com/myapp
tags:
- latest
- ${CI_COMMIT_SHA}
username:
from_secret: registry_username
password:
from_secret: registry_password
when:
branch: main
event: push
Pipeline with services (e.g., database for tests):
steps:
- name: test
image: node:20
commands:
- npm install
- npm test
environment:
DATABASE_URL: postgres://user:pass@postgres:5432/testdb
services:
- name: postgres
image: postgres:15
environment:
POSTGRES_USER: user
POSTGRES_PASSWORD: pass
POSTGRES_DB: testdb
Secrets Management
Add secrets in the Woodpecker UI under Repository > Settings > Secrets, or use the CLI:
# Install the Woodpecker CLI
curl -LO https://github.com/woodpecker-ci/woodpecker/releases/latest/download/woodpecker-cli_linux_amd64.tar.gz
tar xzf woodpecker-cli_linux_amd64.tar.gz -C /usr/local/bin/
chmod +x /usr/local/bin/woodpecker
# Configure CLI
export WOODPECKER_SERVER=https://ci.yourdomain.com
export WOODPECKER_TOKEN=your_api_token # From UI: User Settings > API Token
# Add a secret for a specific repo
woodpecker secret add \
--repository owner/repo-name \
--name registry_password \
--value "supersecretpassword"
# List secrets
woodpecker secret list --repository owner/repo-name
Reference secrets in pipelines:
steps:
- name: deploy
image: alpine
commands:
- echo "Deploying..."
environment:
SSH_KEY:
from_secret: deploy_ssh_key
Multi-Platform Builds
Build for multiple architectures using QEMU or dedicated agents:
# .woodpecker.yml
platform: linux/amd64
steps:
- name: build-amd64
image: golang:1.21
commands:
- GOARCH=amd64 go build -o app-amd64 .
- name: build-arm64
image: golang:1.21
environment:
GOARCH: arm64
GOOS: linux
commands:
- go build -o app-arm64 .
For native ARM builds, register an ARM agent with the same secret and label it:
# On the ARM machine, run an agent
docker run -d \
--name woodpecker-agent-arm \
-v /var/run/docker.sock:/var/run/docker.sock \
-e WOODPECKER_SERVER=ci.yourdomain.com:9000 \
-e WOODPECKER_AGENT_SECRET=your_agent_secret \
-e WOODPECKER_FILTER_LABELS="arch=arm64" \
woodpeckerci/woodpecker-agent:latest
Target it in your pipeline:
labels:
arch: arm64
steps:
- name: build
image: golang:1.21
commands:
- go build -o app .
Docker Compose Full Stack
Full production setup with PostgreSQL:
version: '3.8'
services:
woodpecker-server:
image: woodpeckerci/woodpecker-server:latest
ports:
- "127.0.0.1:8000:8000"
- "127.0.0.1:9000:9000"
volumes:
- ./data:/var/lib/woodpecker
environment:
- WOODPECKER_HOST=https://ci.yourdomain.com
- WOODPECKER_GITEA=true
- WOODPECKER_GITEA_URL=https://git.yourdomain.com
- WOODPECKER_GITEA_CLIENT=${GITEA_CLIENT}
- WOODPECKER_GITEA_SECRET=${GITEA_SECRET}
- WOODPECKER_AGENT_SECRET=${AGENT_SECRET}
- WOODPECKER_DATABASE_DRIVER=postgres
- WOODPECKER_DATABASE_DATASOURCE=postgres://woodpecker:woodpecker@db:5432/woodpecker?sslmode=disable
depends_on:
- db
restart: unless-stopped
woodpecker-agent:
image: woodpeckerci/woodpecker-agent:latest
volumes:
- /var/run/docker.sock:/var/run/docker.sock
environment:
- WOODPECKER_SERVER=woodpecker-server:9000
- WOODPECKER_AGENT_SECRET=${AGENT_SECRET}
depends_on:
- woodpecker-server
restart: unless-stopped
db:
image: postgres:15-alpine
environment:
- POSTGRES_DB=woodpecker
- POSTGRES_USER=woodpecker
- POSTGRES_PASSWORD=woodpecker
volumes:
- ./postgres:/var/lib/postgresql/data
restart: unless-stopped
Troubleshooting
Pipeline not triggering on push:
# Verify the webhook is registered in Gitea
# Repo > Settings > Webhooks - check for Woodpecker webhook
# Check Woodpecker server logs
docker compose logs woodpecker-server | grep -i webhook
# Re-activate the repository in Woodpecker UI to re-register the webhook
Agent not connecting:
# Verify agent can reach server's gRPC port
nc -zv ci.yourdomain.com 9000
# Check agent secret matches
docker compose logs woodpecker-agent | grep -i error
Pipeline step fails with "image not found":
# Agent needs Docker access - verify socket mount
docker compose exec woodpecker-agent docker ps
# For private registries, add credentials as secrets
# and use the docker plugin or set DOCKER_AUTH in environment
Secrets not available in pipeline:
Ensure secrets are added with the correct repository path (owner/repo, not just repo), and that from_secret key names match exactly.
Conclusion
Woodpecker CI provides a clean, container-native CI/CD experience that pairs naturally with Gitea and Forgejo. Its simple YAML pipeline syntax, built-in secrets management, and multi-agent architecture make it suitable for both small projects and larger teams. With all components running in Docker, updates and scaling are straightforward.


