· firecracker · mangofaas · dotnet

Firecracker, part one: getting a microVM to boot

What Firecracker actually is, why it isn't a container, and the unglamorous work of a first boot.

Series notes for MangoFaaS — let’s play with Firecracker.

What is AWS Firecracker?

Firecracker is an open-source virtual machine monitor from AWS that runs lightweight microVMs on KVM, optimised for secure multi-tenancy, fast startup and low overhead. It’s what powers Lambda and Fargate: VM-grade isolation with startup times closer to containers.

So why not just a container?

  • Firecracker uses KVM hardware virtualisation with its own guest kernel; containers share the host kernel. The multi-tenant isolation story is much stronger.
  • Containers start in tens of milliseconds. Firecracker cold starts are ~100–300 ms — and you can optimise the heck out of that down to single-digit ms.
  • MicroVMs are light, but they still carry a guest kernel, so density is lower.
  • Containers use OCI images; Firecracker wants a kernel + rootfs. You build your own images. firecracker-containerd exists, but I wanted to start from scratch.

Picking a project

I asked Copilot what to build and got three options: a declarative launchpad CLI, a snapshot-accelerated mini-FaaS, or a hardened multi-tenant code runner. The CLI was too easy. The code runner reminded me of ACM contest auto-judges — fun, but niche. So: mini-FaaS.

I promptly ignored the plan and started with a WebAPI project that swallows every request — all paths, hosts and methods — and forwards them to Kafka.

Firecracker comes into play

I’m on a Mac, which can’t run Firecracker, so I rented a Linux VM from DigitalOcean (Hetzner doesn’t do nested virtualisation). I needed a kernel and a rootfs. Building the kernel — even a shallow clone — took forever on a cheap VM, so I gave up and took Amazon’s prebuilt one:

ARCH="$(uname -m)"
release_url="https://github.com/firecracker-microvm/firecracker/releases"
latest_version=$(basename $(curl -fsSLI -o /dev/null -w  %{url_effective} ${release_url}/latest))
CI_VERSION=${latest_version%.*}
latest_kernel_key=$(curl "http://spec.ccfc.min.s3.amazonaws.com/?prefix=firecracker-ci/$CI_VERSION/$ARCH/vmlinux-&list-type=2" \
    | grep -oP "(?<=<Key>)(firecracker-ci/$CI_VERSION/$ARCH/vmlinux-[0-9]+\.[0-9]+\.[0-9]{1,3})(?=</Key>)" \
    | sort -V | tail -1)

wget "https://s3.amazonaws.com/spec.ccfc.min/${latest_kernel_key}"

Rootfs time — exported straight out of a container image:

docker pull mcr.microsoft.com/dotnet/aspnet:9.0
CONTAINER_ID=$(docker create mcr.microsoft.com/dotnet/aspnet:9.0)
docker export $CONTAINER_ID -o rootfs.tar
dd if=/dev/zero of=rootfs.ext4 bs=1M count=2048
mkfs.ext4 rootfs.ext4
mkdir /mnt/rootfs
sudo mount -o loop rootfs.ext4 /mnt/rootfs
sudo tar -xf rootfs.tar -C /mnt/rootfs

There’s a twist: containers don’t use an init process, so most container images don’t ship one. I wrote a sample init and moved on.

It works. It has no internet.

Neither container image I tried had ip installed, so I configured networking through the kernel command line instead, pointed the resolver at 1.1.1.1, and this time it really worked.

Next

Each Firecracker process should serve exactly one tenant — which means I now need to launch the Firecracker binary from C#, talk to its API, and boot VMs programmatically.

Get in touch
[email protected]
Phone
+45 60 13 10 54
GitHub
@aarani
LinkedIn
in/afshinarani
Based in
Copenhagen, Denmark

A CV is available on request — I share it privately rather than publishing it.