Jenkins is free and ubiquitous, so this migration is not about a license, it is about maintenance burden. Plugin sprawl and version conflicts, Groovy pipelines and shared libraries that only one person fully understands, a steady stream of plugin security advisories, and the upkeep of controllers and agents all add up to real engineering time. Woodpecker CI, a lightweight, container-native CI engine (a community fork of Drone), takes the opposite approach: every step runs in a container, pipelines are simple YAML in the repo, and there is no plugin ecosystem to patch. If your Jenkins use is mostly “run these steps in containers on push”, Woodpecker is a clean, low-overhead replacement.
From installed plugins to container images
The shift that matters: in Jenkins, capability comes from installed plugins; in Woodpecker, capability comes from container images. Need Node, Go, Terraform, a Docker build, a cloud CLI? You reference an image that already has it instead of installing and maintaining a plugin. That single change is why the maintenance curve flattens, and it is also the main thing your pipeline conversion has to internalise.
Converting pipelines
Each repo gets a .woodpecker.yml describing its pipeline. The conversion from a Jenkinsfile is mostly mechanical once you map the concepts:
| Jenkins | Woodpecker |
|---|---|
stages { stage('x') { steps { … } } } | steps: with named entries |
agent { docker { image '…' } } | image: on each step |
plugin step (e.g. withCredentials) | secrets: referenced by the step |
post { always { … } } | a step with when: conditions |
| shared library | a reusable image or a small script in-repo |
Groovy logic that did real branching is the part that needs thought, push it into scripts the step runs, rather than trying to recreate a Turing-complete pipeline in YAML. If a pipeline is mostly Groovy glue, that’s a signal it was doing too much in Jenkins; simplifying it is part of the win.
Retiring plugins one by one
Plugin sprawl is the tax you are leaving behind, so it pays to inventory it deliberately. List the plugins each pipeline actually invokes, then sort them into three piles. Tool plugins (a language runtime, a linter, a cloud CLI) map to an image that already carries the tool. Orchestration plugins (conditional execution, retries, notifications) map to step conditions, when: blocks, or a few lines of script. Exotic plugins that did something bespoke are the ones to question: often the cleanest conversion is to not carry the behavior across at all. Every plugin you retire is one fewer advisory to track and one fewer version conflict to untangle.
Standing up the server and moving secrets
Woodpecker is a server plus one or more agents, connected to your forge over OAuth. Stand up the server first, then register agents on compute that has a working container runtime. Credentials move out of the Jenkins credential store into Woodpecker secrets, scoped per org or repo, and are referenced explicitly by the steps that need them. This explicitness is a feature: it surfaces which jobs really depend on which secret, information that a shared Jenkins credential store tends to obscure. Keep secrets out of the committed YAML without exception.
A migration that doesn’t freeze delivery
- Stand up Woodpecker, the server plus one or more agents, and connect it to your forge (it integrates with Gitea/Forgejo, GitHub, GitLab).
- Move secrets out of Jenkins credentials into Woodpecker secrets (org/repo scoped); never inline them in YAML.
- Pilot one repo: add its
.woodpecker.ymland run it in parallel with the existing Jenkins job, same triggers, compare outputs and artifacts. - Convert in waves, simplest repos first, leaving the gnarliest Groovy pipelines until you understand the pattern.
- Freeze, then retire Jenkins, stop creating new Jenkins jobs once a repo is migrated, and decommission the controller after the last job moves.
Gotchas worth flagging
- Build agents and Docker. Steps run in containers, so agents need a working container runtime and enough resources; size them for your concurrency.
- Artifact and caching patterns differ. Jenkins archived artifacts and workspace caches map to Woodpecker volumes/caches or an external store (S3), design this rather than assuming workspace persistence.
- Approvals and complex orchestration. Multi-pipeline gating and manual approvals are simpler in Woodpecker than in Jenkins; if you relied on heavy plugins for this, validate the equivalent early.
- Scale ceiling. Woodpecker is deliberately lean. For very large, matrixed, multi-team estates, confirm it meets your concurrency and governance needs before committing the whole org.
Is the trade worth it?
Jenkins → Woodpecker trades a plugin-and-Groovy maintenance tax for container-native YAML pipelines that are far cheaper to keep running. Map plugins to images, push Groovy logic into scripts, and convert repo by repo while running both in parallel so delivery never stops. The payoff isn’t a license line, it’s reclaimed engineering time and a smaller security surface, so weigh it as operational cost-to-run in the calculator above, and keep Jenkins only where its heavier orchestration is genuinely required.