PID 1 init tailored for Firecracker micro-VMs.
Experimental. This init code is
modeled afterarchitecturally based on the Fly.io init, since it's a good reference for Firecracker based init programs. But we experiment a bit more with MMDS and a few other things.
pigeon-init runs as PID 1 inside a Firecracker micro-VM. It handles the full guest lifecycle:
- Mount devtmpfs + redirect console to
/dev/ttyS0 - Load config — MMDS (
169.254.169.254) primary,/pigeon/run.jsonfallback - Mount rootfs + switch_root — mounts root device (default
/dev/vda), pivots into it - Mount essential filesystems —
/proc,/sys,/dev/pts,/dev/shm,/dev/mqueue,/dev/hugepages,/run,/proc/sys/fs/binfmt_misc - Mount cgroups — v1 + v2 hybrid (10 v1 controllers + unified cgroupv2)
- Set rlimits — NOFILE to 10240
- Resolve user/group — from image config or override (
/etc/passwd+/etc/group) - Build env — merge image env + extra env, set PATH
- Start vsock API — HTTP on vsock port 10000 (comes up early so host can probe readiness)
- Mount extra volumes — additional block device mounts with chown
- Set hostname, /etc/hosts, /etc/resolv.conf
- Configure networking — lo up, eth0 MTU + up, disable checksums, add addresses (IFA_F_NODAD), add routes
- Spawn workload — fork/exec with credentials, setsid, merged stdout/stderr pipe
- Main loop — SIGCHLD-driven reaping, OOM detection, signal forwarding to process group
- Shutdown — unmount (retry + lazy fallback), sync, reboot
make build # Static init binary → build/init
make initrd # Build initrd cpio (depends on build)
make rootfs # Docker image → ext4 rootfs
make test # Run unit tests
make clean # Remove build artifactsRequires Go 1.23+. initrd needs cpio + bash, rootfs needs Docker.
Config is delivered via Firecracker MMDS (V2 token + V1 fallback). If MMDS is unreachable, falls back to /pigeon/run.json baked into the initrd.
The JSON format is the contract between the host driver and guest init. PascalCase field names follow the Fly.io convention.
{
"ImageConfig": {
"Entrypoint": ["/bin/myapp"],
"Cmd": ["--port", "8080"],
"Env": ["PATH=/usr/local/bin:/usr/bin:/bin"],
"WorkingDir": "/app",
"User": "nobody"
},
"ExecOverride": null,
"CmdOverride": null,
"UserOverride": null,
"ExtraEnv": {
"DATABASE_URL": "postgres://...",
"LOG_LEVEL": "info"
},
"MTU": 1500,
"IPConfigs": [
{
"Gateway": "169.254.0.1",
"IP": "10.0.0.2",
"Mask": 24
}
],
"Hostname": "my-app-abcdef",
"Mounts": [
{
"DevicePath": "/dev/vdb",
"MountPath": "/data"
}
],
"RootDevice": null,
"EtcResolv": {
"Nameservers": ["8.8.8.8"]
},
"EtcHosts": [
{"Host": "my-app.internal", "IP": "10.0.0.2", "Desc": "app alias"}
]
}| Field | Type | Default | Description |
|---|---|---|---|
ImageConfig |
object | — | OCI image metadata (entrypoint, cmd, env, user, workdir) |
ExecOverride |
string[] | — | Replaces the entire argv (highest priority) |
CmdOverride |
string | — | Replaces the Cmd portion of argv |
UserOverride |
string | — | Overrides the image user ("user" or "user:group") |
ExtraEnv |
map | — | Merged on top of ImageConfig.Env |
MTU |
int | 1500 | MTU for eth0 |
IPConfigs |
array | — | Network addresses and routes for eth0 (omit to skip networking) |
Hostname |
string | — | Guest hostname (omit to skip) |
Mounts |
array | — | Extra block device mounts |
RootDevice |
string | /dev/vda |
Root filesystem device path |
EtcResolv |
object | — | /etc/resolv.conf nameservers (omit to skip) |
EtcHosts |
array | — | Entries appended to /etc/hosts (omit to skip) |
Priority order:
ExecOverride— replaces everythingImageConfig.Entrypoint+CmdOverride— entrypoint with overridden cmdImageConfig.Entrypoint+ImageConfig.Cmd— default OCI behavior
HTTP/1.1 on vsock port 10000 (any CID).
| Method | Path | Description |
|---|---|---|
GET |
/v1/status |
Health check ({"ok": true}) |
GET |
/v1/exit_code |
Blocks until workload exits ({"code": N, "oom_killed": bool}) |
POST |
/v1/signals |
Send signal to workload ({"signal": 15}) |
POST |
/v1/exec |
One-shot command ({"cmd": ["ls", "-la"]}) |
GET |
/v1/ws/exec |
WebSocket interactive exec (optional PTY) |
The vsock API becoming reachable is the implicit readiness signal.