all posts

Firecracker vs Google Cloud Run: Isolation and Control

Ajay Kumar··8 min read

"Firecracker vs Google Cloud Run" is another comparison that isn't quite a comparison — one is a virtual machine monitor, the other is a fully-managed serverless product — but the question underneath it is real. If you're deciding where to run workloads, and especially where to run code you don't fully trust, you're really asking: do I want a managed container platform that runs my containers on Google's infrastructure, or do I want a microVM primitive I control end to end? Those are genuinely different bets, and the difference matters most exactly when the code is untrusted, multi-tenant, or written by an AI agent at runtime.

What Cloud Run actually is

Cloud Run is a managed serverless platform for running containers. You hand Google a container image; Google places it, scales it (including down to zero), routes traffic to it, patches the fleet underneath it, and bills you for what you use. You never see a host, a scheduler, or a kernel. That's the pitch, and for a huge class of stateless HTTP services it's a very good pitch — you write a container and stop thinking about infrastructure.

Underneath, though, "serverless containers" is doing some quiet work. Running arbitrary customer containers on shared infrastructure is a serious isolation problem, and Google's serverless platforms are documented to run workloads inside a sandbox (historically gVisor, a user-space kernel that intercepts syscalls to shrink the host-kernel attack surface) rather than as bare containers on a shared kernel. Describe that qualitatively and verify the specifics against Google's current docs — the exact sandboxing technology and its guarantees are Google's to define and change. The load-bearing point is architectural: the isolation layer exists, it's part of the managed product, and you don't control it or even directly see it.

Cloud Run isn't "just a container on a shared kernel" — Google's serverless platforms sandbox workloads (documented historically as gVisor). But that sandbox is part of the managed product: you inherit whatever isolation Google ships, you can't inspect or tune it, and it's opaque to you by design. Always verify the current model against Google's docs.

What Firecracker is

Firecracker is the other end of the spectrum. It's a VMM — the same one AWS wrote for Lambda and open-sourced — that boots each workload as its own microVM with a real guest kernel, confined by hardware virtualization (KVM). The guest talks to 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 a container sees. And critically, you're the one holding the VMM — the kernel, the networking, the lifecycle, and the isolation policy are all yours to set.

So the axis isn't really "container vs VM." It's "an isolation boundary Google owns and hides" versus "an isolation boundary you own and configure." Both can safely run untrusted code; they differ on who controls the wall and what you can do around it.

Cold starts and what "serverless" means in each

Both worlds have a cold-start story, and they solve it differently. On Cloud Run, "serverless" means Google manages the instance lifecycle for you: it can scale to zero and spin instances up on demand, and a request that arrives when nothing is warm pays a cold start (image pull, container start, your app's init). You can influence this — keeping a minimum number of instances warm is a knob — but the create/reuse policy is fundamentally Google's, not yours. Treat any specific latency figures as directional and check them against Google's current docs; they move over time and depend heavily on your image and runtime.

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 — but in production you don't cold-boot per request at all. 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. That's the mechanism a platform uses to make VM-grade isolation feel serverless — you get a fresh, isolated machine per request without paying a boot each time, and you control the whole lifecycle.

Side by side

  • Isolation model — Firecracker/PandaStack: hardware-virtualized microVM with its own guest kernel; you own and configure the boundary. Cloud Run: containers on Google's managed platform, sandboxed by Google (historically gVisor) — a boundary you inherit but don't control or see. Verify the sandbox model against Google's docs.
  • Untrusted / AI-generated code — Firecracker/PandaStack: per-request microVM, isolation and egress under your direct control. Cloud Run: relies on Google's managed sandbox; safe for many cases, but the wall and its policy are opaque to you.
  • 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. Cloud Run: managed lifecycle, scale-to-zero, cold start on a cold instance — figures are Google's and vary; check current docs.
  • What "serverless" means — Firecracker/PandaStack: you own the fleet but a snapshot-restore create makes it feel serverless. Cloud Run: Google owns the fleet, scaling, and lifecycle end to end.
  • Control & self-hosting — Firecracker/PandaStack: your kernel, byte-level networking, egress rules, and lifecycle; runs on any KVM host, another cloud, or on-prem. Cloud Run: Google Cloud only, on Google's schedule and pricing model.
  • Snapshot & fork — Firecracker/PandaStack: first-class; freeze/restore a running VM, CoW-fork it (same-host 400–750ms, cross-host 1.2–3.5s). Cloud Run: not exposed as a primitive.
  • Persistent local state — Firecracker/PandaStack: attach durable volumes; keep a VM alive as long as you want. Cloud Run: instances are ephemeral; durable state lives in external services.
  • Best fit — Firecracker/PandaStack: untrusted, multi-tenant, or AI-generated workloads where you need to own the isolation and lifecycle. Cloud Run: trusted-ish first-party stateless HTTP services where not managing infrastructure is the win.

The untrusted-code question

Here's where the two genuinely diverge for the workload we care about most: per-request execution of code you don't trust. On Cloud Run, your untrusted code runs inside Google'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 the egress at the byte level, and you don't decide when an instance is fresh versus reused. 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.

Cloud Run isolates your untrusted code behind a wall Google built and hides. Firecracker hands you the wall — you decide how tall it is, whether there's a door, and whether the room is empty when the next request walks in.

A serverless-feeling microVM you actually own

This is the exact line PandaStack is built on: Firecracker microVM isolation with a create call that feels as serverless as Cloud Run. Every sandbox is its own microVM, created via snapshot-restore at a p50 of 179ms and a p99 around 203ms (the restore-and-resume step itself is roughly 49ms of that). The genuine cold boot happens once per template at around 3 seconds and is then baked into a snapshot and amortized away — every create takes the restore path, so there's no warm pool to manage and no cold-start lottery per request. Here's the create-run-teardown loop, per request, with no inherited state:

from pandastack import Sandbox

# One fresh, isolated microVM per request — its own guest kernel behind a
# KVM wall, restored from a baked snapshot (~179ms p50), NOT a container on
# a managed platform's shared sandbox, and NOT reused from a prior request.
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 block exit. Nothing from this request survives for the
    # next caller to inherit — the fresh, empty room is the default.

# print(handle({"code": "print('hello from a microVM you own')"}))

Two things that snippet gives you that a Cloud Run instance doesn't. First, the isolation is a guest kernel behind a KVM wall you control, not a managed sandbox you inherit — you decide the egress policy and the kernel. Second, the per-request freshness is by construction: you created that VM for this request and destroyed it after, so there's no previous tenant and no managed reuse policy to reason about. On a managed serverless platform you code defensively around instance reuse; here, the clean slate is the default.

When to pick each

The honest default: if Cloud Run's model fits, use Cloud Run. For stateless, first-party HTTP services where you trust your own code and "never think about infrastructure" is worth more than controlling the isolation boundary, a fully-managed serverless container platform is the right engineering call. Not owning the fleet, the scaling, the patching, and the sandbox is a genuine feature.

Reach for Firecracker (or a platform built on it) when you need to own the wall: untrusted, multi-tenant, or AI-generated code where you must control and attest the isolation and egress; per-request execution with a guaranteed fresh VM and no opaque reuse policy; snapshot and fork primitives for branch-of-thought agents or instant clones of a warmed environment; durable local state and long-lived VMs; or a home that isn't Google Cloud — another cloud, bare metal, or on-prem. You don't need a fundamentally different isolation story than Cloud Run has. You need one you control, with a serverless-feeling create so that control costs you almost nothing in latency.

So the real answer to "Firecracker vs Google Cloud Run": they're not rivals so much as two answers to "who owns the isolation boundary." Cloud Run says Google does, and hides it well. Firecracker says you do, and hands you the primitive. For trusted first-party services, letting Google own it is a great deal. For untrusted code you have to reason about yourself, owning the microVM — with a snapshot-restore create that feels every bit as serverless — is the one that lets you sleep.

Frequently asked questions

Is Google Cloud Run safe for running untrusted code?

Cloud Run runs your containers inside Google's managed serverless sandbox — historically documented as gVisor, a user-space kernel that shrinks the host-kernel attack surface — not as bare containers on a shared kernel. That's a real, well-engineered boundary suitable for many untrusted workloads. The tradeoff is that the sandbox is opaque: you inherit Google's isolation, can't tune or inspect it, and don't control egress at the byte level. If your threat model requires you to own and attest the isolation boundary, a self-controlled microVM (Firecracker) fits better. Verify Cloud Run's current sandbox model against Google's docs.

What's the difference between Cloud Run and Firecracker?

Cloud Run is a fully-managed serverless container platform: you give Google a container image and it handles placement, scaling, lifecycle, patching, and the isolation sandbox for you. Firecracker is a virtual machine monitor that runs each workload as its own microVM with a real guest kernel behind hardware virtualization — and you own the kernel, networking, egress, and lifecycle. Cloud Run hides the isolation boundary; Firecracker hands it to you.

How do cold starts compare between Cloud Run and Firecracker?

Cloud Run manages the instance lifecycle and can scale to zero, so a request hitting a cold instance pays a cold start (image pull, container start, app init); you can keep instances warm, but the policy is Google's. Firecracker platforms snapshot a fully-booted microVM and restore it per create — restore is not boot, so the guest wakes up warm. On PandaStack that's ~179ms p50 (about 49ms of restore-and-resume), with the genuine ~3s cold boot paid once per template and then amortized. Treat any Cloud Run latency figures as directional and verify against Google's docs.

Can I get serverless-style create latency with Firecracker isolation?

Yes — that's the design point of a snapshot-restore platform. Instead of a warm pool, every create restores a baked snapshot of a booted microVM. On PandaStack a fresh, isolated microVM is created at a p50 of 179ms and p99 around 203ms, so you get VM-grade isolation with a create that feels as serverless as a managed container platform, without a warm pool to manage.

When should I choose Cloud Run over a self-hosted Firecracker platform?

Choose Cloud Run when you're running stateless, first-party HTTP services you trust, and not managing the fleet, scaling, patching, and sandbox is worth more than controlling the isolation boundary. Choose Firecracker when you need to own the wall: untrusted or multi-tenant code where you must control and attest isolation and egress, guaranteed per-request VM freshness, snapshot/fork primitives, durable local state, or the ability to run outside Google Cloud.

Run code in a microVM in one API call.

49ms p50 cold start. Fork, snapshot, and scale to zero.

Start free
Written by Ajay Kumar, Founder, PandaStack.