· 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-containerdexists, 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.