Woodpecker CI, with no SaaS anywhere in the path
Server and agents on one small machine in Prague or Covilha, wired to your own forge - the version of CI where nothing about the pipeline leaves infrastructure you can name.
Who it is for
A team that self-hosts everything else already
The forge is yours, the registry is yours, the deploy target is yours - and CI is the one hop that still leaves the building, which is exactly the hop that holds the source.
A Drone installation that stopped moving
Woodpecker is the fork that kept going and stayed Apache-2.0. The migration is small; the question is what to run it on.
A small team that wants CI to be boring
Two containers, a YAML file per repository, no control plane to learn and no per-minute counter to watch. It only needs somewhere to live.
What this shape gives you
- Server and agents as two containers on one host, which is small enough that CI stops being an infrastructure project.
- Any forge Woodpecker supports - GitHub, GitLab, Gitea, Forgejo or Bitbucket - including one you host yourself on the same network.
- Agents added as separate hosts later, all authenticating to the same server, when one machine stops being enough.
- Apache-2.0 throughout, so nothing in the pipeline depends on a vendor deciding to keep a free tier.
- A static IPv4 from AS204057, so the forge webhook and the deploy target both have one address to trust.
- Local NVMe for the workspace and the Docker layer cache, warm between pipelines.
Sized by what the pipeline actually does
Woodpecker itself is light: the server is a small Go process with an embedded database until you point it at Postgres, and the agent is a supervisor that starts containers. What consumes the host is the build, exactly as with any other runner - so size the machine from the heaviest job, and give the disk room for the layer cache that makes the second pipeline fast. The entry size below runs server and agent together comfortably for a small team. The one honest caveat: a flat monthly host costs the same on a week nobody pushes, where hosted CI minutes cost nothing.
How to install Woodpecker CI with Docker Compose
Server and agent on one host, wired to GitHub in this example. Swap the forge block for the Gitea, Forgejo, GitLab or Bitbucket variables if your forge is one of those.
-
Install Docker and Compose
sudo apt install docker.io docker-compose-v2 sudo systemctl enable --now dockerBoth containers and every pipeline step run on this daemon, so this is the only dependency the host needs.
-
Register an OAuth application on your forge
# GitHub: Settings -> Developer settings -> OAuth Apps -> New OAuth App # Homepage URL: https://ci.example.com # Authorization callback: https://ci.example.com/authorizeWoodpecker signs users in through the forge and reads repositories with that grant, so the client id and secret it hands back are what the server needs. Every supported forge has an equivalent form.
-
Generate the shared agent secret
openssl rand -hex 32The server and the agents authenticate to each other with this string, and Woodpecker documentation names this exact command for producing it. Keep it out of the compose file - put it in an .env file beside it.
-
Write the compose file
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} woodpecker-agent: image: woodpeckerci/woodpecker-agent:v3 command: agent restart: always depends_on: - woodpecker-server volumes: - woodpecker-agent-config:/etc/woodpecker - /var/run/docker.sock:/var/run/docker.sock environment: - WOODPECKER_SERVER=woodpecker-server:9000 - WOODPECKER_AGENT_SECRET=${WOODPECKER_AGENT_SECRET} volumes: woodpecker-server-data: woodpecker-agent-config:This is the upstream example. WOODPECKER_HOST is the public URL users and webhooks reach, port 8000 is the web interface and 9000 is the gRPC port agents connect on. WOODPECKER_OPEN=true lets anyone with a forge account sign in - turn it off once your team is registered.
-
Start it
docker compose up -d docker compose logs -f woodpecker-agentThe agent connects to the server with the shared secret and registers itself on first contact. If it loops on an authentication error, the two secrets do not match - most often because the .env file was not picked up.
-
Add a pipeline
when: - event: push steps: - name: smoke image: alpine:latest commands: - cat /etc/os-release - echo "built on $CI_MACHINE"Commit that as .woodpecker.yaml, enable the repository in the web interface so the webhook is created, and push. Each step is a container, which is why the agent needs the Docker socket.
Checked against the official documentation on 2026-08-15 — read the source
What we are not
- We are not the Woodpecker project. Woodpecker CI is an independent Apache-2.0 project and nothing here is endorsed by it. We sell the machine it runs on.
- We do not host your forge on this page. Gitea, Forgejo or a self-managed GitLab beside it is a straightforward second server, but it is a different size and a different quote.
- We do not write your pipelines. The YAML is yours; the host, the network and the address are ours.
- Woodpecker is not managed for you by default. You run the two containers, or you add our administration service and we keep them updated and backed up.
- Mounting the Docker socket into the agent gives pipeline steps a great deal of power over the host. That is how this design works, and it is a reason to keep the agent on a machine that does nothing else.
The parts teams discover late
- WOODPECKER_AGENT_SECRET has two modes. The same value on the server and every agent is a system token; a per-agent token comes from registering the agent in the interface first, which is what you want once more than one team can reach the host.
- Every sensitive variable also has a _FILE form, so the secret can be read from a mounted file instead of sitting in the compose file or the environment.
- The embedded database is fine to start and is still the thing to move first. Point the server at Postgres before the installation matters to anybody.
- WOODPECKER_OPEN=true means any account on the connected forge can sign in. It is convenient on day one and wrong by day thirty.
- The agent needs the Docker socket because every step is a container, which effectively grants the pipeline root on that host. Keep the agent host boring and separate.
- WOODPECKER_HOST must be the URL the forge can actually reach, not the internal one - the webhook is created against it, and a private address there produces pipelines that never trigger.
Hosted CI minutes versus a runner of your own
| Provider-hosted CI minutes | A runner on DCXV | |
|---|---|---|
| What you pay for | Every minute every job runs, for as long as the project exists | A flat monthly host, whatever the pipeline does that month |
| A build that gets slower | Costs more every month it stays slow | Costs nothing more - the machine is already paid for |
| Docker layer cache | Cold on each job unless you upload and download it yourself | Warm on local NVMe, between jobs and between days |
| Concurrency | A plan tier you upgrade | A number you set in your own configuration file |
| Where the checkout lands | A shared fleet, often in a region you cannot pin | One machine in Prague or Covilha, under a Cyprus contract |
| Outbound address | A large shared range that nothing can allowlist | One static IPv4 from AS204057, yours to allowlist |
| What the machine can hold | Whatever the runner image happens to ship with | Any toolchain, licence or fixture set you install once |
How this gets going on your host
Say what the pipeline does
The heaviest job you run today, how many you want running at once, and whether it builds container images. That is enough to size a host without guessing.
We hand over the machine
Prague or Covilha, root access and a static IPv4, in under ten minutes. Bring your own image if the runner is already baked into one.
Follow the walkthrough on this page
It is the vendor sequence, taken from their current documentation rather than from a blog post, and it takes a few minutes on a clean host.
Snapshot it, then add the second one
Take a snapshot once the first pipeline is green, so the next machine is a restore instead of a rebuild. A pool grows by adding hosts, not by making one enormous.
Why choose us
- Tier III certified facilities, 99.982% facility SLA
- Own network, AS204057, IPv4 and IPv6 dual-stack
- 24/7/365 support with ~10 minute average response
- Cyprus company, EU jurisdiction, GDPR-native since 2007
FAQ
- What is Woodpecker CI?
A lightweight Apache-2.0 CI engine, forked from Drone, that runs as a server plus one or more agents. Each pipeline step is a container, pipelines are a YAML file in the repository, and it signs users in through your forge. It supports GitHub, GitLab, Gitea, Forgejo and Bitbucket, including instances you host yourself
- How do I install Woodpecker CI?
Docker Compose is the documented route: one woodpecker-server container exposing port 8000 and one woodpecker-agent container with the Docker socket mounted, sharing a secret generated with openssl rand -hex 32, plus your forge OAuth client id and secret. The upstream compose file and the surrounding steps are on this page
- What is WOODPECKER_AGENT_SECRET and how do I generate it?
It is the shared secret the server and agents authenticate with, and Woodpecker documentation names openssl rand -hex 32 for producing it. Setting the same value everywhere makes it a system token; registering an agent in the interface first gives that agent its own token instead, which is what you want once more than one team can reach the host. Every sensitive variable also has a _FILE form so the value can be read from a mounted file
- What size server does Woodpecker need?
The software itself is light - the server is a small Go process with an embedded database until you point it at Postgres, and the agent is a supervisor that starts containers. What consumes the machine is the build. 2 vCPU, 4 GB and 64 GB of NVMe runs server and agent together for a small team; move to a dedicated agent host once pipelines start queueing
- Why does the agent need the Docker socket?
Because every pipeline step is a container the agent starts on the host daemon. That effectively gives pipeline steps root on that machine, which is the design rather than a misconfiguration - and it is the reason to keep the agent on a host that does nothing else, which is cheap to do when a second machine costs EUR 15 a month
- My pipelines never trigger. What is wrong?
Nine times in ten it is WOODPECKER_HOST. That value is the public URL the forge uses when it creates the webhook, so an internal address there produces an installation that looks healthy and never receives a push. The other common one is WOODPECKER_OPEN=true, which is not a trigger problem but does mean anyone with an account on your forge can sign in - convenient on day one, wrong by day thirty
If you require assistance or have additional questions, please contact the managers or write to the support team at support@dcxv.com
Ready to get started?
Monthly billing, no setup fee, no lock-in