CircleCI’s credit-based billing has a way of surprising finance once your pipeline volume grows. If your bill scales faster than your team and you have spare compute, self-hosting Woodpecker CI is a serious option. Woodpecker is open source, container-native, and small enough to actually understand. Here is the conversion, and the trade you are making.
Orbs out, images in
In CircleCI, capability comes partly from the platform and partly from orbs: packaged, versioned bundles of config you import. In Woodpecker, capability comes from container images. Every step runs inside a container you name, and whatever that image contains is what you can do. Need Node, Go, the AWS CLI, Terraform? You reference an image that already has them. There is no orb registry because there does not need to be one; Docker Hub is the registry.
That single change reshapes the migration. You are not porting orbs, you are choosing images.
Architecture, briefly
Woodpecker is two pieces:
- A server that talks to your forge (GitHub, GitLab, Gitea) over OAuth and stores pipeline state.
- One or more agents that actually run pipelines. Agents pull work from the server and execute each step as a container.
CircleCI ran both for you invisibly. Now you run them. That is the whole bargain in one sentence.
Flattening the config
CircleCI uses .circleci/config.yml with top-level jobs and workflows. Woodpecker uses a per-repo .woodpecker.yml (or files under .woodpecker/) built from steps. The structure flattens.
CircleCI:
jobs:
test:
docker:
- image: cimg/node:20.11
steps:
- checkout
- run: npm ci
- run: npm test
Woodpecker:
steps:
test:
image: node:20
commands:
- npm ci
- npm test
Note what vanished. There is no explicit checkout; Woodpecker clones the repo into a shared workspace before your steps run. The docker executor key is gone because every step is a container by definition. Commands are a plain list.
Workflows that fanned out across jobs become steps with depends_on, and matrix builds become a matrix: block on the step.
Orbs, contexts, caching, artifacts
- Orbs become images plus a few inline commands. The
aws-cliorb becomesimage: amazon/aws-cli. A deploy orb becomes a step running the vendor’s official container. You trade convenience for transparency: you see exactly what runs. - Contexts (shared secrets) become Woodpecker secrets, defined per repo or org and injected as environment variables. Reference them with
from_secret.
steps:
deploy:
image: amazon/aws-cli
environment:
AWS_ACCESS_KEY_ID:
from_secret: aws_key
commands:
- aws s3 sync ./dist s3://my-bucket
- Caching is not magic restore keys. The common pattern is a plugin that pushes and pulls a cache archive to S3 or a volume. You define cache paths explicitly.
- Artifacts likewise: there is no built-in artifact store, so you push to S3 or a registry with a plugin step.
Cutover team by team
Woodpecker connects to your existing forge over OAuth, so there is no repository move: your code stays where it is and Woodpecker simply becomes another integration on it. That makes the cutover a per-repo switch rather than a bulk migration. Work through teams in waves, starting with the repos whose pipelines are simplest, and leave the ones with heavy caching, matrix fan-out, or macOS builds until the pattern is well understood.
For each repo the checklist is short: enable it in Woodpecker, commit its .woodpecker.yml, move its contexts into secrets, and confirm the webhook fires. Because CircleCI and Woodpecker are independent integrations on the same forge, both can run on every push during the overlap without interfering. The team keeps shipping on CircleCI while Woodpecker proves itself on the exact same commits.
Run them in parallel
Do not cut over cold. Add .woodpecker.yml alongside the existing .circleci/config.yml and let both fire on every push. Compare results for a week or two. When Woodpecker is green on the same commits CircleCI is green on, disable the CircleCI integration. The two ignore each other, so there is no risk in the overlap.
Where CircleCI still wins
This is the part that talks people out of the move when they should be talked out of it.
- Zero infrastructure. CircleCI runs the control plane and the compute. Woodpecker makes both your problem: patching agents, sizing the pool, handling a server that falls over at 2 a.m.
- macOS and GPU executors. If you build iOS apps or run GPU workloads, CircleCI’s hosted macOS and GPU fleets are genuinely hard to replicate. Self-hosting Apple silicon for CI is its own painful project.
- Elastic scale and support. CircleCI absorbs a spiky Monday-morning surge automatically. Your fixed agent pool does not, and there is no vendor to call when it jams.
The headline cost moves from a per-credit invoice to runner operations: compute, patching, and the on-call rotation that now owns your CI. For a team with idle capacity and steady load, that is a clear win. For a small team buying back its own time, it often is not.
Who this actually pays off for
CircleCI to Woodpecker swaps a per-credit SaaS bill for self-hosted open-source CI where capability comes from container images instead of orbs, and the config conversion (jobs to steps, contexts to secrets, orbs to images) is genuinely straightforward. What you are really buying is ownership: you now run the server, the agents, the caching, and the on-call. Price the compute, the patching time, and the macOS or GPU gap honestly in the calculator above, because the savings only show up once your runner ops cost less than the credits you were burning.