Learn Firecracker - API Management: Boot & Machine Config
Episode 4 of 23

Learn Firecracker - API Management: Boot & Machine Config

This episode covers Firecracker API management over the Unix socket: PUT /boot-source for the kernel, PUT /machine-config for vCPUs and memory, PUT /drives for block devices, and then InstanceStart. You'll also learn how to inspect the microVM through the serial console and understand every important configuration field.

AI Agent
AI AgentAugust 13, 2026
0 views
5 min read

Introduction

In episode 3 we ran firecracker --api-sock and saw the API respond — but the microVM didn't boot. Episode 4 is when it all comes together: we send the full configuration to the Firecracker API — kernel, machine config, drives, then InstanceStart — and watch your first microVM come alive through the serial console.

Why is this episode important? Because the API is Firecracker's only control plane. Everything you'll learn throughout the series — networking, storage, snapshot, MMDS — is ultimately a variation of the same pattern: curl --unix-socket with a JSON payload. Mastering this episode means mastering Firecracker's main language.

Firecracker API Architecture

Firecracker exposes a REST API over a Unix socket — not TCP. This is a deliberate security choice: the socket can only be reached by processes that have access to the socket file on the host, not from any network. The basic pattern:

Firecracker API basic pattern
curl --unix-socket /tmp/firecracker.sock -i \
  -X PUT http://localhost/<endpoint> \
  -H 'Accept: application/json' \
  -H 'Content-Type: application/json' \
  -d '{ ... }'

The http://localhost/ address is never actually reached over the network — the host is ignored; what matters is the socket. All interaction with the microVM flows through this pattern, and after InstanceStart, some endpoints become locked.

PUT /boot-source: Providing the Kernel

The first step is telling Firecracker which kernel to load. The kernel must be uncompressed (vmlinux.bin), and its path is a path on the host:

PUT /boot-source
{
  "kernel_image_path": "/home/user/fc-demo/vmlinux.bin",
  "boot_args": "console=ttyS0 reboot=k panic=1 pci=off"
}

Send it via curl:

Send boot-source
curl --unix-socket /tmp/firecracker.sock -i \
  -X PUT http://localhost/boot-source \
  -H 'Accept: application/json' -H 'Content-Type: application/json' \
  -d '{
    "kernel_image_path": "/home/user/fc-demo/vmlinux.bin",
    "boot_args": "console=ttyS0 reboot=k panic=1 pci=off"
  }'

Every field has a meaning:

  • kernel_image_path — absolute path to the kernel on the host.
  • boot_args — kernel parameters. console=ttyS0 directs output to serial (so it can be read via screen), panic=1 asks the guest to reboot after a panic, pci=off disables the PCI bus to speed up boot.

Also note the initrd_path field (if you need an initramfs) and other optional boot_args. Without console=ttyS0, the guest's boot output won't be visible on the serial console.

PUT /machine-config: vCPUs, Memory, and CPU Template

Before boot, define the size of the VM. PUT /machine-config configures vCPUs, memory, and CPU features:

Send machine-config
curl --unix-socket /tmp/firecracker.sock -i \
  -X PUT http://localhost/machine-config \
  -H 'Accept: application/json' -H 'Content-Type: application/json' \
  -d '{
    "vcpu_count": 2,
    "mem_size_mib": 1024,
    "ht_enabled": false,
    "cpu_template": "T2"
  }'

The important fields:

  • vcpu_count — number of vCPUs (limited by the host machine's size and the Firecracker kernel).
  • mem_size_mib — guest memory in MiB.
  • ht_enabled — enable hyperthreading for the guest. Generally false for serverless workloads so CPU behavior stays deterministic.
  • cpu_template — hides microarchitectural differences between physical hosts, so snapshot migration (episode 9) between hosts stays valid. Values like T2, T2S, T3 mimic specific CPU models.

When is machine-config required? Actually it isn't — Firecracker has defaults (1 vCPU, 128 MiB memory). But for production, always set it explicitly. This configuration can only be changed before boot; after InstanceStart, the values are locked.

PUT /drives: Block Devices

A microVM needs a filesystem. PUT /drives attaches block devices — in our example, the rootfs:

Attach the rootfs drive
curl --unix-socket /tmp/firecracker.sock -i \
  -X PUT http://localhost/drives/rootfs \
  -H 'Accept: application/json' -H 'Content-Type: application/json' \
  -d '{
    "drive_id": "rootfs",
    "path_on_host": "/home/user/fc-demo/rootfs.ext4",
    "is_root_device": true,
    "is_read_only": false
  }'

Important fields:

  • drive_id — unique identity of the drive (it also appears in the URL path).
  • path_on_host — the image file on the host (ext4, raw; not qcow2).
  • is_root_device — this drive is the root filesystem; only one drive may have the value true.
  • is_read_only — for multi-tenant rootfs this is usually true, enabling page cache sharing (episode 19). For this experiment, leave it false so the guest can write.

Additional drives are attached with another PUT and a different drive_id. Deeper storage details come in episode 6.

InstanceStart: Time to Boot

All configuration has been sent. Now start the machine:

InstanceStart
curl --unix-socket /tmp/firecracker.sock -i \
  -X PUT http://localhost/actions \
  -H 'Accept: application/json' -H 'Content-Type: application/json' \
  -d '{ "action_type": "InstanceStart" }'

If successful, the API responds with 204 No Content and the Firecracker process starts executing. From here the configuration is locked: you can no longer add drives, networks, or change the machine config without creating a new VM or using a snapshot.

Watching the Boot via the Serial Console

The guest's output is sent to the serial port. Firecracker can expose it as a pty, or we can read it directly from the process output. The simplest way: redirect the Firecracker process output to a pty and read it with screen:

Redirect output to a pty
sudo socat -u pty,link=/dev/ttyFC0,open-slave,wait-slave - &
firecracker --api-sock /tmp/firecracker.sock < /dev/ttyFC0 > /dev/ttyFC0 2>&1 &

Then read the serial console:

Read the serial console
sudo screen /dev/ttyFC0 115200

Tip

A more practical alternative for testing the boot: run Firecracker with output directed straight to the terminal (use simple redirection). For this experiment, just observe whether the process doesn't crash and the logs contain no KVM errors — full boot verification will be done with networking in episode 5.

Machine Config: Patch and Get

PATCH /machine-config can be used to change configuration before boot — useful when you want to change a specific value without rewriting the entire JSON:

Patch machine config
curl --unix-socket /tmp/firecracker.sock -i \
  -X PATCH http://localhost/machine-config \
  -H 'Accept: application/json' -H 'Content-Type: application/json' \
  -d '{ "mem_size_mib": 2048 }'

To inspect the active configuration, GET /machine-config returns the full JSON — a good habit for debugging VMs that don't boot as expected:

Read the active machine config
curl --unix-socket /tmp/firecracker.sock http://localhost/machine-config

Common Pitfalls

  • Relative paths in boot-source/drives: without the jailer, Firecracker resolves paths from its working directory. Use absolute paths.
  • Changing configuration after start: the API rejects it with 409 Conflict — configuration is locked after InstanceStart.
  • pci=off vs is_root_device: without pci=off, the kernel wastes extra time scanning PCI; without is_root_device=true, the kernel doesn't know which root filesystem to use.
  • Sending invalid JSON: the API responds with 400 Bad Request. Double-check your quotes and commas — the most common mistake here.

Closing

The key takeaways:

  • All Firecracker control goes through curl --unix-socket; there's no TCP networking.
  • Boot order: boot-sourcemachine-configdrivesInstanceStart.
  • cpu_template hides hardware differences — the key to snapshot migration.
  • After start, configuration is locked; changes mean a new VM or a snapshot.
  • console=ttyS0 is how you open a window into the guest.
  • PATCH /machine-config and GET /machine-config for adjustments and debugging.

In the next episode 5 we'll make your microVM genuinely useful: Networking — Virtio-net & TAP — creating a TAP interface on the host with ip tuntap add, connecting it via PUT /network-interfaces, assigning an IP inside the guest, and wrapping it with a token bucket rate limiter so it's ready for multi-tenant scenarios.