Kokil Thapa - Professional Web Developer in Nepal
Freelancer Web Developer in Nepal with 15+ Years of Experience

Kokil Thapa is an experienced full-stack web developer focused on building fast, secure, and scalable web applications. He helps businesses and individuals create SEO-friendly, user-focused digital platforms designed for long-term growth.

Woodpecker CI: Lightweight Self-Hosted CI

By Kokil Thapa | Last reviewed: September 2026

Your team needs automated builds, but SaaS CI bills climb fast and shared runners feel slow. Woodpecker CI: Lightweight Self-Hosted CI fills that gap. It runs on a single VPS, speaks Drone-style YAML, and executes each step in an isolated Docker container. If you already ship Laravel apps with GitLab CI pipelines for Laravel, Woodpecker gives you the same mental model on hardware you control. This guide covers install, a real PHP pipeline, security, and how it stacks up against heavier options.

What Is Woodpecker CI: Lightweight Self-Hosted CI?

Woodpecker is a continuous integration server that listens for Git webhooks and runs pipeline steps inside containers. The project continued development after Drone's community edition stalled. The architecture stays deliberately small: one server process, one or more agents, and a database.

In practice, that means you can replace a bloated Jenkins install with a ~200 MB Docker stack on Ubuntu 22 or 24. I've maintained sister legal-tech sites on a shared EC2 box using GitLab CI and Deployer 7. Woodpecker fits the same profile when you want CI on the same machine without GitLab's full footprint.

Woodpecker CI ArchitectureGit HostGitHub / GiteaWoodpeckerServer + UIAgentDocker runnerPipeline Steps (Docker containers)CloneTestBuildDeploySQLite or PostgreSQL stores pipeline state
Woodpecker CI: Lightweight Self-Hosted CI — server receives webhooks, agents run containerised pipeline steps

Core components break down like this:

  • Server — Web UI, REST API, OAuth with your Git host, and webhook receiver.
  • Agent — Pulls jobs from the server and starts Docker containers for each step.
  • Database — SQLite for a single-node lab; PostgreSQL for production.
  • Pipeline file.woodpecker.yml at the repo root, checked into Git.

Woodpecker does not replace your Git host. It complements it, much like the patterns in our self-hosted CI runners setup and security guide. You keep code on GitHub or Gitea. Woodpecker only builds and tests it.

How Do You Install Woodpecker CI on Ubuntu with Docker?

A minimal production stack needs Docker Engine, Docker Compose, and two containers: server and agent. Budget roughly Rs 1,500–3,000/month (~USD 11–22) for a 2 vCPU VPS with 4 GB RAM. That handles small teams running PHP and Node builds comfortably.

Step 1: Prepare the host

Start on Ubuntu 22.04 or 24.04. Install Docker, enable UFW, and open ports 80 and 443 only. If you manage servers for Nepal clients, the same hardening applies as in Linux system administration work: fail2ban, automatic security updates, and non-root deploy users.

  1. Install Docker Engine and the Compose plugin from the official Docker repository.
  2. Create a dedicated system user, e.g. woodpecker, and add it to the docker group.
  3. Point a DNS A record at your server, e.g. ci.example.com.
  4. Issue TLS with Certbot or Caddy in front of the Woodpecker port.

Step 2: Create docker-compose.yml

Save this under /opt/woodpecker/docker-compose.yml. Adjust image tags to the current release on the Woodpecker installation docs.

services:
  woodpecker-server:
    image: woodpeckerci/woodpecker-server:v3
    ports:
      - "8000:8000"
    volumes:
      - woodpecker-server-data:/var/lib/woodpecker/
    environment:
      - WOODPECKER_OPEN=true
      - WOODPECKER_HOST=${WOODPECKER_HOST}
      - WOODPECKER_GITHUB=true
      - WOODPECKER_GITHUB_CLIENT=${WOODPECKER_GITHUB_CLIENT}
      - WOODPECKER_GITHUB_SECRET=${WOODPECKER_GITHUB_SECRET}
      - WOODPECKER_AGENT_SECRET=${WOODPECKER_AGENT_SECRET}
    restart: always

  woodpecker-agent:
    image: woodpeckerci/woodpecker-agent:v3
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock
    environment:
      - WOODPECKER_SERVER=woodpecker-server:9000
      - WOODPECKER_AGENT_SECRET=${WOODPECKER_AGENT_SECRET}
      - WOODPECKER_MAX_PROCS=2
    restart: always
    depends_on:
      - woodpecker-server

volumes:
  woodpecker-server-data:

Place secrets in /opt/woodpecker/.env. Never commit that file. Generate WOODPECKER_AGENT_SECRET with openssl rand -hex 32. Register an OAuth app on GitHub with callback URL https://ci.example.com/authorize.

Step 3: Start and verify

cd /opt/woodpecker
docker compose up -d
docker compose logs -f woodpecker-server

Open the UI, log in via GitHub, and activate a test repository. Push a commit. You should see a pipeline queued within seconds. If the agent never picks up jobs, check that WOODPECKER_AGENT_SECRET matches on both services and that the agent can reach the server on port 9000 internally.

Install FlowUbuntu VPSDocker installComposeServer + agentOAuthGitHub appActivateEnable repoFirst Pipeline TriggerGit pushWebhook firesQueue jobParse YAMLRun stepsDocker execTLS reverse proxy (Caddy/Nginx) sits in front of port 8000Use PostgreSQL when SQLite locks under concurrent builds
Four-step Woodpecker CI install path from bare Ubuntu VPS to a running pipeline

For Gitea or Forgejo, swap GitHub variables for WOODPECKER_GITEA_* equivalents. The agent model matches what you'd expect from Drone CI getting started—because Woodpecker forked from Drone CE.

How Do You Write a Woodpecker CI Pipeline for Laravel?

On production Laravel 12 or 13 apps, a sensible pipeline runs Composer install, static analysis, Pest or PHPUnit tests, and optionally deploys via SSH. PHP 8.3 is the minimum for Laravel 13; Laravel 12 needs PHP 8.2 or higher. Pin the image tag so builds stay reproducible.

Add .woodpecker.yml to your repo root:

when:
  - event: [push, pull_request]
    branch: [main, develop]

steps:
  - name: backend-tests
    image: composer:2.10
    environment:
      APP_ENV: testing
      DB_CONNECTION: sqlite
      DB_DATABASE: ":memory:"
    commands:
      - composer install --no-interaction --prefer-dist
      - cp .env.example .env
      - php artisan key:generate
      - php artisan test

  - name: frontend-build
    image: node:26-alpine
    commands:
      - npm ci
      - npm run build
    when:
      - event: push
        branch: main

  - name: deploy-production
    image: alpine:3.20
    environment:
      SSH_KEY:
        from_secret: deploy_ssh_key
      DEPLOY_HOST:
        from_secret: deploy_host
    commands:
      - apk add --no-cache openssh-client
      - install -m 600 -D /dev/null ~/.ssh/id_ed25519
      - echo "$SSH_KEY" > ~/.ssh/id_ed25519
      - ssh -o StrictHostKeyChecking=no deploy@$DEPLOY_HOST "cd /var/www/app && dep deploy production"
    when:
      - event: push
        branch: main

Store deploy_ssh_key and deploy_host in the Woodpecker UI under repository secrets. Follow the same discipline as CI/CD secrets management best practices: never echo secrets in logs, rotate keys quarterly, and scope deploy keys to one host.

Optimise Laravel builds

Three changes cut pipeline time on real projects:

On booking platforms like Adventure Third Pole Trek, CI must pass before Deployer swaps the release symlink. Woodpecker's deploy step can call the same dep deploy command your GitLab pipeline uses today. Swap the YAML syntax; keep the server-side flow.

Validate YAML locally before pushing. A malformed step name wastes five minutes of queue time and blocks your team. Use the JSON formatter or a YAML linter in a pre-commit hook if your editor lacks schema validation.

How Does Woodpecker CI Compare to GitLab CI, Drone, and Jenkins?

Pick Woodpecker when you want container-native pipelines without running an entire GitLab instance. Pick GitLab CI when you already host GitLab and want integrated registry, issues, and merge requests in one product.

CriteriaWoodpecker CIGitLab CIDrone CEJenkins
Resource footprintLow — 2 containersHigh — full GitLab stackLow — same modelMedium–high — JVM + plugins
Pipeline syntaxYAML, Docker stepsYAML, Docker/KubernetesYAML, Docker stepsJenkinsfile (Groovy)
Active maintenance (2026)Yes — active forkYes — vendor-backedStalled CE lineageYes — plugin ecosystem
Best fitSmall teams, own VPSAll-in-one DevOpsLegacy Drone usersEnterprise plugin needs
Laravel PHP supportExcellent via Docker imagesExcellentExcellentGood with config
Cost on 4 GB VPS~Rs 2,000/mo hosting onlyNeeds 8 GB+ for comfortSame as WoodpeckerVaries by plugin load

For a broader SaaS versus self-hosted lens, read GitHub Actions vs GitLab CI comparison. Woodpecker sits closest to the self-hosted column but with less ceremony than Jenkins.

CI Tool PositioningLow complexityHigh complexityResource useWoodpeckerLightweightDroneLegacy CEGitLab CIFull platformJenkinsPlugin heavyWoodpecker wins on simplicity for single-VPS Laravel teams
Woodpecker CI sits in the low-complexity, low-resource quadrant compared to GitLab CI and Jenkins

Buildkite follows a similar agent model but uses a proprietary server. See Buildkite scalable CI with your own agents if you need elastic scaling across many machines. Woodpecker stays simpler when one or two agents cover your workload.

How Do You Secure and Operate Woodpecker CI in Production?

Self-hosted CI is part of your attack surface. A compromised pipeline can exfiltrate secrets, push malicious images, or pivot into production SSH keys. Treat the CI host like a production server.

Harden the server

  • Disable WOODPECKER_OPEN after initial setup so random users cannot register.
  • Restrict OAuth to your organisation's GitHub org or Gitea team.
  • Run the agent on a dedicated host if pipelines execute untrusted fork PRs.
  • Keep Docker updated; agents mount /var/run/docker.sock, which equals root access.
  • Enable audit logging and ship logs to a central store.

Scan repos for leaked tokens before they reach CI. Pair Woodpecker with guidance from secrets scanning in Git and CI with Gitleaks. Add coverage thresholds using patterns from code coverage gates in CI once tests are stable.

Cache and registry

Pulling composer:2.10 and node:26-alpine on every build wastes bandwidth on Nepali VPS links. Configure a local registry mirror or follow self-host a Docker registry so agents pull from LAN speed. Combine with build caching to speed up CI builds for Composer and npm directories.

Backups and upgrades

Back up the Woodpecker data volume nightly. SQLite files live under /var/lib/woodpecker/. For PostgreSQL, use pg_dump cron jobs—the same approach I use on shared EC2 infrastructure for sister sites like Notary Kathmandu.

Upgrade by pinning a new image tag, running docker compose pull, and restarting. Read release notes on the Woodpecker GitHub releases page before jumping major versions. Roll back by reverting the tag if pipelines break.

Production Security LayersInternet trafficTLS + firewall (UFW)Ports 443 onlyWoodpecker server (OAuth, secrets store)WOODPECKER_OPEN=falseAgent host — isolated Docker, no production DB accessDeploy keys scoped to one environment
Layered security for Woodpecker CI: TLS edge, locked registration, and scoped deploy credentials

For blue-green or zero-downtime deploys after CI passes, wire your pipeline into the flow described in CI/CD blue-green deployment explained. Woodpecker only gates the deploy; Deployer or your shell script still performs the swap.

Ongoing ops belong in a support contract or your runbook. If you prefer someone else patches the server and rotates secrets, see support and maintenance services or domain registration and hosting for the full stack.

Key Takeaways

  • Woodpecker CI runs as two Docker containers—server plus agent—on a modest VPS with Drone-compatible YAML pipelines.
  • Connect GitHub or Gitea via OAuth, store secrets in the UI, and keep WOODPECKER_OPEN=false in production.
  • Laravel 12/13 pipelines need pinned PHP, Composer 2.10, and Node 26 LTS images with cached vendor/ and node_modules/.
  • Choose Woodpecker over GitLab CI when you already have a Git host and want minimal ops overhead.
  • Mounting the Docker socket on agents is convenient but dangerous—isolate untrusted fork builds on a separate agent host.
  • Back up the server data volume and test rollback before upgrading Woodpecker image tags.

People Also Ask

Is Woodpecker CI free to self-host?

Yes. Woodpecker is open source under the Apache 2.0 licence. You pay only for VPS hosting, typically Rs 1,500–3,000/month (~USD 11–22) for a capable build server. There is no per-seat or per-minute SaaS fee.

Can Woodpecker CI replace GitLab CI entirely?

It replaces the CI portion, not GitLab itself. You still need a Git host for repositories, merge requests, and code review. Teams that already run Gitea or GitHub get the biggest win because Woodpecker adds pipelines without a second heavy platform.

Does Woodpecker support monorepos and matrix builds?

Yes. Use matrix: blocks to run the same step across PHP 8.3 and 8.4, or filter pipelines with path: rules so only changed apps rebuild. Parallel steps run as separate containers on the agent until WOODPECKER_MAX_PROCS limits concurrency.

What happens to Drone CI pipelines when migrating to Woodpecker?

Most .drone.yml files convert with minor edits—rename the file to .woodpecker.yml and adjust plugin image namespaces from plugins/ to woodpeckerci/ where needed. Step syntax, secrets, and when conditions largely carry over.

Run Woodpecker CI on Your Own Metal

Woodpecker CI: Lightweight Self-Hosted CI earns its place when you want predictable builds, full data control, and a pipeline file in Git—without maintaining Jenkins plugins or a full GitLab stack. Start with one agent, a Laravel test pipeline, and strict secrets hygiene. Scale agents horizontally when queue times grow.

If you want help standing up CI on Ubuntu, wiring Deployer deploys, or migrating from GitLab CI, contact us or explore web development services. For related reading, browse integration testing in CI pipelines, build verification and quality gates, and the full CI/CD blog archive. You can also review how we ship production apps in the portfolio or learn more about my background with Laravel deployments since 2010.

Frequently Asked Questions

Woodpecker CI is an open-source, Docker-native pipeline server forked from Drone CE. It listens for Git webhooks and runs each pipeline step inside an isolated container on hardware you control.

Yes. Woodpecker is open source under Apache 2.0. You pay only for VPS hosting—typically Rs 1,500–3,000/month (~USD 11–22)—with no per-seat or per-minute SaaS fees.

Budget Rs 1,500–3,000/month (~USD 11–22) for a 2 vCPU VPS with 4 GB RAM. That handles small-team PHP and Node builds with no licence fees beyond hosting.

Start on Ubuntu 22.04 or 24.04 with Docker Engine and the Compose plugin. Create /opt/woodpecker/docker-compose.yml with woodpeckerci/woodpecker-server:v3 and woodpeckerci/woodpecker-agent:v3. Store GitHub OAuth credentials and WOODPECKER_AGENT_SECRET—generate the latter with openssl rand -hex 32—in /opt/woodpecker/.env, never in Git. Point DNS at the host, terminate TLS on ports 80 and 443 with Certbot or Caddy, run docker compose up -d, log in via GitHub, and activate a test repository. Push a commit and confirm the pipeline queues within seconds.

Woodpecker replaces the CI portion of your workflow, not your Git host. You still need GitHub, Gitea, or Forgejo for repositories, merge requests, and code review. Teams already on a lightweight Git host gain the most because Woodpecker adds containerised pipelines without running GitLab's full stack. I've maintained sister legal-tech sites with GitLab CI and Deployer 7; Woodpecker fits the same profile when you want CI on the same VPS with far less footprint than a complete GitLab install.

Add .woodpecker.yml at the repo root with when rules for push and pull_request on main and develop. Run backend-tests in a composer:2.10 container: composer install, copy .env.example, php artisan key:generate, and php artisan test against SQLite in memory. Use node:26-alpine for npm ci and npm run build on main pushes only. Pin image tags for reproducibility. PHP 8.3 is the minimum for Laravel 13; Laravel 12 needs PHP 8.2 or higher. Cache vendor/ and node_modules/ on the agent to cut build time on real projects.

Woodpecker sits in the low-complexity, low-resource quadrant—a ~200 MB two-container stack versus GitLab's full platform needing 8 GB+ RAM for comfort. Drone CE shares the same agent model but its community edition stalled; Woodpecker is actively maintained in 2026. Jenkins demands a JVM plus plugins and medium-to-high resource load. All support Laravel via Docker images, but Woodpecker's Drone-compatible YAML beats Jenkins ceremony and GitLab's all-in-one weight when you already own a Git host and want predictable Rs 2,000/month hosting costs.

Woodpecker's server process needs a database. SQLite suits a single-node lab—the files live under /var/lib/woodpecker/ inside the server data volume. For production, use PostgreSQL and back it up with pg_dump cron jobs, the same approach I use on shared EC2 infrastructure for sister sites like Notary Kathmandu. Schedule nightly backups of the Woodpecker data volume before upgrading image tags. Test rollback by reverting the pinned tag if pipelines break after an upgrade.

If builds queue but never start, verify WOODPECKER_AGENT_SECRET matches exactly on both woodpecker-server and woodpecker-agent in docker-compose.yml. Confirm the agent can reach the server internally on port 9000—the gRPC channel separate from the public UI port. Check docker compose logs -f woodpecker-agent for connection errors. After changing secrets or environment variables, restart both containers. Also confirm the agent container has /var/run/docker.sock mounted and the woodpecker system user belongs to the docker group.

Disable WOODPECKER_OPEN after initial setup so random users cannot register. Restrict OAuth to your organisation's GitHub org or Gitea team. Never commit /opt/woodpecker/.env. Store deploy keys in the Woodpecker UI as repository secrets, rotate quarterly, and scope SSH deploy keys to one host—never echo secrets in logs. Run agents on a dedicated host when pipelines execute untrusted fork PRs, because mounting /var/run/docker.sock equals root access. Keep Docker updated, enable audit logging, and pair Woodpecker with Gitleaks to scan repos for leaked tokens before they reach CI.

Most .drone.yml files convert with minor edits. Rename the file to .woodpecker.yml and adjust plugin image namespaces from plugins/ to woodpeckerci/ where needed. Step syntax, secrets, when conditions, and matrix blocks largely carry over because Woodpecker forked from Drone CE. Validate YAML locally before pushing—a malformed step name wastes five minutes of queue time. Run a test pipeline on a non-production repo before migrating production deploy steps that call Deployer via SSH.

Yes. Use matrix: blocks to run the same step across PHP 8.3 and 8.4, or add path: rules so only changed apps in a monorepo rebuild. Parallel steps spawn separate containers on the agent until WOODPECKER_MAX_PROCS limits concurrency—the default compose example sets this to 2. Split Pest test suites into parallel steps when test count exceeds a few hundred. On booking platforms like Adventure Third Pole Trek, CI must pass before Deployer swaps the release symlink, so faster parallel test runs directly shorten your deploy gate.

Woodpecker agents mount /var/run/docker.sock so each pipeline step runs in an isolated container. That convenience grants effective root on the host—any malicious pipeline or compromised fork PR can read secrets, spawn privileged containers, or pivot toward production SSH keys stored in repository secrets. Run untrusted fork builds on a separate agent host, keep Docker patched, and harden the CI VPS with UFW on ports 80 and 443, fail2ban, automatic security updates, and non-root deploy users—the same baseline applied to production servers.

Pick Woodpecker when you want container-native YAML pipelines on hardware you control without running an entire GitLab instance or maintaining Jenkins plugins. It fits small teams on a 4 GB VPS who already use GitHub or Gitea and need predictable builds with full data control and no per-minute SaaS fees. Choose GitLab CI when you want registry, issues, and merge requests in one product. Choose Jenkins when enterprise plugin ecosystems outweigh ops overhead. Buildkite follows a similar agent model but uses a proprietary server—Woodpecker stays simpler when one or two agents cover your workload.

Add a deploy-production step in .woodpecker.yml that runs only on push to main. Use alpine:3.20, install openssh-client, load SSH_KEY and DEPLOY_HOST from Woodpecker repository secrets, then ssh to the deploy user and run dep deploy production—the same Deployer 7 command a GitLab pipeline would call. Woodpecker gates the deploy; Deployer still performs the zero-downtime symlink swap server-side. Store deploy_ssh_key and deploy_host in the UI, scope the key to one host, and run migrations in a dedicated step only on deploy branches to avoid side effects on pull request builds.

Share this article

0 Comments

Leave a comment

Your email is not published. Comments appear once they have been read. Sign in to have your details filled in.

Quick Contact Options
Choose how you want to connect me: