Skip to content

Firecracker Templates

Firecracker Templates are a Firecracker-only feature. Without a template, every Firecracker sandbox runs the full OCI-to-rootfs pipeline on each create - expect 10–60 seconds per boot. A template pre-builds the ext4 rootfs once and captures a paused VM memory state; subsequent sandbox creates clone that snapshot instead of cold-booting the kernel, bringing boot time under 100ms.

Firecracker templates let one OCI image power many sandboxes with sub-100ms cold starts. Register an image once; the daemon builds an ext4 rootfs and captures a paused-VM snapshot. Subsequent firecracker sandbox creates against that template clone the snapshot instead of cold-booting the kernel.

Templates are an opt-in alternative to the Docker runtime - your existing sandboxes are unaffected. Use the template lifecycle on this page when you want predictable, fast cold starts (CI runners, REPLs, agent workloads) and accept the up-front build cost.

  • Fast cold starts. Once a template is ready, new sandboxes boot from a memory snapshot, not from kernel init.
  • Predictable image content. A template is built once and reused; you control when it gets rebuilt.
  • Cluster replication. Templates with a registry destination ship their artifacts to all peers automatically (see Cluster Setup).

If you only need one-off sandboxes from arbitrary images, stick with the default Docker runtime - templates are not required.

Pass an explicit id so retried CI steps are idempotent - the daemon rejects a duplicate id with 409 Conflict rather than creating a second row. The build runs asynchronously; the call returns immediately with status: "pending".

const tpl = await microvm.createTemplate({
id: "py311",
image: "docker://python:3.11",
minSizeMiB: 1024,
});
console.log(tpl.id, tpl.status); // "py311" "pending"

The image field is passed straight to the daemon's OCI builder, so any skopeo-style reference works - docker://python:3.11, oci-archive:/path/to/image.tar, docker-daemon:my-tag:latest, etc.

The template moves through pending → building_rootfs → snapshotting → ready (or ready_no_snapshot if the snapshot phase failed but the rootfs is usable for cold boot). Poll getTemplate until it leaves the in-flight states. A simple polling loop is enough - the API has no webhook surface today.

async function awaitReady(id: string) {
for (;;) {
const tpl = await microvm.getTemplate(id);
if (tpl.status === "ready" || tpl.status === "ready_no_snapshot") return tpl;
if (tpl.status === "failed") throw new Error(`build failed: ${tpl.lastError}`);
await new Promise((r) => setTimeout(r, 2000));
}
}

ready means the daemon captured a snapshot - sandboxes against this template will fast-boot. ready_no_snapshot means the rootfs was built but the snapshot phase failed (check snapshotError); sandboxes still work, they just cold-boot the kernel.

for (const tpl of await microvm.listTemplates()) {
console.log(tpl.id, tpl.status, tpl.hasSnapshot ? "fast-boot" : "cold-boot");
}

rebuildTemplate re-runs the snapshot phase against an existing rootfs. Use it when:

  • The snapshot artifact got corrupted and you want to force the rebuild ahead of the next sandbox create.
  • Operator intervention - e.g. kernel or toolbox-agent upgrades - and you don't want to wait for the daemon's optional rotation reconciler.

The call is idempotent under concurrent retry: N parallel callers against the same ready template collapse to one rebuild kick (the daemon's CAS gates the transition). The 202 response carries the template in its post-transition state (typically unhealthy); poll getTemplate to observe the transition back to ready.

const tpl = await microvm.rebuildTemplate("py311");
console.log(tpl.status); // "unhealthy"
await awaitReady("py311");

The endpoint refuses templates that are not in a re-runnable state:

Current statusWhy rebuild is refused
pending, building_rootfs, snapshottingInitial build is in flight; the goroutine owns the row's state
ready_no_snapshotNo snapshot to re-derive from; needs a full from-scratch rebuild path (not yet supported - delete+recreate the row)
failedTerminal state from a failed initial build; delete and recreate

When the row is already unhealthy, the call returns the row as-is - a rebuild is in flight or will be re-kicked on the next daemon restart.

Deleting a template removes the rootfs and snapshot artifacts. The daemon rejects the delete with 409 Conflict if any active sandbox still references the template - destroy those first.

await microvm.deleteTemplate("py311");

When the daemon runs in cluster mode with AOCR (Aerol OCI Registry) configured, the build node ships the template's rootfs.ext4 + paused-VM snapshot to AOCR as a single OCI artifact under cluster/<id>/templates/<tid>:latest, and peer nodes lazily pull + extract it the first time a sandbox lands on them. Placement is template-aware: the scheduler prefers nodes that already hold a copy of the template's artifacts. See Cluster Setup for the deployment knobs.

This reuses the same push pipeline as Docker Snapshots - one switch turns on both, and both push to the same AOCR host with the same cluster credential:

Env varPurpose
SB_SNAPSHOT_PUSH_ENABLEDGates the background push for both snapshots and templates. When false (default), templates stay local to the build node.
SB_ENABLE_FIRECRACKERTemplates only exist on Firecracker nodes, so template push additionally requires this.
SB_MIRROR_PUSH_HOSTAOCR push host (falls back to SB_IMAGE_DISTRIBUTION_AOCR_HOST).
SB_AUTO_IMPORT_CLUSTER_IDCluster label that namespaces the artifact (cluster/<id>/templates/...) and scopes the credential.
SB_AUTO_IMPORT_CLUSTER_PAT_PATHFile holding the cluster PAT presented as the registry credential for push and pull. Re-read per call, so rotation is just a file write.

The pull side is authenticated with the same cluster PAT and does not require SB_SNAPSHOT_PUSH_ENABLED - a consume-only node fetches template artifacts from AOCR as long as the cluster ID and PAT file are set. Each template row carries a pushState you can read back to watch the lifecycle, mirroring snapshots. Unlike snapshots, a template has no "register an existing image" shortcut: it always carries the locally-built rootfs + snapshot, so its only distribution states are pushState + registryRef.

Set SB_FIRECRACKER_TEMPLATE_ROTATION_INTERVAL and SB_FIRECRACKER_TEMPLATE_MAX_AGE to let the daemon re-kick the snapshot phase for templates older than MAX_AGE on each interval tick. Both knobs must be non-zero - interval-only setups log a warning and no-op. This is the operator-side equivalent of looping over rebuildTemplate for every ready row; if you want fine-grained control, drive it from your application code instead.