Skip to content

feat(firecracker): launch with control socket instead of config file - #809

Open
Anamika1608 wants to merge 17 commits into
urunc-dev:mainfrom
Anamika1608:firecracker-api-sock
Open

feat(firecracker): launch with control socket instead of config file#809
Anamika1608 wants to merge 17 commits into
urunc-dev:mainfrom
Anamika1608:firecracker-api-sock

Conversation

@Anamika1608

@Anamika1608 Anamika1608 commented Jul 8, 2026

Copy link
Copy Markdown
Contributor

Description

Expose Firecracker's control socket for both boot modes, so the runtime can talk to the VMM after the guest starts (for graceful shutdown, and later snapshots), instead of everything being driven through a static config file.

  • config-file-based (boot_mode = "config-file"): Firecracker launches with --api-sock alongside --config-file, so it still boots itself from the config file and the socket stays open and reachable afterwards.
  • api-based (boot_mode = "api", default): the whole boot is driven over the socket. urunc spawns Firecracker as a supervised child and configures it in stages (machine-config immediately, the network interface once the tap device exists, drives and boot-source after unikernel.Init) while doing its own setup, then sends InstanceStart. The runtime stays alive and supervises the child.
  • The control socket path is configurable via a socket_path field; urunc creates the directory of a custom path so it can be placed outside /tmp.
  • The API client polls the socket every 1ms and removes a stale socket before spawning.

Related issues

How was this tested?

  • go build ./... and go test ./internal/... ./pkg/... pass; make lint reports no findings in the changed files; cspell and commitlint pass (Ubuntu 24.04 aarch64 VM, KVM, Firecracker v1.7.0).
  • Live, through real containerd + nerdctl: the chttp-firecracker image boots and the guest serves HTTP 200 in both boot modes, with the default socket path and a configured socket_path; an invalid socket_path (a file already on the path) fails cleanly.
  • Block-based rootfs via devmapper (ubuntu-firecracker-linux-raw): the guest boots off the block device and mounts its root in both boot modes.
  • Known limitation, tracked separately: in api mode a custom socket_path is created against the host view (the default /tmp is unaffected); a chroot-at-spawn fix is being discussed.

LLM usage

Checklist

  • I have read the contribution guide.
  • The linter passes locally (make lint).
  • The e2e tests of at least one tool pass locally (make test_ctr, make test_nerdctl, make test_docker, make test_crictl).
  • If LLMs were used: I have read the llm policy.

@netlify

netlify Bot commented Jul 8, 2026

Copy link
Copy Markdown

Deploy Preview for urunc ready!

Name Link
🔨 Latest commit 03896d2
🔍 Latest deploy log https://app.netlify.com/projects/urunc/deploys/6a683652d698010008b6cea8
😎 Deploy Preview https://deploy-preview-809--urunc.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify project configuration.

Firecracker currently boots via --no-api --config-file, which disables
its control socket entirely. Switch to --api-sock so the socket stays
open and reachable after the guest starts, instead of driving
everything through a static config file.

Signed-off-by: Anamika AggarwaL <anamikaagg18@gmail.com>
@Anamika1608
Anamika1608 force-pushed the firecracker-api-sock branch from 691f601 to 811a606 Compare July 8, 2026 10:09
Add an optional socket_path field under a monitor's configuration
section so users can override where the control socket is created.
Falls back to today's default path when unset. The field is added to
the shared MonitorConfig type, so it is available to any monitor, but
only Firecracker's BuildExecCmd reads it in this change, since it is
the only monitor with a control socket right now.

Signed-off-by: Anamika AggarwaL <anamikaagg18@gmail.com>
Add the new socket_path field to the Monitor Options table and the
Firecracker example, matching the existing style for path/data_path.

Signed-off-by: Anamika AggarwaL <anamikaagg18@gmail.com>
…ased boot

Add an optional boot_mode field under a monitor's configuration. "api"
(default) keeps today's behavior: drive the monitor's boot over its
control socket. "config-file" lets Firecracker boot itself from the
JSON config file, using the socket only for communication after the
guest is running. Currently only used by Firecracker.

Signed-off-by: Anamika AggarwaL <anamikaagg18@gmail.com>
Stop syscall.Exec-ing into Firecracker for boot_mode=api. Instead, spawn
it as a child, configure it over its control socket using the existing
config-building logic (now shared with the config-file path), start the
guest, then supervise the child: forward SIGTERM/SIGINT and exit with
its status once it exits. The child inherits whatever confinement
changeRoot already set up on this process (pivot_root or chroot), so no
separate confinement step is needed here.

Add HasNetwork() as a cheap, early check for whether a container has a
network interface, separate from the full (expensive) NetworkSetup.
Rootfs prep only needs this cheap fact (see prepareMonRootfs's needsTAP
argument), not the finished network, so for boot_mode=api, rootfs prep
now runs concurrently with the real network setup, joining just before
unikernel.Init (every supported unikernel type needs the resolved
network result to build the guest's boot command line).

Also fixes a real bug this surfaced: createTapDevice's file descriptor
was opened O_CLOEXEC and relied on syscall.Exec to close it as a side
effect of replacing the process. Since urunc no longer execs away for
boot_mode=api, that fd was staying open indefinitely, blocking
Firecracker's own attempt to attach to the same single-queue tap
device ("Resource busy"). The device is created with TUNSETPERSIST, so
it's safe to close the fd explicitly once setup is done.

Signed-off-by: Anamika AggarwaL <anamikaagg18@gmail.com>
Spawn Firecracker at the start of Exec and send each piece of its
configuration as soon as urunc produces it: machine-config immediately,
the network interface once the tap device exists, drives and boot
source right after unikernel.Init, and InstanceStart only after the
start-success handshake. The API client now holds one persistent
connection, established before changeRoot, since the socket path is
not resolvable after the root changes.

Signed-off-by: Anamika Aggarwal <anamikaagg18@gmail.com>
Firecracker binds its API socket within ~1ms of starting, but connect()
polled every 10ms, so it usually slept several ms past the moment the
socket was ready before noticing. Poll every 1ms: this recovers almost
all of that wasted wait (~6ms less socket-wait measured in isolation),
while a 1ms sleep stays well clear of a CPU-burning busy-loop. The
end-to-end boot time is unchanged (guest boot dominates it), so this is
a small efficiency fix, not a boot-time speedup.

Signed-off-by: Anamika Aggarwal <anamikaagg18@gmail.com>
Firecracker creates its API socket with bind(), which fails if a file
already exists at that path. A crash or a reused container id can leave
a stale socket behind, blocking the next start. Remove any leftover
socket before spawning, ignoring the not-found case.

Signed-off-by: Anamika Aggarwal <anamikaagg18@gmail.com>
Signed-off-by: Anamika Aggarwal <anamikaagg18@gmail.com>
Signed-off-by: Anamika Aggarwal <anamikaagg18@gmail.com>
Signed-off-by: Anamika Aggarwal <anamikaagg18@gmail.com>
Signed-off-by: Anamika Aggarwal <anamikaagg18@gmail.com>
Spawn the api-mode Firecracker child after changeRoot, so it inherits
the pivoted root: the control socket and every path it opens live inside
the monitor rootfs, matching the confinement of the config-file mode.
A custom socket_path is now created inside the monitor rootfs in both
boot modes, so the directory-creation feature applies uniformly and no
path can land on the host filesystem.

Since the child now shares urunc's mount view, the monitor-rootfs
prefixing in ConfigureGuest is removed and the guest paths are sent
exactly as the exec path uses them. The SetupNet/prepareMonRootfs
overlap is unaffected; only the VMM configuration calls move after the
pivot, and InstanceStart still waits for the start handshake.

Signed-off-by: Anamika Aggarwal <anamikaagg18@gmail.com>
Add a read-header timeout to the fake API server (gosec G112) and
rename the placeholder container ID to a dictionary word (cspell).

Signed-off-by: Anamika Aggarwal <anamikaagg18@gmail.com>
Signed-off-by: Anamika Aggarwal <anamikaagg18@gmail.com>
Signed-off-by: Anamika Aggarwal <anamikaagg18@gmail.com>
In the api boot mode the monitor is a supervised child, and an unset
Stdin on exec.Cmd connects it to /dev/null, so the guest's serial
console could produce output but never receive input. The exec-based
config-file mode does not have this problem, since the monitor inherits
the runtime's stdin when it replaces the process.

Wire the caller's stdin through to the child, restoring input for
interactive guests (e.g. a container run with -i whose command line is
a shell) and matching the behavior of the config-file mode.

Signed-off-by: Anamika Aggarwal <anamikaagg18@gmail.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant