Building an AWS Lambda-like Runtime with Firecracker MicroVMs | by Vivek Jadhav | May, 2026 | MediumSitemapOpen in appSign up<br>Sign in
Medium Logo
Get app<br>Write
Search
Sign up<br>Sign in
Building an AWS Lambda-like Runtime with Firecracker MicroVMs
Vivek Jadhav
5 min read·<br>3 days ago
Listen
Share
I’ve always found serverless platforms a little magical. You deploy a function, send a request, and somewhere in the world, resources are allocated automatically and your code gets executed. But the more I used these platforms, the more I wanted to understand what was actually happening underneath.<br>So I built a lightweight serverless runtime from scratch using Firecracker microVMs.<br>This is an honest account of what I learned — including the parts where I had no idea what I was doing.<br>The full project is on GitHub: https://github.com/vivek1504/serverless-runtime<br>Why Firecracker?<br>Traditional containers boot fast but offer weak isolation. Traditional VMs offer strong isolation but boot slowly. Neither is a great fit for serverless, where you need to run arbitrary user-submitted code quickly and safely on shared infrastructure.<br>Firecracker is a minimal VMM (Virtual Machine Monitor) developed by Amazon, built specifically for Lambda and Fargate. Instead of booting full VMs, it launches microVMs with a stripped-down device model, minimal attack surface, and KVM-based virtualization — fast enough for serverless workloads, isolated enough to run untrusted code.<br>The Cold Start Problem<br>The biggest challenge in any serverless system is cold start latency. A naive execution looks like this:<br>Request → Create VM → Boot kernel → Start runtime → Load user code → Execute handlerEven optimized microVMs take time to boot. In my implementation, a full cold boot clocked at around ~200ms. At serverless scale, that becomes noticeable and painful.<br>The solution is snapshots. Boot the VM once, initialize the runtime, then take a memory snapshot of that state. For every subsequent invocation, restore from the snapshot instead of booting fresh.<br>- Full VM boot → ~200ms<br>- Snapshot restore → ~1–5ms<br>That’s a 40–200x improvement. Most of the work in this project went into making that restoration reliable.<br>System Architecture<br>The runtime has two layers:<br>Control Plane — handles deployment, VM lifecycle, snapshot orchestration, scheduling, and request routing.<br>Execution Layer — handles isolated function execution inside Firecracker microVMs.<br>both of this layers communicate using vsock.<br>Press enter or click to view image in full size
When a function is deployed:<br>The function zip is uploaded<br>A minimal root filesystem is prepared<br>User code is extracted into the VM filesystem<br>Firecracker boots the microVM<br>The runtime initializes inside the VM<br>A snapshot is taken<br>Future invocations restore directly from step 6.<br>Three Things That Were Harder Than Expected<br>1. The PID 1 Rabbit Hole<br>The first time I booted a custom microVM, I got a kernel panic almost immediately. The VM crashed and gave me nothing useful to debug with no SSH, no shell, just silence.<br>Linux expects PID 1 to stay alive and take responsibility for the entire userspace. It needs to reap zombie processes, forward signals, and manage process lifecycle. My init script was exiting after launching the runtime, which the kernel interpreted as a catastrophic failure.<br>I spent a whole afternoon trying to figureout why my VMs were crashing. The fix was tini — a minimal init system that handles zombie cleanup, signal forwarding, and process lifecycle correctly. One binary, many headaches solved.<br>2. Snapshot Boundaries Are Architectural Decisions<br>I assumed snapshotting was straightforward — freeze the VM state, save it, restore it later. It’s not.<br>The tricky part is timing. Snapshot too early, and the runtime isn’t initialized. Snapshot too late, and you’ve captured live socket state, open vsock connections, or in-flight network handles that won’t survive a restore correctly.<br>I learned this the hard way when restored VMs would inherit inherited identical vsock state, causing guest-host communication to collide or hang immediately after restorek<br>The fix was using a newer Firecracker development build with improved vsock handling during snapshot restore<br>3. vsock IPC Race Conditions<br>Host-to-VM communication uses vsock . Conceptually simple but In practice, much harder to implement.<br>The failure mode I kept hitting was the guest runtime would start listening on a vsock port before the host-side bridge was ready, or vice versa. The connection would either fail silently or hang indefinitely. Using socat as a bridge introduced its own synchronization race conditions on top.<br>The fix required careful lifecycle ordering — host bridge first, then guest listener, with explicit readiness signaling before any IPC traffic. But the bigger lesson was that vsock is only a byte-stream transport. Message framing, connection lifecycle, and error propagation are entirely your responsibility. There are no built-in message...