Firecracker vs Cloudflare Containers: which model?
Cloudflare Containers went GA in 2025, and they're a genuinely different animal from Cloudflare Workers. Workers run your code as V8 isolates — a language-runtime sandbox, no container in sight (I wrote about that trade-off in Firecracker vs Cloudflare Workers). Containers, by contrast, run actual container images on Cloudflare's platform, orchestrated through Durable Objects, so you can finally ship the things a Worker can't: native binaries, subprocesses, a full filesystem, arbitrary languages. That makes "Firecracker vs Cloudflare Containers" the more interesting comparison, because now both sides can run a real program — the question moves to who owns the isolation boundary, where the code runs, and what the programming model asks of you. I'm Ajay; I built PandaStack on the microVM side of this line, so treat me as an interested but honest narrator, and verify every Cloudflare specific against their own docs, which move faster than any blog post.
The short version, so you can bail early if you need to: Cloudflare Containers are a lovely fit when your container needs to live next to Workers logic at the edge, tied into the Durable Objects programming model and the rest of Cloudflare's platform — Cloudflare owns and hides the sandbox, and that's a feature for a lot of teams. Firecracker is the model when you need per-tenant hardware isolation for untrusted code, a general microVM API you can self-host or use through a platform, and full control over the kernel, networking, and lifecycle. Both can run a container's worth of work; they differ on control, placement, and who you're trusting with the wall. Everything below is the reasoning.
What Cloudflare Containers actually are
Cloudflare Containers let you run a container image on Cloudflare's network, and the orchestration story is the distinctive part: a container instance is driven by a Durable Object. You define a container in your Wrangler config, and a Durable Object acts as its programmatic controller — it can start the container, route requests to it, stop it when idle, and hold the coordination state. That's a deliberate design choice: instead of a separate cluster scheduler, you get the container's lifecycle as code, colocated with your Workers, using the same Durable Object primitive you'd already reach for to coordinate stateful edge logic. It's containers wired into the Workers ecosystem, not containers as a standalone product.
Underneath, running arbitrary customer container images on shared infrastructure is a serious isolation problem, and Cloudflare runs these workloads inside its own sandboxing rather than as bare containers on a shared kernel. Describe that qualitatively and verify the specifics against Cloudflare's current docs — the exact isolation layer, its guarantees, and how instances are placed and reused are Cloudflare's to define and change. The load-bearing point is architectural: the isolation exists, it's part of the managed product, and you inherit it as a black box rather than owning or tuning it.
What Firecracker is
Firecracker is the other end of the spectrum. It's a Virtual Machine Monitor — the same one AWS wrote for Lambda and Fargate and open-sourced — that boots each workload as its own microVM with a real guest kernel, confined by hardware virtualization (KVM). The guest reaches the outside world only through a handful of emulated virtio devices. There is no shared host kernel to attack and no user-space syscall interceptor to trust: an escape has to defeat the hypervisor itself, a far smaller and more heavily audited surface than the full Linux syscall interface. And critically, you're the one holding the VMM — the guest kernel, the networking, the egress rules, the lifecycle, and the isolation policy are all yours to set. See what is a microVM for the primitive in more depth.
So the axis isn't really "container vs microVM" — both can run a full program with native binaries and subprocesses. It's "an isolation boundary Cloudflare owns and hides, placed on Cloudflare's edge" versus "a hardware boundary you own and configure, placed wherever you have a KVM host." Both can safely run untrusted code; they differ on who controls the wall, where the room is, and what you can build around it.
The programming model: Durable Objects vs a microVM API
This is where the day-to-day experience diverges most. Cloudflare Containers are opinionated: you declare the container in Wrangler and drive it from a Durable Object, so the mental model is "my container is an appendage of my Worker, coordinated by edge-native primitives." That's powerful when your architecture already lives in that world — the container inherits Cloudflare's routing, its bindings, its placement, and its lifecycle conventions for free. Here's the shape of the declaration and the controller, sketched (check current Wrangler and container-class APIs against Cloudflare's docs, since the names evolve):
// wrangler.jsonc — declare a container and bind it to a Durable Object class.
// (Illustrative sketch — verify field names against Cloudflare's current docs.)
{
"name": "my-edge-app",
"main": "src/index.ts",
"containers": [
{
"class_name": "MyContainer",
"image": "./Dockerfile",
"instances": 5
}
],
"durable_objects": {
"bindings": [
{ "name": "MY_CONTAINER", "class_name": "MyContainer" }
]
}
}// A Durable Object orchestrates the container instance's lifecycle.
// The container is an appendage of your Worker, not a standalone box.
import { Container } from "@cloudflare/containers";
export class MyContainer extends Container {
defaultPort = 8080;
sleepAfter = "10m";
}
export default {
async fetch(request: Request, env: Env) {
// Route the request to a specific container instance via its DO.
const id = env.MY_CONTAINER.idFromName("tenant-42");
return env.MY_CONTAINER.get(id).fetch(request);
},
};Firecracker (and platforms built on it) give you the opposite: a general microVM API that isn't tied to any one edge runtime or coordination primitive. You create a sandbox, run commands in it, snapshot it, fork it, tear it down — from any language, any orchestrator, any cloud. There's no Durable Object to model your lifecycle around; the VM is a first-class thing you own. On PandaStack that looks like a plain create-run-teardown loop you can drop into any backend:
from pandastack import Sandbox
# A general microVM API — not tied to Durable Objects or an edge runtime.
# One fresh, isolated microVM: its own guest kernel behind a KVM wall,
# restored from a baked snapshot (~179ms p50), destroyed on block exit.
def handle(request: dict) -> dict:
with Sandbox.create(template="code-interpreter", ttl_seconds=120) as sbx:
# Run untrusted / model-written code inside a boundary you own.
sbx.filesystem.write("/workspace/task.py", request["code"])
result = sbx.exec("python3 /workspace/task.py", timeout_seconds=30)
return {
"exit_code": result.exit_code,
"stdout": result.stdout,
"stderr": result.stderr,
}
# VM destroyed on exit. Nothing from this request survives for the next
# caller to inherit — the fresh, empty room is the default.Cold starts and where the code runs
Both worlds have a cold-start story. On Cloudflare Containers, a container instance has to be started on demand — image pull, container start, your app's init — and Cloudflare manages that lifecycle for you, including sleeping idle instances (the sleepAfter knob) and waking them on the next request. Treat any specific latency figures as directional and check them against Cloudflare's current docs; they depend heavily on your image, your placement, and Cloudflare's platform behavior, and they change over time. Don't trust a number from a blog post — including this one — for a moving managed platform.
Firecracker attacks cold start from below, at the VMM. A genuine cold boot of a microVM is a few milliseconds of VMM work plus guest-kernel boot — about 3 seconds for a full template boot in practice — but in production you don't cold-boot per request. You snapshot a fully-booted, running microVM (its entire RAM plus vCPU and device state) and restore it, and restore isn't boot: the guest wakes up mid-instruction with its page cache warm and processes already running. On PandaStack every create takes that restore path at a p50 of 179ms and a p99 around 203ms, with the underlying restore-and-resume step around 49ms; the ~3s cold boot is paid once per template and then amortized away, so there's no warm pool to manage and no cold-start lottery per request. A same-host fork — copy-on-write guest memory plus an XFS-reflink rootfs — lands in 400–750ms, and a cross-host fork in 1.2–3.5s.
The other half of "where the code runs" is placement. Cloudflare's whole reason to exist is the edge: your container runs on Cloudflare's global network, close to users, which is a real and often decisive advantage for latency-sensitive, request-shaped workloads. Firecracker has no opinion about geography — it runs wherever you put a KVM host: one region, many regions, another cloud, bare metal, or on-prem. If edge proximity is the point, that's Cloudflare's home turf; if control and portability are the point, the microVM goes where you go.
Side by side
- Isolation — Firecracker/PandaStack: hardware-virtualized microVM with its own guest kernel; you own and configure the boundary, no shared tenant. Cloudflare Containers: real container images run inside Cloudflare's own sandboxing — a boundary you inherit but don't control or see. Verify the sandbox model against Cloudflare's docs.
- Programming model — Firecracker/PandaStack: a general microVM API (create / exec / snapshot / fork / destroy) from any language, orchestrator, or cloud. Cloudflare Containers: Durable-Object-driven — the container is declared in Wrangler and its lifecycle is coordinated by a DO, colocated with your Workers.
- Cold start — Firecracker/PandaStack: snapshot-restore per create (~49ms restore step; ~179ms p50, ~203ms p99), genuine cold boot (~3s) paid once per template and amortized. Cloudflare Containers: managed instance lifecycle with sleep/wake; figures are Cloudflare's and vary — check current docs.
- State & persistence — Firecracker/PandaStack: attach durable volumes, keep a VM alive as long as you want, snapshot and fork running state. Cloudflare Containers: instances are managed and can sleep; durable coordination state lives in the Durable Object, and long-term data in external/Cloudflare services.
- Edge placement — Firecracker/PandaStack: no built-in geography; runs on any KVM host you choose, one region or many. Cloudflare Containers: runs on Cloudflare's global edge network, close to users — the core advantage.
- Self-host & control — Firecracker/PandaStack: your kernel, byte-level networking, egress rules, and lifecycle; runs on any KVM host, another cloud, or on-prem. Cloudflare Containers: Cloudflare only, on Cloudflare's platform, schedule, and pricing.
- Best fit — Firecracker/PandaStack: untrusted, multi-tenant, or AI-generated code where you need per-tenant hardware isolation and full control. Cloudflare Containers: edge-proximate container workloads tied into the Workers/Durable Objects ecosystem.
The untrusted-code question
Here's where the two genuinely diverge for the workload I care about most: per-request execution of code you don't trust. On Cloudflare Containers, your untrusted code runs inside Cloudflare's managed sandbox, and that sandbox is real and well-engineered — but it's a boundary you inherit as a black box. You can't tune it, you can't inspect exactly what it permits, you don't control egress at the byte level, and the instance-reuse and placement policy are Cloudflare's, not yours. For a lot of teams that's fine, even preferable — one fewer thing to own. But if your threat model requires you to reason about, tune, or attest the isolation boundary yourself, an opaque managed sandbox is an uncomfortable place to put your trust.
With Firecracker, the boundary is yours. You decide the guest kernel, whether a sandbox can reach the network at all, what egress is permitted, and — critically — whether each request gets a brand-new VM with zero inherited state. "Fresh isolated VM per request" is a configuration choice, not a policy you have to reverse-engineer from a managed platform's docs. When you're running AI-agent-generated commands, per-user code playgrounds, code interpreters, or arbitrary customer repos, that difference is the whole game — and it's exactly what a hardware boundary between mutually-untrusting tenants buys you.
Use Cloudflare Containers when…
Reach for Cloudflare Containers when the shape of the problem matches their strengths:
- Your workload needs to live at the edge, close to users, and latency to the request origin is a first-order concern.
- You're already in the Cloudflare ecosystem — Workers, Durable Objects, KV, R2, D1 — and want the container to be a natural extension of that architecture rather than a separate system.
- You want a container's capabilities (native binaries, a real filesystem, arbitrary languages) but are happy for Cloudflare to own placement, scaling, lifecycle, and the isolation sandbox.
- Your untrusted-code needs are covered by a managed boundary you don't need to inspect or attest yourself, and 'not managing infrastructure' is worth more than owning the wall.
If that's your situation, Cloudflare Containers aren't a compromise — they're the better tool, and a self-hosted microVM would be over-paying for control and portability you don't need. Edge-proximate containers wired into Durable Objects are a genuinely nice place to be, and reinventing that orchestration yourself would be a poor trade.
Use a microVM (Firecracker) when…
Reach for a microVM the moment you need to own the wall or the lifecycle:
- You're running mutually-untrusting, multi-tenant, or AI-generated code and need a per-tenant hardware isolation boundary you control and can attest, not a managed sandbox you inherit as a black box.
- You need guaranteed per-request VM freshness with no opaque reuse policy — the empty room is the default because you created the VM for this request and destroyed it after.
- You want whole-machine snapshot and fork — freeze an environment mid-task and branch it (same-host 400–750ms, cross-host 1.2–3.5s) — as a first-class primitive rather than something the platform doesn't expose.
- You need a home that isn't Cloudflare: another cloud, bare metal, on-prem, or a specific region — and full control over the guest kernel, networking, and egress rules.
- Density and per-host economics matter: one agent pre-allocates 16,384 /30 subnets, so a single host can pack an enormous number of isolated microVMs.
This is exactly the AI-agent and general code-execution case. An agent's value is that it runs commands you didn't write and couldn't fully predict — it shells out, installs native-dependency packages, spawns subprocesses, edits files, hits the network — and it does that on behalf of many tenants who must not be able to reach each other. The honest reason to pick the microVM here isn't that a container can't run the program; Cloudflare Containers can. It's that you need to own and attest the boundary between those tenants, control the egress and the lifecycle, and carry the whole thing wherever your infrastructure lives.
Where PandaStack lands
PandaStack is built on the microVM rung because that's where arbitrary, untrusted, model-generated code belongs. Every sandbox, database, and app is its own Firecracker microVM with its own guest kernel (5.10, Ubuntu 24.04 guest), isolated by KVM hardware virtualization — a boundary you own, not a managed sandbox you inherit. Every create takes the snapshot-restore path at 179ms p50 (about 203ms p99, roughly 49ms of restore-and-resume), so VM-grade isolation with a general microVM API costs you almost nothing in latency, and there's no warm pool and no cold-start lottery. A same-host fork lands in 400–750ms, so you can branch a running environment cheaply.
Cloudflare Containers put a real container next to your Workers, at the edge, behind a wall Cloudflare built and hides. Firecracker hands you the wall — you decide how tall it is, whether there's a door, whether the room is empty for the next request, and which continent it sits on.
None of this makes the microVM the right answer for edge-proximate containers tied into Durable Objects — if your workload fits that shape, ship it on Cloudflare and enjoy the ecosystem. It makes the microVM the right answer for the workload it's actually for: untrusted, multi-tenant, AI-generated code where you need per-tenant hardware isolation, full control, and portability, with a serverless-feeling create so that control costs you almost nothing. These aren't always either/or, either — a common shape is Cloudflare at the edge for routing and coordination that hands the heavy, untrusted, or long-running work off to a microVM backend you own.
Frequently asked questions
What's the difference between Cloudflare Containers and Cloudflare Workers?
Workers run your code as V8 isolates — a language-runtime sandbox that runs JavaScript, TypeScript, and Wasm against a curated API surface, with no containers, subprocesses, or native binaries. Cloudflare Containers, GA in 2025, run actual container images on Cloudflare's platform, orchestrated by Durable Objects, so they can run native binaries, subprocesses, arbitrary languages, and a full filesystem. Isolates win on cold start and density for edge glue; Containers exist for the container-shaped workloads a Worker can't run. See our Firecracker vs Cloudflare Workers post for the isolate trade-off in depth.
How do Cloudflare Containers and Firecracker microVMs differ on isolation?
Cloudflare Containers run container images inside Cloudflare's own sandboxing — a real, well-engineered boundary, but one you inherit as a black box: you can't tune or inspect it, and you don't control egress or placement. Firecracker gives each workload its own microVM with a real guest kernel behind hardware virtualization (KVM), and you own the kernel, networking, egress, and lifecycle. Cloudflare hides the isolation boundary and places it at the edge; Firecracker hands it to you and runs it wherever you have a KVM host. Verify Cloudflare's current isolation model against their docs.
Are Cloudflare Containers good for running untrusted code?
They can run a container's worth of untrusted work behind Cloudflare's managed sandbox, which is suitable for many cases and saves you from owning the fleet and the wall. The tradeoff is that the sandbox is opaque: you can't tune or inspect it, you don't control egress at the byte level, and instance reuse and placement are Cloudflare's policy, not yours. If your threat model requires you to reason about, tune, or attest the isolation boundary between mutually-untrusting tenants yourself, a self-controlled microVM (Firecracker) fits better. Verify Cloudflare's current model against their docs.
How do cold starts compare between Cloudflare Containers and Firecracker?
A Cloudflare Container instance is started on demand — image pull, container start, app init — with Cloudflare managing sleep and wake; treat any specific figures as directional and check Cloudflare's current docs, since they depend on your image and placement and change over time. Firecracker platforms snapshot a fully-booted microVM and restore it per create — restore isn't boot, so the guest wakes up warm. On PandaStack that's ~179ms p50 (about 203ms p99, roughly 49ms of restore-and-resume), with the genuine ~3s cold boot paid once per template and then amortized, plus same-host forks in 400–750ms.
When should I choose Cloudflare Containers over a Firecracker-based sandbox?
Choose Cloudflare Containers when your container needs to live at the edge close to users and slot into the Workers and Durable Objects ecosystem, and you're happy for Cloudflare to own placement, scaling, lifecycle, and the isolation sandbox. Choose a Firecracker microVM when you need per-tenant hardware isolation for untrusted or AI-generated code that you control and can attest, guaranteed per-request VM freshness, snapshot/fork primitives, durable local state, or a home outside Cloudflare — another cloud, bare metal, or on-prem. A common architecture uses both: Cloudflare at the edge handing heavy, untrusted work to a microVM backend you own.
49ms p50 cold start. Fork, snapshot, and scale to zero.