Skip to main contentSkip to navigation
[email protected]
Client AreaSupport
Hosting Mammoth
HostingMammothYour Data, Our Responsibility
Home
Solutions
Hosting Services
Store
Pricing
About
Blog
API
Contact

Stay Ahead of the Curve

Get the latest insights on cybersecurity, AI innovations, and enterprise data solutions delivered to your inbox.

Hosting Mammoth
HostingMammothEnterprise Solutions

Enterprise-grade data solutions. Hosting, recovery, cybersecurity, and AI-powered services for businesses worldwide.

[email protected]
Sun - Fri, 9:00am - 5:00pm

Services

  • Cloud Hosting
  • Data Recovery
  • Cybersecurity
  • Legal Support
  • MSP Services
  • Web Development
  • AI Services
  • Free Server Migration

Hosting

  • VPS Hosting (NVMe SSD)
  • VDS Hosting (NVMe)
  • Storage VPS (High SSD)
  • GPU Servers
  • Managed Services
  • Cloud Firewall
  • Load Balancer
  • One-Click Apps
  • n8n Hosting
  • Object Storage
  • FAQ

Company

  • Store
  • Pricing
  • About Us
  • Locations
  • Blog
  • Testimonials
  • Contact
  • Affiliate Program
  • White-Label
  • Terms of Service
  • Privacy Policy
  • Browser Cookies
  • SLA

Support

  • Client Area
  • Submit Ticket
  • Knowledge Base
  • Server Status
  • API Documentation

© 2026 Hosting Mammoth. All rights reserved.

Knowledge Base
Getting StartedAccount ManagementVPS HostingGPU ServersStorage VPSCloud FirewallLoad BalancerServer ManagementBilling & PaymentsSupport & TicketsAffiliate ProgramReseller ProgramMarketplace & Appsn8n HostingManaged ServicesServer MigrationAPI & DevelopersSecurityTroubleshootingGlossaryInstall Guides
  1. Home
  2. /
  3. Support
  4. /
  5. Install Guides
  6. /
  7. How To Install Woodpecker Ci Ubuntu
GUIDEInstall Guides

How to Install Woodpecker CI on Ubuntu 24.04 VPS: Lightweight Open-Source Drone CI Fork

27 min read

How to Install Woodpecker CI on Ubuntu 24.04 VPS: Lightweight Open-Source Drone CI Fork

Continuous integration is the connective tissue of modern development, and most teams end up paying a surprising amount of money for it. SaaS CI platforms bill per build minute, per seat, and often per concurrent job, which punishes you exactly when velocity goes up. Self-hosting a CI server on your own VPS flips that cost curve upside down: you pay a flat monthly rate and run as many builds as your hardware can handle. This guide walks you through installing Woodpecker CI, a community-driven fork of Drone, on an Ubuntu 24.04 VPS. By the end, you will have a Docker Compose stack with a Woodpecker server and agent, OAuth login wired up to Gitea, an HTTPS reverse proxy with a valid certificate, and your first pipeline running on every push.

Need a server first? Deploy Ubuntu 24.04 on our CloudCore Professional plan and have Woodpecker CI online in well under an hour.

Table of Contents

  • What is Woodpecker CI?
  • Why Self-Host CI Instead of Using Drone Cloud or GitHub Actions?
  • Prerequisites
  • Step 1: Update System Packages
  • Step 2: Install Docker and Docker Compose
  • Step 3: Create an OAuth Application on Your Git Backend
  • Step 4: Write the Docker Compose Stack
  • Step 5: Boot Woodpecker and Become the First Admin
  • Step 6: Commit Your First .woodpecker.yml Pipeline
  • Step 7: Expose Woodpecker via Nginx with TLS
  • Step 8: Register Additional Agents
  • Step 9: Use Secrets, Matrix Builds, and Plugins
  • Running Woodpecker on Kubernetes
  • Troubleshooting
  • FAQ
  • Next Steps
  • What is Woodpecker CI?

    Woodpecker CI is a simple, lightweight, open-source continuous integration engine driven by a YAML pipeline file in your repository. It was forked from Drone 0.8 in 2019 after Drone's maintainers moved the project onto a business-source license, and has since grown into a fully independent CI system with active community development under the Apache 2.0 license.

    Architecturally, Woodpecker has two moving parts. The server is a single Go binary that talks to your Git forge over OAuth, receives webhooks, stores pipeline state in SQLite or Postgres, and serves the web UI. Agents are separate processes that connect outbound to the server, pull pending jobs, and execute pipeline steps. Each step runs inside a container, so your build environment is reproducible and throwaway. Because the agent connects out to the server (not the other way around), you can place agents anywhere on the internet -- in a different region, behind NAT, on a beefier build box -- and they will register themselves automatically.

    The pipeline definition lives in a file called .woodpecker.yml (or a directory .woodpecker/ holding multiple workflow files) at the root of each repository. The syntax will look instantly familiar to anyone coming from Drone, GitLab CI, or GitHub Actions: a list of named steps, each with an image, commands, and optional triggers. Woodpecker resolves the DAG automatically and runs steps in parallel where possible.

    Out of the box Woodpecker supports Gitea, Forgejo, GitHub, GitLab, and Bitbucket as Git forges, with OAuth2 for authentication. It supports Docker, local, and Kubernetes execution backends, matrix builds for testing multiple language or OS versions in parallel, secrets with per-repository or organization-wide scoping, cron schedules for nightly builds, and a mature plugin ecosystem covering Docker registry pushes, SSH deployments, Slack notifications, S3 uploads, and dozens more.

    Why Self-Host CI Instead of Using Drone Cloud or GitHub Actions?

    Hosted CI platforms are fine for hobby projects, but once a team starts shipping daily they bring real friction.

    • Predictable, flat cost -- A VPS costs the same whether you run 10 builds a week or 1,000. GitHub Actions charges per minute beyond the free tier; CircleCI meters credits; Drone Cloud charges per seat. Self-hosted Woodpecker lets a five-person team run continuous builds around the clock for the price of a single VPS.
    • Full control over the build environment -- You pick the kernel, the Docker version, the amount of disk available for build caches, and the network routes to your private registries. Hosted runners give you a sandbox; a self-hosted agent is your machine.
    • Secrets never leave your perimeter -- Production deployment keys, signing certificates, and customer credentials stay on hardware you control. No third-party breach can leak them.
    • Fast access to private resources -- Agents that need to deploy to a private Kubernetes cluster, push to a local Docker registry, or run integration tests against a VPC database work without VPN acrobatics when they share a network with those resources.
    • No minute caps or queue waits -- Public runners get congested. Your own agent is idle the instant your last build finishes.
    • Freedom from vendor lock-in -- Woodpecker is Apache-licensed and community-governed. No single company can change the pricing model, sunset a feature, or relicense it out from under you the way Drone's upstream did.

    CI Cost Comparison at Typical Team Scale

    ScenarioGitHub ActionsDrone CloudCircleCISelf-Hosted Woodpecker
    Monthly cost (5 devs, ~3k min/mo)~$30-50/moPaid tier + per-seat~$30-100/moEUR 19.99/mo (flat)
    Concurrent jobs20 (Team)2-54-30Hardware-limited
    Build minute capYesYesYesNone
    Private network accessSelf-hosted runner requiredSelf-hosted requiredSelf-hosted requiredNative
    Custom OS/kernelLimitedLimitedDocker onlyFull
    Secrets exfiltration riskThird-partyThird-partyThird-partyIn-house
    Once your team pushes past a few thousand build minutes a month, the self-hosted bill pays for itself in the first week and everything after is margin.

    Prerequisites

    Before you begin, make sure you have:

    • A VPS running Ubuntu 24.04 LTS with root or sudo access.
    • SSH access to your server.
    • A domain pointed at your server's public IP (for example ci.example.com). OAuth callbacks require a resolvable hostname, so plain IPs will not work cleanly.
    • At least 4 GB of RAM (8-12 GB is strongly recommended once you start running real pipelines in parallel).
    • An account on a supported Git forge -- Gitea, Forgejo, GitHub, GitLab, or Bitbucket -- where you can create an OAuth application.
    Recommended Plan: CloudCore Professional
    >
    Woodpecker itself is tiny, but build pipelines are hungry. Node test suites, Rust compilation, Docker-in-Docker, and Playwright all consume CPU and RAM aggressively. We recommend the CloudCore Professional plan:
    >
    - 6 vCPU cores
    - 12 GB RAM
    - 100 GB NVMe SSD
    - Unmetered bandwidth
    - EUR 19.99/month
    >
    This gives you plenty of headroom for the server, at least two concurrent agents, and layer caching on disk. If you want Woodpecker to live next to a Git forge on the same box, pair this with our Gitea install guide first.

    Connect to your server via SSH before continuing:

    bash
    ssh root@your-server-ip

    Step 1: Update System Packages

    Keeping the base image current matters even more on a CI host, because your agents will be pulling images, writing caches, and touching the kernel's container subsystems constantly. Start with a clean slate.

    bash
    sudo apt update && sudo apt upgrade -y

    Expected output (abbreviated):

    text
    Hit:1 http://archive.ubuntu.com/ubuntu noble InRelease
    Reading package lists... Done
    Building dependency tree... Done
    Calculating upgrade... Done
    0 upgraded, 0 newly installed, 0 to remove and 0 not upgraded.

    If the kernel was updated, reboot now:

    bash
    sudo reboot

    Step 2: Install Docker and Docker Compose

    Woodpecker runs as two containers, and each pipeline step runs in its own container, so Docker Engine plus the Compose plugin is mandatory. If you do not already have Docker installed, follow our dedicated Docker install guide for a full walkthrough. The short version is below.

    Remove any old Docker-related packages shipped by Ubuntu:

    bash
    sudo apt remove -y docker docker-engine docker.io containerd runc || true

    Install the prerequisites and add Docker's official repository:

    bash
    sudo apt install -y ca-certificates curl gnupg lsb-release
    sudo install -m 0755 -d /etc/apt/keyrings
    curl -fsSL https://download.docker.com/linux/ubuntu/gpg | \
      sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg
    sudo chmod a+r /etc/apt/keyrings/docker.gpg

    echo \ "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] \ https://download.docker.com/linux/ubuntu $(. /etc/os-release && echo "$VERSION_CODENAME") stable" | \ sudo tee /etc/apt/sources.list.d/docker.list > /dev/null

    Install Docker Engine plus the Compose plugin:

    bash
    sudo apt update
    sudo apt install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin

    Verify both components:

    bash
    docker --version
    docker compose version

    Expected output:

    text
    Docker version 27.1.2, build 241b696
    Docker Compose version v2.29.2

    Enable the service so it comes back after reboots:

    bash
    sudo systemctl enable --now docker

    Step 3: Create an OAuth Application on Your Git Backend

    Woodpecker does not manage its own user database. Authentication is delegated entirely to your Git forge, and the admin list is keyed by forge usernames. You therefore need to register an OAuth2 application on whichever forge you plan to integrate.

    This guide uses Gitea as the primary example because it is the most common self-hosted pairing. The same callback pattern applies to GitHub and GitLab.

    Gitea

  • Sign in to your Gitea instance. If you have not installed it yet, follow our Gitea install guide.
  • Click your avatar and open Settings -> Applications -> Manage OAuth2 Applications.
  • Click Create a new OAuth2 Application.
  • Fill in:
  • - Application Name: Woodpecker CI - Redirect URI: https://ci.example.com/authorize
  • Save, then copy the Client ID and Client Secret -- you will need them in the next step.
  • Need push-to-main pipelines from Gitea's own Actions as well? See our companion guide: How to Install Gitea Actions on Ubuntu.

    GitHub

    In GitHub, go to Settings -> Developer settings -> OAuth Apps -> New OAuth App and set the callback URL to https://ci.example.com/authorize. The rest of the flow is identical.

    GitLab

    In GitLab, open User Settings -> Applications, create a new application with api and read_user scopes, and use https://ci.example.com/authorize as the redirect URI.

    Keep the Client ID and Secret handy. The next step consumes them.

    Step 4: Write the Docker Compose Stack

    Create a working directory for Woodpecker and its persistent data:

    bash
    sudo mkdir -p /opt/woodpecker
    sudo chown "$USER":"$USER" /opt/woodpecker
    cd /opt/woodpecker

    Generate a strong shared secret. This value authenticates agents to the server, so treat it like a password:

    bash
    openssl rand -hex 32

    Copy the output -- you will paste it into both services below.

    Create a .env file for secrets so they stay out of the Compose file:

    bash
    cat > .env <<'EOF'
    

    --- Git forge OAuth (Gitea example) ---

    WOODPECKER_GITEA=true WOODPECKER_GITEA_URL=https://git.example.com WOODPECKER_GITEA_CLIENT=paste-your-gitea-client-id WOODPECKER_GITEA_SECRET=paste-your-gitea-client-secret

    --- Shared cluster secret (openssl rand -hex 32) ---

    WOODPECKER_AGENT_SECRET=paste-the-generated-hex-string

    --- Admin users (comma-separated forge usernames) ---

    WOODPECKER_ADMIN=your-gitea-username

    --- Public URL of the server ---

    WOODPECKER_HOST=https://ci.example.com EOF chmod 600 .env

    If you are using GitHub instead, replace the Gitea block with:

    bash
    WOODPECKER_GITHUB=true
    WOODPECKER_GITHUB_CLIENT=...
    WOODPECKER_GITHUB_SECRET=...

    And for GitLab:

    bash
    WOODPECKER_GITLAB=true
    WOODPECKER_GITLAB_URL=https://gitlab.com
    WOODPECKER_GITLAB_CLIENT=...
    WOODPECKER_GITLAB_SECRET=...

    Now create the Compose file itself:

    bash
    cat > docker-compose.yml <<'EOF'
    services:
      woodpecker-server:
        image: woodpeckerci/woodpecker-server:latest
        container_name: woodpecker-server
        restart: unless-stopped
        ports:
          - "127.0.0.1:8000:8000"   # HTTP (Nginx will front this)
          - "9000:9000"             # gRPC for remote agents
        volumes:
          - woodpecker-server-data:/var/lib/woodpecker/
        environment:
          - WOODPECKER_OPEN=true
          - WOODPECKER_HOST=${WOODPECKER_HOST}
          - WOODPECKER_ADMIN=${WOODPECKER_ADMIN}
          - WOODPECKER_AGENT_SECRET=${WOODPECKER_AGENT_SECRET}
          # Git forge (pass through whichever block you set in .env)
          - WOODPECKER_GITEA=${WOODPECKER_GITEA:-}
          - WOODPECKER_GITEA_URL=${WOODPECKER_GITEA_URL:-}
          - WOODPECKER_GITEA_CLIENT=${WOODPECKER_GITEA_CLIENT:-}
          - WOODPECKER_GITEA_SECRET=${WOODPECKER_GITEA_SECRET:-}
          - WOODPECKER_GITHUB=${WOODPECKER_GITHUB:-}
          - WOODPECKER_GITHUB_CLIENT=${WOODPECKER_GITHUB_CLIENT:-}
          - WOODPECKER_GITHUB_SECRET=${WOODPECKER_GITHUB_SECRET:-}
          - WOODPECKER_GITLAB=${WOODPECKER_GITLAB:-}
          - WOODPECKER_GITLAB_URL=${WOODPECKER_GITLAB_URL:-}
          - WOODPECKER_GITLAB_CLIENT=${WOODPECKER_GITLAB_CLIENT:-}
          - WOODPECKER_GITLAB_SECRET=${WOODPECKER_GITLAB_SECRET:-}

    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 - woodpecker-agent-config:/etc/woodpecker environment: - WOODPECKER_SERVER=woodpecker-server:9000 - WOODPECKER_AGENT_SECRET=${WOODPECKER_AGENT_SECRET} - WOODPECKER_MAX_WORKFLOWS=4

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

    A few notes on the settings you see:

    • WOODPECKER_OPEN=true allows any user on your Git forge to sign in. Flip this to false on a private instance and add users explicitly via the admin UI.
    • WOODPECKER_ADMIN is a comma-separated list of forge usernames who get admin rights automatically on first login. Add yourself here.
    • Port 8000 is bound to 127.0.0.1 because Nginx will terminate TLS and proxy to it. Do not expose it publicly.
    • Port 9000 is the gRPC endpoint that agents connect to. If all your agents live on the same host, you can comment it out; remote agents need it.
    • WOODPECKER_MAX_WORKFLOWS=4 controls how many pipelines this agent runs concurrently. Tune to match your CPU count.
    • The docker.sock mount is how the agent spawns step containers. This grants root-equivalent access to the host, which is fine on a dedicated CI VPS but be aware of it.
    By default Woodpecker uses SQLite stored in the woodpecker-server-data volume, which is perfect for teams up to a few dozen users. For heavy usage switch to Postgres by adding WOODPECKER_DATABASE_DRIVER=postgres and WOODPECKER_DATABASE_DATASOURCE=... and adding a Postgres service.

    Step 5: Boot Woodpecker and Become the First Admin

    With the Compose file and .env in place, start the stack:

    bash
    cd /opt/woodpecker
    docker compose up -d

    Expected output:

    text
    [+] Running 3/3
     ! Network woodpecker_default             Created
     ! Volume "woodpecker_woodpecker-server-data"   Created
     ! Volume "woodpecker_woodpecker-agent-config"  Created
     ! Container woodpecker-server            Started
     ! Container woodpecker-agent             Started

    Verify both containers are healthy:

    bash
    docker compose ps

    Expected output:

    text
    NAME                IMAGE                                    STATUS          PORTS
    woodpecker-agent    woodpeckerci/woodpecker-agent:latest     Up 10 seconds
    woodpecker-server   woodpeckerci/woodpecker-server:latest    Up 10 seconds   127.0.0.1:8000->8000/tcp, 0.0.0.0:9000->9000/tcp

    Tail the logs to catch any misconfiguration early:

    bash
    docker compose logs -f woodpecker-server

    You should see lines like level=info msg="starting Woodpecker server..." and level=info msg="starting http server..." with no error stacks. Press Ctrl+C to stop following.

    We will finish the TLS setup in Step 7. For the moment, you can reach the UI by tunneling port 8000:

    bash
    ssh -L 8000:127.0.0.1:8000 root@your-server-ip

    Open http://localhost:8000 in your browser, click Login, and complete the OAuth dance. Because your username is in WOODPECKER_ADMIN, the top-right menu will show a Repositories list and an Admin settings area.

    Click Add Repository, pick a repo from your forge, and enable it. Woodpecker registers a webhook on that repo automatically.

    Step 6: Commit Your First .woodpecker.yml Pipeline

    Pipelines are code. Woodpecker looks for one or more YAML files in .woodpecker/ (or a single .woodpecker.yml) at the repo root. Create a minimal file in a repository you enabled above:

    yaml
    # .woodpecker.yml
    steps:
      - name: lint
        image: node:20-alpine
        commands:
          - npm ci
          - npm run lint

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

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

    Commit and push. Within a second or two the server receives the webhook, the agent picks up the job, and each step runs inside its own ephemeral container. Watch it live in the UI: you will see real-time log streaming, a DAG visualization, and the final status badge.

    A few pipeline patterns worth knowing from day one:

    • when: clauses control when a step runs. Common filters are branch, event (push, pull_request, tag, cron), path, and status.
    • depends_on: wires the DAG manually. Omit it and steps run in declaration order with implicit dependencies.
    • services: starts sidecar containers (databases, caches) visible to all steps in the same pipeline. Perfect for integration tests.
    • failure: ignore lets a step fail without marking the whole pipeline red -- useful for non-critical linters.
    The full reference lives at the Woodpecker docs.

    Step 7: Expose Woodpecker via Nginx with TLS

    OAuth callbacks over plain HTTP are a non-starter. Nginx plus Certbot gives you a clean TLS termination point.

    Install Nginx and the Certbot Nginx plugin:

    bash
    sudo apt install -y nginx certbot python3-certbot-nginx

    Create a reverse proxy configuration:

    bash
    sudo tee /etc/nginx/sites-available/woodpecker > /dev/null <<'EOF'
    server {
        listen 80;
        server_name ci.example.com;

    # Certbot will replace this block with an HTTPS redirect. 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;

    # Live log streaming requires long-lived connections proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection "upgrade"; proxy_buffering off; proxy_read_timeout 600s; proxy_send_timeout 600s; }

    client_max_body_size 50m; } EOF

    sudo ln -s /etc/nginx/sites-available/woodpecker /etc/nginx/sites-enabled/ sudo nginx -t && sudo systemctl reload nginx

    Now obtain a Let's Encrypt certificate:

    bash
    sudo certbot --nginx -d ci.example.com

    Certbot rewrites the config to listen on 443 with a valid cert, adds an HTTP-to-HTTPS redirect, and installs a systemd timer that renews certificates every 12 hours. Visit https://ci.example.com and confirm the padlock shows a green certificate.

    Finally, lock down the firewall so only the ports you actually need are reachable:

    bash
    sudo ufw allow OpenSSH
    sudo ufw allow 'Nginx Full'
    sudo ufw allow 9000/tcp  # Only if you plan to run remote agents
    sudo ufw enable

    If all your agents run on the same host, omit 9000/tcp; the agent talks to the server over the internal Docker network.

    Step 8: Register Additional Agents

    One agent is enough to prove the stack works, but in production you will want several so builds run in parallel without queuing. Woodpecker supports two registration modes: shared secret (what we configured in Step 4) and agent registration tokens.

    Shared Secret (Simplest)

    Any machine that knows WOODPECKER_AGENT_SECRET and can reach port 9000 on the server can register itself. On a second VPS with Docker installed, run:

    bash
    docker run -d --name woodpecker-agent \
      --restart unless-stopped \
      -v /var/run/docker.sock:/var/run/docker.sock \
      -e WOODPECKER_SERVER=ci.example.com:9000 \
      -e WOODPECKER_AGENT_SECRET=paste-the-same-hex-string \
      -e WOODPECKER_GRPC_SECURE=true \
      -e WOODPECKER_MAX_WORKFLOWS=4 \
      woodpeckerci/woodpecker-agent:latest

    WOODPECKER_GRPC_SECURE=true wraps the gRPC connection in TLS using the certificate on your Nginx (only works once you have set up a certificate on port 9000 too -- easiest way is a second server block in Nginx that proxies grpcs:// to grpc://127.0.0.1:9000).

    Agent Registration Tokens

    For more auditable setups, generate per-agent tokens in the UI. Open the admin area -> Agents -> Add Agent. The server prints a one-time registration token. Start the agent with:

    bash
    -e WOODPECKER_AGENT_TOKEN=paste-registration-token

    The agent exchanges the token for a long-lived credential on first connect, and you can revoke individual agents from the UI without rotating the cluster-wide secret.

    Tokens are the right choice once you have more than a handful of agents or agents running in environments with mixed trust levels.

    Step 9: Use Secrets, Matrix Builds, and Plugins

    With the basics in place, three features make Woodpecker genuinely competitive with the big CI SaaS platforms.

    Secrets

    Secrets are injected as environment variables into steps that request them. Add a secret in the UI (per repository or org-wide) named DOCKER_PASSWORD, then reference it in .woodpecker.yml:

    yaml
    steps:
      - name: publish
        image: plugins/docker
        settings:
          repo: example/app
          tags: latest
          username: your-docker-username
          password:
            from_secret: DOCKER_PASSWORD
        when:
          event: tag

    By default secrets are only exposed to trusted images (configured per-secret). This prevents a malicious PR from swapping the image and exfiltrating the value.

    Matrix Builds

    Matrix is a first-class feature. To test a Go library against three Go versions and two OSes in parallel:

    yaml
    matrix:
      GO_VERSION:
        - "1.21"
        - "1.22"
        - "1.23"
      OS:
        - linux
        - alpine

    steps: - name: test image: golang:${GO_VERSION}-${OS} commands: - go test ./...

    Woodpecker fans this out into six independent pipelines. Each runs concurrently up to the agent's WOODPECKER_MAX_WORKFLOWS cap, then queues the rest.

    Plugins

    Plugins are regular Docker images that follow a light convention: they read their configuration from environment variables prefixed with PLUGIN_. The official plugin directory lists community- and core-maintained plugins for Docker, Slack, S3, SSH, Git, Gitea releases, Kubernetes, and much more. Drone plugins almost always work unmodified because the prefix is identical.

    A typical deploy step looks like:

    yaml
    - name: deploy
        image: appleboy/drone-ssh
        settings:
          host: prod.example.com
          username: deploy
          key:
            from_secret: SSH_KEY
          script:
            - cd /opt/app && ./deploy.sh
        when:
          event: tag

    Running Woodpecker on Kubernetes

    If you already run a Kubernetes cluster, the Kubernetes backend lets the agent schedule each pipeline step as a native pod instead of a Docker container. The server can still run anywhere -- on a VPS, in the cluster, or elsewhere -- while the agent lives inside Kubernetes and drives the scheduler.

    The two settings that flip the switch are:

    yaml
    woodpecker-agent:
        environment:
          - WOODPECKER_BACKEND=kubernetes
          - WOODPECKER_BACKEND_K8S_NAMESPACE=woodpecker-builds
          - WOODPECKER_BACKEND_K8S_STORAGE_CLASS=fast
          - WOODPECKER_SERVER=ci.example.com:9000
          - WOODPECKER_AGENT_SECRET=${WOODPECKER_AGENT_SECRET}

    The agent needs a ServiceAccount with permission to create pods, PVCs, and secrets in that namespace. The official Kubernetes deployment guide ships a ready-made Helm chart. Once running, every step in every pipeline becomes a real pod -- which means node autoscaling, GPU scheduling, and Kubernetes-native RBAC all apply to your CI jobs without extra work.

    For small teams the Docker backend we configured in this guide is perfectly sufficient. The Kubernetes backend pays off once you are running hundreds of concurrent pipelines or want to share cluster capacity between CI and application workloads.

    Troubleshooting

    ProblemCauseSolution
    OAuth returns redirect_uri_mismatchCallback URL in the forge app does not match WOODPECKER_HOSTUpdate the OAuth app's redirect URI to ${WOODPECKER_HOST}/authorize exactly, including scheme and trailing path
    Agent logs rpc error: code = UnauthenticatedShared secret mismatchVerify both containers read the same WOODPECKER_AGENT_SECRET from .env; redeploy with docker compose up -d
    Pipeline stuck in pending foreverNo agent connected, or no agent with matching labelsCheck docker compose logs woodpecker-agent. If you use labels, ensure a step's labels: block matches an online agent
    Steps fail with docker: Cannot connect to the Docker daemonAgent missing /var/run/docker.sock mountConfirm the volumes: section on the agent mounts the host socket
    UI shows blank log panel during buildsNginx not configured for websocket/upgrade headersEnsure the proxy block contains proxy_set_header Upgrade and proxy_buffering off
    Certbot fails with connection refusedFirewall blocking port 80 during HTTP-01 challengesudo ufw allow 'Nginx Full' before rerunning Certbot
    docker pull rate-limited inside pipelinesAnonymous Docker Hub limitsAdd Docker Hub credentials as a secret and log in during the pipeline, or mirror images into a private registry
    Out-of-memory kills on big buildsDefault VPS swap is smallAdd 4 GB of swap: sudo fallocate -l 4G /swapfile && sudo chmod 600 /swapfile && sudo mkswap /swapfile && sudo swapon /swapfile

    Viewing Logs

    bash
    cd /opt/woodpecker
    docker compose logs -f woodpecker-server
    docker compose logs -f woodpecker-agent

    Press Ctrl+C to stop following. Per-build logs also live inside each step's container and are streamed to the UI in real time.

    FAQ

    Is Woodpecker CI a drop-in replacement for Drone CI?

    Yes, for most intents and purposes. Woodpecker forked Drone 0.8 before Harness.io relicensed Drone under a business-source license. Pipeline syntax is nearly identical, most Drone plugins work unchanged, and migration is usually a matter of renaming .drone.yml to .woodpecker.yml, pointing the OAuth callback at the new server, and reconfiguring secrets. The one area where the projects diverge is governance: Woodpecker is community-maintained under Apache 2.0, while Drone's licensing is now tied to Harness's commercial strategy.

    How much RAM does Woodpecker CI need?

    The server itself runs comfortably in under 256 MB. The agent's resource footprint is dominated by whatever your pipeline steps do inside their containers -- a Go compile is light, a Playwright suite is heavy. For small teams running typical Go, Node, or Python builds, a 4 GB VPS handles server plus one agent. For parallel pipelines, Docker-in-Docker steps, or multi-agent setups, plan for 8-12 GB such as our CloudCore Professional plan.

    Does Woodpecker support matrix builds?

    Yes. Woodpecker supports matrix pipelines out of the box. Declaring a matrix: section in .woodpecker.yml fans out one pipeline per combination, which is ideal for testing multiple language versions or target platforms in parallel. Each combination runs independently and the overall pipeline status reflects the combined result.

    Can Woodpecker run pipelines on Kubernetes?

    Yes. Woodpecker ships a Kubernetes backend that schedules each pipeline step as a native pod. Set WOODPECKER_BACKEND=kubernetes on the agent and it picks up jobs from the server while running the actual workload inside your cluster. This is the recommended approach once you are running hundreds of concurrent pipelines, need GPU scheduling, or want to share autoscaled nodes between CI and application workloads.

    How do I register additional agents?

    Generate an agent registration token in the server admin panel, then start a new agent container on any Docker host with WOODPECKER_SERVER and WOODPECKER_AGENT_TOKEN set. The agent registers itself on first connect and exchanges the one-time token for a long-lived credential. Shared-secret mode also works and is simpler for a homogeneous cluster, but per-agent tokens give you auditable revocation.

    How does Woodpecker compare to Gitea Actions, Jenkins, and GitHub Actions Self-Hosted Runners?

    Gitea Actions is a GitHub-Actions-compatible runner built into Gitea. It is perfect if you already run Gitea and want zero extra infrastructure -- but it is tightly coupled to Gitea and less featureful than Woodpecker for cross-forge setups. See our Gitea Actions install guide for that path.

    Jenkins is the old guard: extremely flexible via its plugin ecosystem, but XML-heavy, Groovy-heavy, and resource-heavy. Woodpecker's YAML-first, container-native model is a better fit for modern stacks.

    GitHub Actions self-hosted runners work well if you live entirely on GitHub, but they require installing a binary per runner, run jobs outside Docker by default, and are tied to GitHub's workflow syntax. Woodpecker gives you the same containerized isolation with a forge-agnostic server.

    For a lean, modern, container-first CI on your own infrastructure that supports Gitea, GitHub, GitLab, and Bitbucket with a single server, Woodpecker is hard to beat.

    Next Steps

    Now that Woodpecker is running on your VPS, here are high-value follow-ups:

    • Pair it with a self-hosted Gitea -- Run Gitea and Woodpecker side by side for an entirely in-house code-and-CI stack. Start with our Gitea install guide.
    • Add Gitea Actions for lightweight jobs -- Use Gitea's built-in runner for small per-repo tasks and Woodpecker for heavier cross-repo pipelines. Install Gitea Actions on Ubuntu.
    • Scale out agents on a second VPS -- Spin up a second Ubuntu box, install Docker, and register a remote agent. You will double your concurrency without touching the server.
    • Set up scheduled pipelines -- Woodpecker's cron feature runs nightly builds, dependency updates, and security scans. Define them in the UI under each repository's settings.
    • Front it with an identity provider -- If you want SSO beyond Git OAuth, place Authelia or Authentik in front of Woodpecker and require MFA for admin routes.
    • Add a container registry -- Pair Woodpecker with a self-hosted registry (Harbor, Forgejo, or Gitea's built-in one) so your build and publish steps push into infrastructure you control end-to-end.
    For the full reference on pipeline syntax, plugins, and deployment options, consult the official Woodpecker CI documentation.

    Want a pre-tuned VPS for CI workloads?
    >
    Our CloudCore Professional plan gives you the 6 vCPU / 12 GB RAM / NVMe profile that Woodpecker, Gitea, and a couple of build agents love. Unmetered bandwidth means no surprise bill when your pipelines pull 50 GB of images a day.
    >
    - 6 vCPU cores, 12 GB RAM, 100 GB NVMe
    - Unmetered bandwidth
    - EUR 19.99/month
    - Ubuntu 24.04 LTS deployed in under a minute
    >
    Deploy your CI VPS now and have Woodpecker building on every push by the end of your coffee break.

    Was this article helpful?

    ← Back to Install GuidesBrowse all categories →

    Still have questions?

    Contact Support →Submit a Ticket