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:

  1. Go to User Settings > Applications > Manage OAuth2 Applications
  2. Name: Woodpecker CI
  3. Redirect URI: https://ci.yourdomain.com/authorize
  4. 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.