Alan: off CircleCI and onto GitHub Actions, with a CI bill 75% lower
Alan moved its monorepo CI from CircleCI to GitHub Actions on RunsOn, in its own AWS account. CI costs fell 75%; PR checks went from 15 to under 10 minutes.
- −75% monthly CI cost
- 15 → <10 min PR checks
- 6% → 4% failure rate on main
- a few weeks to migrate every job
- Company
- Alan · Health insurance, Paris
- Before
- CircleCI
- After
- GitHub Actions + RunsOn, mostly EC2 spot
- Source
- Moving our CI/CD to GitHub Actions, by Tim Petricola(Software Engineer), November 12, 2024
About Alan#
Alan is a digital health insurer, founded in Paris in 2016, that covers companies and individuals in France, Spain and Belgium, and more recently Canada. Several hundred people work there.
Its CI does a lot more than run tests: it triggers deployments, runs static analysis and secret scanning, builds the iOS and Android apps, and builds Docker images, all from one monorepo.
Alan had been on CircleCI from the start. In 2024, it moved its CI to GitHub Actions, with RunsOn runners in its own AWS account.
What hurt#
Two things went wrong at once.
- The config. Moving to a single monorepo made engineers faster but made the CI configuration hard to keep in shape, even with CircleCI’s dynamic configuration. The YAML kept growing.
- The bill. Alan had a yearly CircleCI contract with a fixed number of credits. With the team growing, it was on track to burn twelve months of credits in eight.
Alan already used GitHub for code, pull requests and company-wide decisions, so GitHub Actions was the obvious candidate.
Picking GitHub Actions#
The team wanted GitHub’s orchestration, not GitHub’s runners: they are expensive, not especially fast, and at the time there was no ARM option, which some of Alan’s workloads run better on.
To check the migration was worth it before investing in anything, they ran a set of representative jobs on GitHub Actions with a third-party runner provider: AMD and ARM, CPU- and memory-heavy jobs, Docker builds, linters. Those jobs were on average 12% faster and 40% cheaper than on CircleCI.
CircleCI still did two things better: per-job CPU and memory visibility, and SSH into a failing job. Both had workarounds in the Actions ecosystem. What tipped it was how simple workflow configuration is on GitHub Actions (triggers with the full GitHub context, reusable workflows, custom actions) and how many open-source actions already exist.
Why RunsOn#
With GitHub Actions settled, the next question was where the jobs would run.
- Actions Runner Controller on Kubernetes was ruled out early. The feedback Alan heard about it at scale was poor, and their own tests were slow to start jobs and inflexible on a shared cluster.
- Third-party hosted runners cost more than self-hosting, meant giving another company access, and often capped concurrency.
- RunsOn, then a young project, runs in Alan’s own AWS account and starts one EC2 instance per job.
They priced one of their most frequent and expensive pipelines on all three. The CircleCI version had been heavily optimized; the GitHub Actions versions had not been tuned at all.
| One pipeline run | CircleCI | GitHub + RunsOn | GitHub + third party |
|---|---|---|---|
| Cost | $2.82 | $1.60 | $1.90 |
| Time | 11 min | 11 min | 14 min |
Same speed as CircleCI, 43% cheaper, before any tuning.
Moving one job at a time#
A one-shot switch was not an option for a codebase that size. Both CI systems ran side by side for a few weeks, and each job went through the same four steps:
- Shadow mode. Build the job on GitHub Actions and run it next to the CircleCI one, with every non-idempotent step in dry-run, so a failure could not block anyone.
- Watch it. Compare speed and failure rate on both systems. Alan used Datadog CI Visibility for this.
- Flip. Once GitHub Actions was at least as good, put the CircleCI job in shadow mode instead.
- Remove. Delete the CircleCI job once the GitHub Actions one had proven stable.
Results#
After a few weeks the migration was done:
- Monthly CI costs down 75%.
- PR checks under 10 minutes, down from 15.
- Failure rate on the main branch down from 6% to 4%.
On the RunsOn side, what made the difference was picking any EC2 instance type per job: each job gets the machine that suits it. RunsOn also shipped fixes and improvements quickly while Alan migrated.
What comes next#
A few things are still rough:
- GitHub API rate limits, hit often enough to slow work down.
- The Actions UI, for example copying a file path from an error annotation. Alan works around it with its own browser extension.
- Spot interruption spikes. Alan runs mostly on spot instances, and a burst of interruptions makes CI worse for a while.
RunsOn could do something about the last one. In February 2025, v2.6.7 added a spot circuit breaker: when interruptions pass a threshold within a time window, RunsOn switches new jobs to on-demand instances for a while, then goes back to spot. See spot instances and interruptions for how to set it.
Alan also builds its own tooling on top: alerting, and CLI commands that reproduce a CI failure on an engineer’s machine.
I've been testing Kubernetes with ARC: more complex, each runner takes ~25 seconds to boot (on an existing node), far less attractive than the simplicity of RunsOn.
Tim Petricola Software Engineer, Alan