How We Moved 200 Services off Jenkins in About 3 Months
👤 @pete-builds-ciSaaS100-500 engineers2025
01The Setup
I owned the least glamorous part of platform engineering: keeping an aging Jenkins estate alive while the rest of the company added more services every quarter. By the time we started this migration, we had around 200 repositories, a Jenkins controller, and a few dozen EC2-backed workers glued together with plugins, shared libraries, and a lot of institutional memory. Average build time hovered around 18 minutes, but the real complaint was queue time whenever a few teams tried to release at once.
02What Happened
The migration got approved after a plugin update broke the controller and we lost most of a workday coaxing Jenkins back to life. We did not build a magical Jenkins-to-Actions compiler. We wrote a converter for the three Jenkinsfile patterns most teams had copied from shared libraries, and that got about 70% of repositories most of the way there. Everything else was manual: secrets cleanup, Docker layer caching, self-hosted runners for jobs that needed VPC access, and a long tail of weird assumptions buried in old Groovy helpers.
03Timeline
Week 1-2 - Audit shared libraries, runner requirements, and secrets sprawl
Week 3-4 - Build the converter for our common pipeline patterns and migrate low-risk repos
Week 5-8 - Move the middle 120 services in waves, fixing caching and deployment edge cases
Week 9-11 - Run Jenkins and Actions side by side for critical services
Week 12 - Cut the last production pipelines over and decommission Jenkins
04The Resolution
We finished the risky part in about 11 weeks and spent the last week cleaning up stragglers and shutting the old fleet down. Median build time ended up just under 10 minutes, queueing was dramatically lower, and CI spend fell because the new runner fleet scaled down at night instead of idling all day. The best outcome was not the dashboard metric. It was getting two platform engineers back from "Jenkins janitor" duty so they could work on deployment safety and developer tooling instead.
LessonsWhat We Learned
01
Automate the common cases, but do not pretend every pipeline deserves or needs full automation.
02
Critical services should run old and new pipelines in parallel until the new path is boring.
03
A CI migration is part tooling project and part archaeology project; budget time for both.
What I'd Do Differently
I would have audited shared-library usage and secrets first. The YAML conversion was the easy part. The slow work was discovering how many pipelines depended on hidden behavior that only existed in somebody's memory.