{"schemaVersion":1,"id":"https://lucioduran.com/blog/unikernels-firecracker-microvm-serverless","url":"https://lucioduran.com/blog/unikernels-firecracker-microvm-serverless","title":"Unikernels vs MicroVMs: Firecracker Internals, Unikraft, and the Real Cost of Serverless Isolation","description":"A comprehensive comparison of Firecracker MicroVMs and Unikraft unikernels: boot time benchmarks, memory footprint analysis, KVM internals, and what actually provides isolation for serverless functions in production.","language":"en","datePublished":"2025-12-09","dateModified":null,"contentFormat":"text/markdown","content":"Infrastructure\n\n# Unikernels vs MicroVMs: Firecracker Internals, Unikraft, and the Real Cost of Serverless Isolation\n\n2025-12-098 min read\n\nLucio Durán\nEngineering Manager & AI Solutions Architect\n\nAlso available in: [Español](<https://lucioduran.com/es/blog/unikernels-firecracker-microvm-serverless>)\n\n## The Isolation Problem\n\nLet's start with why this matters. In a serverless platform, you're running untrusted code from different tenants on shared hardware. The isolation mechanism must guarantee that:\n\n1. Tenant A cannot read Tenant B's memory\n2. Tenant A cannot access Tenant B's filesystem\n3. Tenant A cannot consume more CPU/memory than allocated\n4. A bug or exploit in Tenant A's code cannot compromise the host\n\nContainers provide property 2 (via mount namespaces), property 3 (via cgroups), and partially property 1 (via PID/user namespaces). But they fail critically on property 4: all containers share the host kernel, and the Linux kernel has a massive attack surface. A kernel vulnerability means a container escape, and Linux gets kernel CVEs regularly.\n\n## Firecracker: A VMM Written in Rust\n\n[Firecracker](<https://firecracker-microvm.github.io/>) was built by [AWS](<https://aws.com/>) specifically for Lambda and Fargate. It's a Virtual Machine Monitor (VMM) that uses KVM (Linux's built-in hypervisor) to create lightweight virtual machines. Each VM gets its own Linux kernel, its own memory space, and its own virtual devices.\n\nThe architecture:\n\n```\n┌─────────────────────────────────────────┐\n│ HOST OS │\n│ ┌──────────┐ ┌──────────┐ ┌────────┐ │\n│ │Firecracker│ │Firecracker│ │ ... │ │\n│ │ VMM │ │ VMM │ │ │ │\n│ │ (Rust) │ │ (Rust) │ │ │ │\n│ ├──────────┤ ├──────────┤ ├────────┤ │\n│ │ KVM │ │ KVM │ │ KVM │ │\n│ ├──────────┤ ├──────────┤ ├────────┤ │\n│ │Guest │ │Guest │ │Guest │ │\n│ │Linux │ │Linux │ │Linux │ │\n│ │Kernel │ │Kernel │ │Kernel │ │\n│ ├──────────┤ ├──────────┤ ├────────┤ │\n│ │Function A│ │Function B│ │Func C │ │\n│ └──────────┘ └──────────┘ └────────┘ │\n└─────────────────────────────────────────┘\n```\n\nWhat makes Firecracker a \"micro\" VMM:\n\n**Minimal device model**: Traditional VMMs (QEMU) emulate hundreds of devices, USB controllers, sound cards, legacy ISA buses. Firecracker emulates exactly 5 device types: virtio-net, virtio-block, serial console, a minimal i8042 keyboard controller (for shutdown), and a KVM clock. That's it. Fewer devices = smaller attack surface = less code to audit.\n\n**No BIOS, no UEFI**: Firecracker loads the Linux kernel directly into guest memory and sets up the boot parameters manually. This skips the entire firmware boot sequence that adds seconds to traditional VM boot times.\n\n**Rate limiters built in**: Every virtio device has configurable rate limiters for both bandwidth and operations per second. This is resource isolation at the VMM level, not bolted on after the fact.\n\nHere's what Firecracker does at startup:\n\n```rust\n// Simplified Firecracker boot sequence (from the source)\nfn boot_microvm(config: &VmConfig) -> Result<Vm> {\n // 1. Open /dev/kvm and create a VM\n let kvm_fd = open(\"/dev/kvm\", O_RDWR)?;\n let vm_fd = ioctl(kvm_fd, KVM_CREATE_VM, 0)?;\n\n // 2. Configure guest memory (typically 128MB-2GB)\n let guest_mem = GuestMemory::new(config.mem_size_mib)?;\n ioctl(vm_fd, KVM_SET_USER_MEMORY_REGION, &guest_mem.region())?;\n\n // 3. Load the kernel directly into guest memory\n let kernel = load_kernel(&config.kernel_path, &guest_mem)?;\n\n // 4. Set up boot parameters (command line, initrd, etc.)\n let boot_params = configure_boot_params(kernel.entry_point, &config)?;\n\n // 5. Create vCPUs and set registers\n let vcpu_fd = ioctl(vm_fd, KVM_CREATE_VCPU, 0)?;\n set_initial_registers(vcpu_fd, &boot_params)?;\n\n // 6. Set up virtio devices (net, block)\n let mmio_bus = setup_virtio_devices(&config, &guest_mem)?;\n\n // 7. Enter the VM loop (runs guest code via KVM_RUN)\n Ok(Vm { vm_fd, vcpu_fd, guest_mem, mmio_bus })\n}\n```\n\nThe VM loop itself is surprisingly simple:\n\n```rust\nloop {\n // Run guest code until it exits (I/O, interrupt, etc.)\n ioctl(vcpu_fd, KVM_RUN, 0)?;\n\n match vcpu_fd.exit_reason() {\n KvmExit::IoOut { port, data } => {\n // Handle I/O port access (serial console, etc.)\n handle_io_out(port, data)?;\n }\n KvmExit::MmioWrite { addr, data } => {\n // Handle MMIO access (virtio devices)\n mmio_bus.write(addr, data)?;\n }\n KvmExit::Shutdown => break,\n _ => { /* handle other exit reasons */ }\n }\n}\n```\n\n## Boot Time: Real Numbers\n\nFirecracker boot times measured on an m5.metal instance (bare-metal, no nested virtualization):\n\n```\nPhase Time\n─────────────────────────────────────\nKVM_CREATE_VM ioctl: 0.2ms\nMemory setup (256MB): 1.8ms\nKernel load (5.10, stripped): 3.1ms\nvCPU setup + register init: 0.4ms\nVirtio device setup: 0.8ms\n─────────────────────────────────────\nVMM ready to KVM_RUN: 6.3ms\nGuest kernel boot to init: ~125ms\nAlpine Linux init to shell: ~30ms\n─────────────────────────────────────\nTotal to running userspace: ~160ms\n```\n\n160ms from nothing to a running Linux userspace. That's remarkable, but for serverless functions that need to respond in \\<200ms, that boot time is still a significant chunk of the latency budget. This is where snapshot-restore comes in.\n\n### Firecracker Snapshots\n\nFirecracker can snapshot a running VM's entire state (CPU registers, memory, device state) to disk, and restore it in ~5ms:\n\n```bash\n# Create a snapshot of a running VM\ncurl --unix-socket /tmp/firecracker.socket -X PUT \\\n http://localhost/snapshot/create \\\n -d '{\n \"snapshot_type\": \"Full\",\n \"snapshot_path\": \"/snapshots/function-v1.snap\",\n \"mem_file_path\": \"/snapshots/function-v1.mem\"\n }'\n\n# Restore a VM from snapshot (~5ms)\ncurl --unix-socket /tmp/firecracker.socket -X PUT \\\n http://localhost/snapshot/load \\\n -d '{\n \"snapshot_path\": \"/snapshots/function-v1.snap\",\n \"mem_file_path\": \"/snapshots/function-v1.mem\"\n }'\n```\n\nAWS Lambda uses this exact mechanism: when you deploy a function, Lambda boots it once, snapshots the initialized state, and restores from snapshot on subsequent cold starts. This is why Lambda cold starts are ~200ms for most runtimes instead of the 5+ seconds you'd expect from a full VM boot.\n\n## Unikraft: Library Operating Systems\n\nUnikernels take a fundamentally different approach. Instead of running a full Linux kernel with thousands of syscalls, drivers, and subsystems, a unikernel includes only the OS components that the application actually uses.\n\n[Unikraft](<https://unikraft.org/>) is the most actively developed unikernel framework. It's a library OS, you pick the components you need:\n\n```\nTraditional VM:\n Application → libc → Linux Kernel (25M+ lines of code) → Hardware\n\nUnikraft Unikernel:\n Application → lib/nolibc → lib/vfscore → lib/9pfs → KVM driver\n → lib/uknetdev → virtio-net driver\n → lib/uksched → cooperative scheduler\n (total: ~100K lines of code)\n```\n\nA Unikraft unikernel is compiled into a single binary that boots directly on KVM (or Xen):\n\n```makefile\n# Unikraft application configuration (kraft.yaml)\nspec: v0.6\n\nunikraft:\n version: stable\n kconfig:\n CONFIG_LIBUKBOOT: y\n CONFIG_LIBUKLIBPARAM: y\n CONFIG_LIBPOSIX_PROCESS: y\n\nlibraries:\n musl:\n version: stable\n lwip:\n version: stable\n kconfig:\n CONFIG_LWIP_TCP: y\n CONFIG_LWIP_DHCP: y\n\ntargets:\n - platform: qemu\n architecture: x86_64\n - platform: firecracker\n architecture: x86_64\n```\n\nBuild and run:\n\n```bash\n# Build the unikernel\nkraft build --target firecracker/x86_64 --log-level debug\n\n# Run with Firecracker\nkraft run --target firecracker/x86_64 \\\n --memory 64 \\\n --network bridge:kraft0 \\\n -- /my-http-server --port 8080\n```\n\n## Head-to-Head Comparison\n\nAll measurements on the same hardware (c5.metal, bare-metal):\n\n```\n Firecracker Unikraft Docker\n (Alpine guest) (HTTP server) (Alpine)\n────────────────────────────────────────────────────────────────────────────\nBoot to first HTTP response: 160ms 8ms ~800ms*\nMemory footprint (idle): 32MB 2MB ~15MB\nImage size: ~50MB ~1.2MB ~8MB\nSupported syscalls: ~400 ~160 ~400\nSecurity boundary: KVM + VMM KVM Namespace\nDebugging experience: Good (gdb) Painful Excellent\nEcosystem compatibility: Linux binaries Recompile req'd Everything\n────────────────────────────────────────────────────────────────────────────\n* Docker measured including containerd/shim overhead\n```\n\nThat 8ms boot time for Unikraft is not a typo. A unikernel has no BIOS, no bootloader, no kernel module loading, no systemd, no init process. It goes directly from \"VM started\" to \"application main() running.\" The memory footprint tells the same story, 2MB versus 32MB because there's no kernel memory management overhead, no process scheduler, no driver framework for devices that don't exist.\n\nBut look at the ecosystem compatibility row. Firecracker runs any Linux binary, Node.js, Python, Go, Java, anything with a Linux build. Unikraft requires recompilation against its libc (musl-based) and may not support all syscalls your application uses. In one case, two days were spent trying to get a Python Flask app running on Unikraft before discovering that it used ` sendfile() ` which was not implemented in Unikraft's network stack at the time.\n\n## The Security Model Comparison\n\nBoth approaches use KVM for memory isolation, so the hardware-enforced boundary is identical. The difference is in the attack surface within the VM:\n\n```\nFirecracker MicroVM attack surface:\n - Guest Linux kernel (~25M lines of C)\n - Firecracker VMM (~50K lines of Rust)\n - 5 virtual device implementations\n Total guest attack surface: Large kernel, but isolated by KVM\n\nUnikraft unikernel attack surface:\n - Unikraft kernel (~100K lines of C)\n - Only included libraries\n - Same 5 virtual device implementations (Firecracker backend)\n Total guest attack surface: Minimal kernel, isolated by KVM\n```\n\nIf an attacker achieves code execution within the guest, a Unikraft unikernel gives them almost nothing to work with, no shell, no filesystem utilities, no kernel exploit gadgets. A Firecracker guest running Alpine Linux has the full Linux kernel attack surface from within.\n\nHowever, in practice, the KVM boundary is what matters most. The guest kernel attack surface is relevant only if the attacker has already compromised the application, at which point you're in damage-limitation mode regardless.\n\n## Practical Production Choices\n\nFor serverless function isolation: **Firecracker**. The ecosystem compatibility is worth the extra memory and boot time. With snapshot-restore, cold starts are ~5ms anyway. The operational maturity is unmatched, AWS runs billions of Lambda invocations per day on Firecracker.\n\nFor edge computing with known, controlled workloads: **Unikraft**. When the application's behavior is well-characterized (serve static files, run WASM modules, proxy HTTP), a 2MB unikernel that boots in 8ms is irresistible. The operational overhead is higher because debugging is harder, but for well-characterized workloads, it's worth it.\n\nFor everything else: **Firecracker with Kata Containers**. This gives you container ergonomics (docker build, docker push, standard CI/CD) with VM isolation. The boot time overhead is ~150ms, which is acceptable for most workloads that aren't latency-critical serverless functions.\n\nThe honest truth is that unikernels have been \"the future\" for a decade, and they're still not mainstream. Firecracker succeeded where unikernels haven't because it made a pragmatic tradeoff: accept a full Linux kernel with its overhead and attack surface, but make it fast enough and isolated enough that it doesn't matter. Sometimes good enough, shipped and battle-tested, beats perfect and still-maturing.\n\n## Frequently asked questions\n\n### Why doesn't AWS Lambda just use containers for isolation?\n\nContainers share the host kernel, and kernel vulnerabilities allow escapes. Lambda runs on Firecracker MicroVMs where each function invocation gets a dedicated Linux kernel instance with minimal attack surface. A container escape gives you host access; a VM escape requires a KVM vulnerability, which is a much harder target.\n\n### Are unikernels production-ready?\n\nIt depends on the unikernel framework. Unikraft has reached production readiness for specific workloads, static HTTP servers, WASM runtimes, specific language runtimes. MirageOS is used in production at some organizations. But general-purpose unikernels that can run arbitrary Linux binaries don't exist. You're trading compatibility for performance and security.\n\n### Can Firecracker run containers?\n\nNot directly. Firecracker is a VMM (Virtual Machine Monitor) that boots a Linux kernel and a root filesystem. However, tools like Kata Containers and AWS's containerd-snapshotter use Firecracker as the isolation backend for OCI container images. The container image becomes the root filesystem for the MicroVM, giving you container ergonomics with VM isolation.\n\n## Tips\n\n1. **Tip:** When benchmarking Firecracker boot times, measure from the ` CreateVM ` ioctl to the first successful HTTP response from the guest, not to kernel boot. The kernel boots in ~125ms but your application's init adds 20-500ms depending on the runtime. The number that matters is time-to-first-response.\n2. **Tip:** Use Firecracker's ` jailer ` binary in production, not bare ` firecracker `. The jailer sets up cgroups, chroot, seccomp filters, and drops privileges before starting the VMM. Running without it is like running Docker without namespaces, technically works, but the outer security layer is removed.\n3. **Tip:** For Unikraft, start with the ` kraft ` CLI and the pre-built app catalog at ` unikraft.org/apps ` before trying to build custom images. Building a Unikraft unikernel from scratch involves configuring a KConfig menu with 400+ options, and getting it wrong means a kernel panic with no debugger. Learn the tooling first.\n\n- unikernels\n- firecracker\n- microvm\n- serverless\n- unikraft\n- kvm\n- virtualization\n- cloud-infrastructure\n\n[Back to Blog](<https://lucioduran.com/blog>)","headings":[{"level":1,"text":"Unikernels vs MicroVMs: Firecracker Internals, Unikraft, and the Real Cost of Serverless Isolation","url":"https://lucioduran.com/blog/unikernels-firecracker-microvm-serverless#title"},{"level":2,"text":"The Isolation Problem","url":"https://lucioduran.com/blog/unikernels-firecracker-microvm-serverless#the-isolation-problem"},{"level":2,"text":"Firecracker: A VMM Written in Rust","url":"https://lucioduran.com/blog/unikernels-firecracker-microvm-serverless#firecracker-a-vmm-written-in-rust"},{"level":2,"text":"Boot Time: Real Numbers","url":"https://lucioduran.com/blog/unikernels-firecracker-microvm-serverless#boot-time-real-numbers"},{"level":3,"text":"Firecracker Snapshots","url":"https://lucioduran.com/blog/unikernels-firecracker-microvm-serverless#firecracker-snapshots"},{"level":2,"text":"Unikraft: Library Operating Systems","url":"https://lucioduran.com/blog/unikernels-firecracker-microvm-serverless#unikraft-library-operating-systems"},{"level":2,"text":"Head-to-Head Comparison","url":"https://lucioduran.com/blog/unikernels-firecracker-microvm-serverless#head-to-head-comparison"},{"level":2,"text":"The Security Model Comparison","url":"https://lucioduran.com/blog/unikernels-firecracker-microvm-serverless#the-security-model-comparison"},{"level":2,"text":"Practical Production Choices","url":"https://lucioduran.com/blog/unikernels-firecracker-microvm-serverless#practical-production-choices"},{"level":2,"text":"Frequently asked questions","url":"https://lucioduran.com/blog/unikernels-firecracker-microvm-serverless#questions"},{"level":3,"text":"Why doesn't AWS Lambda just use containers for isolation?","url":"https://lucioduran.com/blog/unikernels-firecracker-microvm-serverless#question-1"},{"level":3,"text":"Are unikernels production-ready?","url":"https://lucioduran.com/blog/unikernels-firecracker-microvm-serverless#question-2"},{"level":3,"text":"Can Firecracker run containers?","url":"https://lucioduran.com/blog/unikernels-firecracker-microvm-serverless#question-3"},{"level":2,"text":"Tips","url":"https://lucioduran.com/blog/unikernels-firecracker-microvm-serverless#article-tips"}],"sections":[{"heading":"Unikernels vs MicroVMs: Firecracker Internals, Unikraft, and the Real Cost of Serverless Isolation","level":1,"url":"https://lucioduran.com/blog/unikernels-firecracker-microvm-serverless","content":"Infrastructure"},{"heading":"Unikernels vs MicroVMs: Firecracker Internals, Unikraft, and the Real Cost of Serverless Isolation","level":1,"url":"https://lucioduran.com/blog/unikernels-firecracker-microvm-serverless#title","content":"2025-12-098 min read\n\nLucio Durán\nEngineering Manager & AI Solutions Architect\n\nAlso available in: [Español](<https://lucioduran.com/es/blog/unikernels-firecracker-microvm-serverless>)"},{"heading":"The Isolation Problem","level":2,"url":"https://lucioduran.com/blog/unikernels-firecracker-microvm-serverless#the-isolation-problem","content":"Let's start with why this matters. In a serverless platform, you're running untrusted code from different tenants on shared hardware. The isolation mechanism must guarantee that:\n\n1. Tenant A cannot read Tenant B's memory\n2. Tenant A cannot access Tenant B's filesystem\n3. Tenant A cannot consume more CPU/memory than allocated\n4. A bug or exploit in Tenant A's code cannot compromise the host\n\nContainers provide property 2 (via mount namespaces), property 3 (via cgroups), and partially property 1 (via PID/user namespaces). But they fail critically on property 4: all containers share the host kernel, and the Linux kernel has a massive attack surface. A kernel vulnerability means a container escape, and Linux gets kernel CVEs regularly."},{"heading":"Firecracker: A VMM Written in Rust","level":2,"url":"https://lucioduran.com/blog/unikernels-firecracker-microvm-serverless#firecracker-a-vmm-written-in-rust","content":"[Firecracker](<https://firecracker-microvm.github.io/>) was built by [AWS](<https://aws.com/>) specifically for Lambda and Fargate. It's a Virtual Machine Monitor (VMM) that uses KVM (Linux's built-in hypervisor) to create lightweight virtual machines. Each VM gets its own Linux kernel, its own memory space, and its own virtual devices.\n\nThe architecture:\n\n```\n┌─────────────────────────────────────────┐\n│ HOST OS │\n│ ┌──────────┐ ┌──────────┐ ┌────────┐ │\n│ │Firecracker│ │Firecracker│ │ ... │ │\n│ │ VMM │ │ VMM │ │ │ │\n│ │ (Rust) │ │ (Rust) │ │ │ │\n│ ├──────────┤ ├──────────┤ ├────────┤ │\n│ │ KVM │ │ KVM │ │ KVM │ │\n│ ├──────────┤ ├──────────┤ ├────────┤ │\n│ │Guest │ │Guest │ │Guest │ │\n│ │Linux │ │Linux │ │Linux │ │\n│ │Kernel │ │Kernel │ │Kernel │ │\n│ ├──────────┤ ├──────────┤ ├────────┤ │\n│ │Function A│ │Function B│ │Func C │ │\n│ └──────────┘ └──────────┘ └────────┘ │\n└─────────────────────────────────────────┘\n```\n\nWhat makes Firecracker a \"micro\" VMM:\n\n**Minimal device model**: Traditional VMMs (QEMU) emulate hundreds of devices, USB controllers, sound cards, legacy ISA buses. Firecracker emulates exactly 5 device types: virtio-net, virtio-block, serial console, a minimal i8042 keyboard controller (for shutdown), and a KVM clock. That's it. Fewer devices = smaller attack surface = less code to audit.\n\n**No BIOS, no UEFI**: Firecracker loads the Linux kernel directly into guest memory and sets up the boot parameters manually. This skips the entire firmware boot sequence that adds seconds to traditional VM boot times.\n\n**Rate limiters built in**: Every virtio device has configurable rate limiters for both bandwidth and operations per second. This is resource isolation at the VMM level, not bolted on after the fact.\n\nHere's what Firecracker does at startup:\n\n```rust\n// Simplified Firecracker boot sequence (from the source)\nfn boot_microvm(config: &VmConfig) -> Result<Vm> {\n // 1. Open /dev/kvm and create a VM\n let kvm_fd = open(\"/dev/kvm\", O_RDWR)?;\n let vm_fd = ioctl(kvm_fd, KVM_CREATE_VM, 0)?;\n\n // 2. Configure guest memory (typically 128MB-2GB)\n let guest_mem = GuestMemory::new(config.mem_size_mib)?;\n ioctl(vm_fd, KVM_SET_USER_MEMORY_REGION, &guest_mem.region())?;\n\n // 3. Load the kernel directly into guest memory\n let kernel = load_kernel(&config.kernel_path, &guest_mem)?;\n\n // 4. Set up boot parameters (command line, initrd, etc.)\n let boot_params = configure_boot_params(kernel.entry_point, &config)?;\n\n // 5. Create vCPUs and set registers\n let vcpu_fd = ioctl(vm_fd, KVM_CREATE_VCPU, 0)?;\n set_initial_registers(vcpu_fd, &boot_params)?;\n\n // 6. Set up virtio devices (net, block)\n let mmio_bus = setup_virtio_devices(&config, &guest_mem)?;\n\n // 7. Enter the VM loop (runs guest code via KVM_RUN)\n Ok(Vm { vm_fd, vcpu_fd, guest_mem, mmio_bus })\n}\n```\n\nThe VM loop itself is surprisingly simple:\n\n```rust\nloop {\n // Run guest code until it exits (I/O, interrupt, etc.)\n ioctl(vcpu_fd, KVM_RUN, 0)?;\n\n match vcpu_fd.exit_reason() {\n KvmExit::IoOut { port, data } => {\n // Handle I/O port access (serial console, etc.)\n handle_io_out(port, data)?;\n }\n KvmExit::MmioWrite { addr, data } => {\n // Handle MMIO access (virtio devices)\n mmio_bus.write(addr, data)?;\n }\n KvmExit::Shutdown => break,\n _ => { /* handle other exit reasons */ }\n }\n}\n```"},{"heading":"Boot Time: Real Numbers","level":2,"url":"https://lucioduran.com/blog/unikernels-firecracker-microvm-serverless#boot-time-real-numbers","content":"Firecracker boot times measured on an m5.metal instance (bare-metal, no nested virtualization):\n\n```\nPhase Time\n─────────────────────────────────────\nKVM_CREATE_VM ioctl: 0.2ms\nMemory setup (256MB): 1.8ms\nKernel load (5.10, stripped): 3.1ms\nvCPU setup + register init: 0.4ms\nVirtio device setup: 0.8ms\n─────────────────────────────────────\nVMM ready to KVM_RUN: 6.3ms\nGuest kernel boot to init: ~125ms\nAlpine Linux init to shell: ~30ms\n─────────────────────────────────────\nTotal to running userspace: ~160ms\n```\n\n160ms from nothing to a running Linux userspace. That's remarkable, but for serverless functions that need to respond in \\<200ms, that boot time is still a significant chunk of the latency budget. This is where snapshot-restore comes in."},{"heading":"Firecracker Snapshots","level":3,"url":"https://lucioduran.com/blog/unikernels-firecracker-microvm-serverless#firecracker-snapshots","content":"Firecracker can snapshot a running VM's entire state (CPU registers, memory, device state) to disk, and restore it in ~5ms:\n\n```bash\n# Create a snapshot of a running VM\ncurl --unix-socket /tmp/firecracker.socket -X PUT \\\n http://localhost/snapshot/create \\\n -d '{\n \"snapshot_type\": \"Full\",\n \"snapshot_path\": \"/snapshots/function-v1.snap\",\n \"mem_file_path\": \"/snapshots/function-v1.mem\"\n }'\n\n# Restore a VM from snapshot (~5ms)\ncurl --unix-socket /tmp/firecracker.socket -X PUT \\\n http://localhost/snapshot/load \\\n -d '{\n \"snapshot_path\": \"/snapshots/function-v1.snap\",\n \"mem_file_path\": \"/snapshots/function-v1.mem\"\n }'\n```\n\nAWS Lambda uses this exact mechanism: when you deploy a function, Lambda boots it once, snapshots the initialized state, and restores from snapshot on subsequent cold starts. This is why Lambda cold starts are ~200ms for most runtimes instead of the 5+ seconds you'd expect from a full VM boot."},{"heading":"Unikraft: Library Operating Systems","level":2,"url":"https://lucioduran.com/blog/unikernels-firecracker-microvm-serverless#unikraft-library-operating-systems","content":"Unikernels take a fundamentally different approach. Instead of running a full Linux kernel with thousands of syscalls, drivers, and subsystems, a unikernel includes only the OS components that the application actually uses.\n\n[Unikraft](<https://unikraft.org/>) is the most actively developed unikernel framework. It's a library OS, you pick the components you need:\n\n```\nTraditional VM:\n Application → libc → Linux Kernel (25M+ lines of code) → Hardware\n\nUnikraft Unikernel:\n Application → lib/nolibc → lib/vfscore → lib/9pfs → KVM driver\n → lib/uknetdev → virtio-net driver\n → lib/uksched → cooperative scheduler\n (total: ~100K lines of code)\n```\n\nA Unikraft unikernel is compiled into a single binary that boots directly on KVM (or Xen):\n\n```makefile\n# Unikraft application configuration (kraft.yaml)\nspec: v0.6\n\nunikraft:\n version: stable\n kconfig:\n CONFIG_LIBUKBOOT: y\n CONFIG_LIBUKLIBPARAM: y\n CONFIG_LIBPOSIX_PROCESS: y\n\nlibraries:\n musl:\n version: stable\n lwip:\n version: stable\n kconfig:\n CONFIG_LWIP_TCP: y\n CONFIG_LWIP_DHCP: y\n\ntargets:\n - platform: qemu\n architecture: x86_64\n - platform: firecracker\n architecture: x86_64\n```\n\nBuild and run:\n\n```bash\n# Build the unikernel\nkraft build --target firecracker/x86_64 --log-level debug\n\n# Run with Firecracker\nkraft run --target firecracker/x86_64 \\\n --memory 64 \\\n --network bridge:kraft0 \\\n -- /my-http-server --port 8080\n```"},{"heading":"Head-to-Head Comparison","level":2,"url":"https://lucioduran.com/blog/unikernels-firecracker-microvm-serverless#head-to-head-comparison","content":"All measurements on the same hardware (c5.metal, bare-metal):\n\n```\n Firecracker Unikraft Docker\n (Alpine guest) (HTTP server) (Alpine)\n────────────────────────────────────────────────────────────────────────────\nBoot to first HTTP response: 160ms 8ms ~800ms*\nMemory footprint (idle): 32MB 2MB ~15MB\nImage size: ~50MB ~1.2MB ~8MB\nSupported syscalls: ~400 ~160 ~400\nSecurity boundary: KVM + VMM KVM Namespace\nDebugging experience: Good (gdb) Painful Excellent\nEcosystem compatibility: Linux binaries Recompile req'd Everything\n────────────────────────────────────────────────────────────────────────────\n* Docker measured including containerd/shim overhead\n```\n\nThat 8ms boot time for Unikraft is not a typo. A unikernel has no BIOS, no bootloader, no kernel module loading, no systemd, no init process. It goes directly from \"VM started\" to \"application main() running.\" The memory footprint tells the same story, 2MB versus 32MB because there's no kernel memory management overhead, no process scheduler, no driver framework for devices that don't exist.\n\nBut look at the ecosystem compatibility row. Firecracker runs any Linux binary, Node.js, Python, Go, Java, anything with a Linux build. Unikraft requires recompilation against its libc (musl-based) and may not support all syscalls your application uses. In one case, two days were spent trying to get a Python Flask app running on Unikraft before discovering that it used ` sendfile() ` which was not implemented in Unikraft's network stack at the time."},{"heading":"The Security Model Comparison","level":2,"url":"https://lucioduran.com/blog/unikernels-firecracker-microvm-serverless#the-security-model-comparison","content":"Both approaches use KVM for memory isolation, so the hardware-enforced boundary is identical. The difference is in the attack surface within the VM:\n\n```\nFirecracker MicroVM attack surface:\n - Guest Linux kernel (~25M lines of C)\n - Firecracker VMM (~50K lines of Rust)\n - 5 virtual device implementations\n Total guest attack surface: Large kernel, but isolated by KVM\n\nUnikraft unikernel attack surface:\n - Unikraft kernel (~100K lines of C)\n - Only included libraries\n - Same 5 virtual device implementations (Firecracker backend)\n Total guest attack surface: Minimal kernel, isolated by KVM\n```\n\nIf an attacker achieves code execution within the guest, a Unikraft unikernel gives them almost nothing to work with, no shell, no filesystem utilities, no kernel exploit gadgets. A Firecracker guest running Alpine Linux has the full Linux kernel attack surface from within.\n\nHowever, in practice, the KVM boundary is what matters most. The guest kernel attack surface is relevant only if the attacker has already compromised the application, at which point you're in damage-limitation mode regardless."},{"heading":"Practical Production Choices","level":2,"url":"https://lucioduran.com/blog/unikernels-firecracker-microvm-serverless#practical-production-choices","content":"For serverless function isolation: **Firecracker**. The ecosystem compatibility is worth the extra memory and boot time. With snapshot-restore, cold starts are ~5ms anyway. The operational maturity is unmatched, AWS runs billions of Lambda invocations per day on Firecracker.\n\nFor edge computing with known, controlled workloads: **Unikraft**. When the application's behavior is well-characterized (serve static files, run WASM modules, proxy HTTP), a 2MB unikernel that boots in 8ms is irresistible. The operational overhead is higher because debugging is harder, but for well-characterized workloads, it's worth it.\n\nFor everything else: **Firecracker with Kata Containers**. This gives you container ergonomics (docker build, docker push, standard CI/CD) with VM isolation. The boot time overhead is ~150ms, which is acceptable for most workloads that aren't latency-critical serverless functions.\n\nThe honest truth is that unikernels have been \"the future\" for a decade, and they're still not mainstream. Firecracker succeeded where unikernels haven't because it made a pragmatic tradeoff: accept a full Linux kernel with its overhead and attack surface, but make it fast enough and isolated enough that it doesn't matter. Sometimes good enough, shipped and battle-tested, beats perfect and still-maturing."},{"heading":"Frequently asked questions","level":2,"url":"https://lucioduran.com/blog/unikernels-firecracker-microvm-serverless#questions","content":""},{"heading":"Why doesn't AWS Lambda just use containers for isolation?","level":3,"url":"https://lucioduran.com/blog/unikernels-firecracker-microvm-serverless#question-1","content":"Containers share the host kernel, and kernel vulnerabilities allow escapes. Lambda runs on Firecracker MicroVMs where each function invocation gets a dedicated Linux kernel instance with minimal attack surface. A container escape gives you host access; a VM escape requires a KVM vulnerability, which is a much harder target."},{"heading":"Are unikernels production-ready?","level":3,"url":"https://lucioduran.com/blog/unikernels-firecracker-microvm-serverless#question-2","content":"It depends on the unikernel framework. Unikraft has reached production readiness for specific workloads, static HTTP servers, WASM runtimes, specific language runtimes. MirageOS is used in production at some organizations. But general-purpose unikernels that can run arbitrary Linux binaries don't exist. You're trading compatibility for performance and security."},{"heading":"Can Firecracker run containers?","level":3,"url":"https://lucioduran.com/blog/unikernels-firecracker-microvm-serverless#question-3","content":"Not directly. Firecracker is a VMM (Virtual Machine Monitor) that boots a Linux kernel and a root filesystem. However, tools like Kata Containers and AWS's containerd-snapshotter use Firecracker as the isolation backend for OCI container images. The container image becomes the root filesystem for the MicroVM, giving you container ergonomics with VM isolation."},{"heading":"Tips","level":2,"url":"https://lucioduran.com/blog/unikernels-firecracker-microvm-serverless#article-tips","content":"1. **Tip:** When benchmarking Firecracker boot times, measure from the ` CreateVM ` ioctl to the first successful HTTP response from the guest, not to kernel boot. The kernel boots in ~125ms but your application's init adds 20-500ms depending on the runtime. The number that matters is time-to-first-response.\n2. **Tip:** Use Firecracker's ` jailer ` binary in production, not bare ` firecracker `. The jailer sets up cgroups, chroot, seccomp filters, and drops privileges before starting the VMM. Running without it is like running Docker without namespaces, technically works, but the outer security layer is removed.\n3. **Tip:** For Unikraft, start with the ` kraft ` CLI and the pre-built app catalog at ` unikraft.org/apps ` before trying to build custom images. Building a Unikraft unikernel from scratch involves configuring a KConfig menu with 400+ options, and getting it wrong means a kernel panic with no debugger. Learn the tooling first.\n\n- unikernels\n- firecracker\n- microvm\n- serverless\n- unikraft\n- kvm\n- virtualization\n- cloud-infrastructure\n\n[Back to Blog](<https://lucioduran.com/blog>)"}],"links":[{"text":"Español","url":"https://lucioduran.com/es/blog/unikernels-firecracker-microvm-serverless"},{"text":"Firecracker","url":"https://firecracker-microvm.github.io/"},{"text":"AWS","url":"https://aws.com/"},{"text":"Unikraft","url":"https://unikraft.org/"},{"text":"Back to Blog","url":"https://lucioduran.com/blog"}],"structuredData":[{"@context":"https://schema.org","@graph":[{"@type":"BlogPosting","@id":"https://lucioduran.com/blog/unikernels-firecracker-microvm-serverless#article","mainEntityOfPage":{"@type":"WebPage","@id":"https://lucioduran.com/blog/unikernels-firecracker-microvm-serverless"},"headline":"Unikernels vs MicroVMs: Firecracker Internals, Unikraft, and the Real Cost of Serverless Isolation","description":"A comprehensive comparison of Firecracker MicroVMs and Unikraft unikernels: boot time benchmarks, memory footprint analysis, KVM internals, and what actually provides isolation for serverless functions in production.","datePublished":"2025-12-09","author":{"@type":"Person","@id":"https://lucioduran.com/#person","name":"Lucio Durán","url":"https://lucioduran.com"},"publisher":{"@type":"Person","@id":"https://lucioduran.com/#person","name":"Lucio Durán","url":"https://lucioduran.com"},"image":"https://lucioduran.com/favicons/og-default.png","url":"https://lucioduran.com/blog/unikernels-firecracker-microvm-serverless","inLanguage":"en","isPartOf":{"@id":"https://lucioduran.com/blog#blog"},"articleSection":"Infrastructure","keywords":"unikernels, firecracker, microvm, serverless, unikraft, kvm, virtualization, cloud-infrastructure","wordCount":1565,"timeRequired":"PT8M","workTranslation":[{"@type":"BlogPosting","inLanguage":"es","url":"https://lucioduran.com/es/blog/unikernels-firecracker-microvm-serverless"}]},{"@type":"BreadcrumbList","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https://lucioduran.com/"},{"@type":"ListItem","position":2,"name":"Blog","item":"https://lucioduran.com/blog"},{"@type":"ListItem","position":3,"name":"Unikernels vs MicroVMs: Firecracker Internals, Unikraft, and the Real Cost of Serverless Isolation","item":"https://lucioduran.com/blog/unikernels-firecracker-microvm-serverless"}]},{"@type":"FAQPage","mainEntity":[{"@type":"Question","name":"Why doesn't AWS Lambda just use containers for isolation?","acceptedAnswer":{"@type":"Answer","text":"Containers share the host kernel, and kernel vulnerabilities allow escapes. Lambda runs on Firecracker MicroVMs where each function invocation gets a dedicated Linux kernel instance with minimal attack surface. A container escape gives you host access; a VM escape requires a KVM vulnerability, which is a much harder target."}},{"@type":"Question","name":"Are unikernels production-ready?","acceptedAnswer":{"@type":"Answer","text":"It depends on the unikernel framework. Unikraft has reached production readiness for specific workloads, static HTTP servers, WASM runtimes, specific language runtimes. MirageOS is used in production at some organizations. But general-purpose unikernels that can run arbitrary Linux binaries don't exist. You're trading compatibility for performance and security."}},{"@type":"Question","name":"Can Firecracker run containers?","acceptedAnswer":{"@type":"Answer","text":"Not directly. Firecracker is a VMM (Virtual Machine Monitor) that boots a Linux kernel and a root filesystem. However, tools like Kata Containers and AWS's containerd-snapshotter use Firecracker as the isolation backend for OCI container images. The container image becomes the root filesystem for the MicroVM, giving you container ergonomics with VM isolation."}}]}]}],"contentHash":"df8c78795820c40f071df1d2c7eb9017e5c0e9fa89347ae6bbd462f7160aac1e","provenance":{"method":"Deterministic serialization of the server-rendered main element","source":"https://lucioduran.com/blog/unikernels-firecracker-microvm-serverless","hashAlgorithm":"sha256","hashScope":"JSON document fields before contentHash and provenance","sourceHTMLBytes":67268,"contentBytes":15132}}
