Since we launched, every build on orkestr ran in a shared build environment. Your next build, someone else's Nuxt build, a Go compile, all in the same place. It worked well for most apps. But each build ran against a fixed memory budget, and a heavy Next.js build could bump into it - occasionally get killed partway through - and we'd end up explaining why a perfectly good app wouldn't deploy.
We tuned around it for a while. A bit more memory here, a cache setting there. Each adjustment held for a few weeks before a slightly heavier build found the next edge.
So we changed the shape of it. Every build now runs in its own Firecracker microVM - a real kernel, its own memory, walled off from every other build. It got faster too.
What changed
Instead of one shared environment, each build gets its own small virtual machine on a dedicated build machine.
One microVM per build
UNTIL NOW NOW
Shared build environment A microVM per build
+--------------------------+ +----------+ +----------+ +----------+
| [bld] [bld] [bld] | ---> | microVM | | microVM | | microVM |
+--------------------------+ | [kernel] | | [kernel] | | [kernel] |
| build | | build | | build |
Shared, one fixed memory | isolated | | isolated | | isolated |
budget per build. +----------+ +----------+ +----------+
Own kernel, own memory
(4-12 GB), isolated.
A microVM boots in well under a second, gets a full Linux kernel, and runs a standard image builder against real syscalls. That last part is what ends the memory tuning. The shared environment handed each build a fixed budget; a microVM brings its own, sized to your plan.
Build sizes now scale with your plan instead of one fixed number: free builds get 4 GB, Pro gets 6 GB, and the largest get 12 GB. The build machine is a dedicated, high-memory EU box, so those envelopes are real, not oversubscribed.
How a build becomes an image
A build runs your code - postinstall scripts, Dockerfile RUN steps, whatever your dependencies do at build time. So the design keeps a build focused on one job: turn your repo into an image, and nothing else.
The microVM builds the image and writes it to a disk the host can read. Publishing that image to our registry happens outside the VM, on the host, over a private network. The build itself never needs registry access.
How a build becomes an image
+------------------------+
| Build microVM |
| runs your code |
| builds the image |
| own kernel |
| no registry access |
+------------------------+
|
| writes
v
+------------------------+
| Image on disk |
| host-readable |
+------------------------+
|
| hands off
v
+------------------------+
| orkestr host |
| publishes to registry |
| over the private net |
| VM is never on this path
+------------------------+
The microVM builds. The host publishes. Each build is a throwaway VM.
The property that falls out: each build is a throwaway VM with its own kernel, so one build can't see another, and the part that talks to the registry was never inside your build. It's the same microVM isolation model we already run under our EU sandboxes for untrusted code. A build is code too, so it gets the same boundary.
The speed
We went in expecting to trade a little build time for the isolation. Booting a VM per build sounds like overhead. It wasn't.
Two things pay for the boot. The build image ships with everything pre-baked, so there's no setup step before the build starts. And each project gets a persistent build cache on disk, so your dependency layers survive between builds.
A real Next.js app, old path versus new on comparable hardware:
| Build | Time |
|---|---|
| Shared builder (baseline) | 109 s |
| Firecracker, cold (empty cache) | 93 s |
| Firecracker, warm (cache hit) | 62 s |
That warm number is a real git push to live in 62 seconds. A small FastAPI app lands the build step in about 14 seconds warm. The first deploy of a project pays the cold price once; every deploy after reuses the cache and drops to seconds. That's the case you feel day to day.
Where this is right now
This is live in production for container apps - the standard git push deploy. Serverless Functions still build the old way for now, and they move to microVM builds next.
You don't configure any of it. No new setting, no Dockerfile change. Push to a repo you've connected to orkestr and it works the way it always has from your side. It's just faster.
FAQ
Do I need to change my Dockerfile or build settings?
No. The build interface is identical - same generated Dockerfiles, same detected frameworks, same git push to deploy. The machinery underneath changed; your side didn't.
Why Firecracker instead of a container for builds?
A container shares the host kernel, so builds share one memory budget. A Firecracker microVM has its own kernel, so each build brings its own memory envelope and its own isolation boundary. That's what removes the fixed per-build memory cap and lets build sizes scale with your plan.
Can a build reach the registry or another build?
No. The build VM has no registry credentials and isn't on the platform network. It writes the finished image to a disk, and a process on the host publishes it. Each build is its own VM, so builds can't see each other.
Is my code still built in the EU?
Yes. Builds run on a dedicated EU build machine, same as the rest of the platform. Nothing about this touches a US region.
Will my builds actually be faster?
The first build of a project is comparable to before. Every build after that hits a persistent per-project cache and typically drops to the low tens of seconds. The warm path is the common case.
Top comments (3)
One microVM per build is a strong isolation story, especially for untrusted or AI-generated changes. The real operational question becomes cache strategy: how much speed can you keep without weakening the boundary?
Yeah, this was the exact tradeoff we occured. We ended up with: cache never crosses tenants. Each project gets its own cache volume that attaches to its build VM, so our build kit caching still makes warm rebuilds fast - you just never get cross-tenant layer sharing, which is where poisoning risk actually lives. Rebuild speed mostly comes from your own unchanged layers anyway, so the loss is smaller than it sounds.
Also the VMs have zero registry access in either direction - the host stages base images onto the volume before boot and pushes the result after the VM is gone. A hostile build can only waste its own CPU and dirty its own cache.
That staging boundary is the detail that makes the design believable. No registry access from the VM removes a whole class of quiet supply-chain mistakes, and per-project cache keeps the performance story without pretending shared cache is free.