node-firecracker
    Preparing search index...

    node-firecracker

    Firecracker logo

    @sourceregistry/node-firecracker

    Typed Node.js client for the Firecracker microVM REST API

    npm version npm downloads JSR license node CI issues

    Docs · npm · JSR · Issues


    A thin, fully-typed client for the Firecracker VMM's HTTP API. Firecracker exposes its API over a unix domain socket (one socket per running microVM process) — this library speaks that API. It does not spawn or manage the firecracker binary itself; point it at the socket of an already-running Firecracker process and use it to configure and control that VM.

    npm install @sourceregistry/node-firecracker
    

    Node.js 18+ is required.

    import { FirecrackerClient } from "@sourceregistry/node-firecracker";

    const client = new FirecrackerClient({ socketPath: "/run/firecracker.socket" });

    await client.bootSource.set({
    kernel_image_path: "/var/lib/firecracker/vmlinux",
    boot_args: "console=ttyS0 reboot=k panic=1 pci=off",
    });

    await client.drive("rootfs").set({
    drive_id: "rootfs",
    is_root_device: true,
    path_on_host: "/var/lib/firecracker/rootfs.ext4",
    });

    await client.machineConfig.set({ vcpu_count: 2, mem_size_mib: 512 });

    await client.action("InstanceStart");

    const info = await client.getInstanceInfo();
    console.log(info.state); // "Running"

    FirecrackerClient groups operations by resource — each resource exposes get/set/update (mirroring Firecracker's GET/PUT/PATCH). All request/response bodies are typed against the Firecracker OpenAPI spec.

    Member Endpoint
    getInstanceInfo() GET /
    getVersion() GET /version
    createSyncAction(action) / action(type) PUT /actions
    balloon.get() GET /balloon
    balloon.set(config) PUT /balloon
    balloon.update(partial) PATCH /balloon
    balloon.stats.get() GET /balloon/statistics
    balloon.stats.update(partial) PATCH /balloon/statistics
    bootSource.set(config) PUT /boot-source
    drive(id).set(drive) PUT /drives/{drive_id}
    drive(id).update(partial) PATCH /drives/{drive_id}
    logger.set(config) PUT /logger
    machineConfig.get() GET /machine-config
    machineConfig.set(config) PUT /machine-config
    machineConfig.update(partial) PATCH /machine-config
    metrics.set(config) PUT /metrics
    mmds.get() GET /mmds
    mmds.set(data) PUT /mmds
    mmds.update(data) PATCH /mmds
    mmds.config.set(config) PUT /mmds/config
    networkInterface(id).set(iface) PUT /network-interfaces/{iface_id}
    networkInterface(id).update(partial) PATCH /network-interfaces/{iface_id}
    snapshot.create(params) PUT /snapshot/create
    snapshot.load(params) PUT /snapshot/load
    vm.update(state) PATCH /vm
    vmConfig.get() GET /vm/config
    vsock.set(config) PUT /vsock

    Non-2xx responses reject with FirecrackerApiError, which carries the HTTP statusCode and the fault_message Firecracker returns in its error body:

    import { FirecrackerApiError } from "@sourceregistry/node-firecracker";

    try {
    await client.drive("rootfs").set({ drive_id: "rootfs", is_root_device: true, path_on_host: "/missing.ext4" });
    } catch (err) {
    if (err instanceof FirecrackerApiError) {
    console.error(err.statusCode, err.faultMessage);
    }
    }
    new FirecrackerClient({
    socketPath: "/run/firecracker.socket",
    timeoutMs: 5000, // default: 5000
    });

    npm test            # run tests
    npm run test:ui # vitest UI
    npm run test:coverage
    npm run build
    npm run docs:build # generate TypeDoc

    src/integration.real.test.ts boots an actual Firecracker microVM through the client and is skipped by default — it needs a Linux host with /dev/kvm access plus a real firecracker binary, kernel image, and rootfs (none of which are checked into this repo).

    Open this repo in the provided devcontainer (.devcontainer/devcontainer.json — "Reopen in Container" in VSCode), which passes /dev/kvm through, downloads the firecracker binary/kernel/ rootfs on first create, and keeps a long-lived firecracker process bound to /run/firecracker.socket running on every container start — so a real VM is already reachable as soon as the container is up:

    curl --unix-socket /run/firecracker.socket http://localhost/
    

    FIRECRACKER_BIN, FIRECRACKER_KERNEL, FIRECRACKER_ROOTFS and FIRECRACKER_SOCKET are set in the container environment automatically, so the real integration test just runs:

    npx vitest run src/integration.real.test.ts
    

    The guest's serial console runs inside a tmux session and is fully interactive — attach to it from a terminal in the container to log in and poke around:

    docker exec -it <container> tmux attach -t firecracker   # Ctrl-b d to detach without killing the VM
    

    Outside the devcontainer, fetch the fixtures and run the test manually instead:

    ./scripts/fetch-firecracker-fixtures.sh   # downloads firecracker + vmlinux + rootfs.ext4, prints the command below

    FIRECRACKER_BIN=/path/to/firecracker \
    FIRECRACKER_KERNEL=/path/to/vmlinux \
    FIRECRACKER_ROOTFS=/path/to/rootfs.ext4 \
    npx vitest run src/integration.real.test.ts

    Without a devcontainer, this needs a native Linux environment (WSL2 works) — running on a filesystem shared with Windows (/mnt/c/...) breaks native node_modules binaries when you switch between npm install on Windows and on Linux. Either keep two separate npm installs (one per OS, reinstalled when you switch), or clone the repo into the Linux-native filesystem (e.g. ~/node-firecracker) and work from there instead.


    Apache-2.0 © Alexander Slaa