DEV Community

Paradith
Paradith

Posted on

Replacing GitHub Runners with Lambda MicroVMs

In a typical CI setup, multiple jobs run on shared runners. Even when you’re using containers, those jobs are still sitting on top of the same underlying machine, sharing resources in ways that aren’t always obvious. Most of us in quality engineering have felt the impact of that over time. Tests that pass one minute and fail the next, environments that behave slightly differently run to run, and that lingering doubt of “is it the code, or the pipeline?”

One of the most effective ways I’ve seen to remove that uncertainty is to stop sharing runners altogether.

Instead, imagine each test job spinning up its own tiny, fully isolated machine, running exactly what it needs, and then disappearing completely once it’s done. That’s the model you get with MicroVM-based execution. Every job runs in its own environment, with its own kernel, its own filesystem, and no overlap with anything else.

From a testing perspective, this is where things start to feel different (in a good way).

The first change you notice is stability. When every test starts from a completely clean environment, a whole category of flakiness just… goes away. There are no leftover files from previous runs, no shared caches behaving unpredictably, and no hidden dependencies creeping in from other jobs. You’re no longer fighting the environment to understand your results.

It also makes failures much easier to reason about. When something breaks, you can be far more confident that it’s a real issue in the code or the test itself, not some side effect of how or where it ran. That clarity is incredibly valuable, especially in larger systems where reproducing issues can otherwise be half the battle.

Another thing that tends to surprise people is how fast this approach can actually feel. Traditionally, giving every job its own machine would be too slow to be practical. But when those environments are created from pre-initialised snapshots, they don’t need to boot from scratch or reinstall dependencies every time. They start in a ready-to-run state, which means your pipeline spends less time preparing and more time actually testing.

For teams running large suites or complex setups, that can noticeably tighten feedback loops. You’re not cutting corners on testing you’re just removing the overhead around it.

There’s also a security angle that’s hard to ignore. Most pipelines today are running code from a mix of trusted and untrusted sources - feature branches, forks, external contributors. On shared runners, even with container isolation, there’s always a level of risk that one job could interfere with another or access something it shouldn’t.

When each job runs in its own disposable virtual machine, that risk drops significantly. The environment is created for that job and that job alone, and once it’s finished, everything is torn down. There’s nothing lingering, nothing shared, and nothing for the next job to inherit.

Of course, this isn’t a magic switch you flip without thinking. Moving in this direction does mean taking more ownership of your test environments. You’re defining what goes into them, how they’re built, and how they’re maintained. For some teams, that’s a big shift from relying on 'off the shelf' runner images.

It also changes how you debug. You don’t tend to “jump into” these environments in the same way you might with a traditional runner. Instead, you rely more on logs, reproducibility, and rerunning jobs in identical conditions. In practice, though, that’s often a more reliable way to debug anyway, because you’re not accidentally changing the system while you investigate it.

For simpler pipelines, this approach might feel like overkill. If your tests are quick, stateless, and already stable, shared runners are probably doing the job just fine. But once your test suite grows in complexity, or flakiness becomes a regular frustration, the benefits become much more tangible.

What I like most about this model isn’t the technology itself, but the shift in mindset it encourages. Instead of treating environments as something shared and long lived, you treat them as disposable and deterministic. Every run is a fresh start. Every result is easier to trust.

And ultimately, that’s what we’re aiming for in quality engineering: confidence. Not just that tests are running, but that the results genuinely reflect the state of the system.

When you get to that point, your pipeline stops being something you work around, and starts being something you can rely on.

Lambda MicroVMs are a very valid candidate for GitHub self-hosted runners. The initial setup takes some effort, but once it’s deployed it’s fully serverless, auto-scaling, and highly available.

Top comments (0)