{"id":49414085,"url":"https://github.com/misaelzapata/node-vmm","last_synced_at":"2026-04-29T02:12:21.497Z","repository":{"id":354519692,"uuid":"1223880884","full_name":"misaelzapata/node-vmm","owner":"misaelzapata","description":"TypeScript/JavaScript SDK and CLI for Linux KVM microVMs from Node.js","archived":false,"fork":false,"pushed_at":"2026-04-28T22:26:09.000Z","size":6800,"stargazers_count":1,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-04-28T23:24:06.299Z","etag":null,"topics":["kvm","microvm","nodejs","oci","typescript","virtualization"],"latest_commit_sha":null,"homepage":"https://www.npmjs.com/package/@misaelzapata/node-vmm","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/misaelzapata.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-04-28T18:41:21.000Z","updated_at":"2026-04-28T22:26:13.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/misaelzapata/node-vmm","commit_stats":null,"previous_names":["misaelzapata/node-vmm"],"tags_count":null,"template":false,"template_full_name":null,"purl":"pkg:github/misaelzapata/node-vmm","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/misaelzapata%2Fnode-vmm","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/misaelzapata%2Fnode-vmm/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/misaelzapata%2Fnode-vmm/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/misaelzapata%2Fnode-vmm/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/misaelzapata","download_url":"https://codeload.github.com/misaelzapata/node-vmm/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/misaelzapata%2Fnode-vmm/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":32404341,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-28T19:38:08.556Z","status":"ssl_error","status_checked_at":"2026-04-28T19:37:55.688Z","response_time":56,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["kvm","microvm","nodejs","oci","typescript","virtualization"],"created_at":"2026-04-29T02:12:20.893Z","updated_at":"2026-04-29T02:12:21.490Z","avatar_url":"https://github.com/misaelzapata.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# node-vmm\n\n[![CI](https://github.com/misaelzapata/node-vmm/actions/workflows/ci.yml/badge.svg?branch=main)](https://github.com/misaelzapata/node-vmm/actions/workflows/ci.yml)\n[![npm version](https://img.shields.io/badge/npm-v0.1.3-cb3837.svg?style=flat)](https://www.npmjs.com/package/@misaelzapata/node-vmm)\n[![npm install](https://img.shields.io/badge/install-npm%20i%20%40misaelzapata%2Fnode--vmm-cb3837.svg?style=flat)](https://www.npmjs.com/package/@misaelzapata/node-vmm)\n[![Node.js](https://img.shields.io/badge/node-%3E%3D18.19-339933.svg?style=flat)](package.json)\n[![License: MIT](https://img.shields.io/badge/license-MIT-blue.svg?style=flat)](LICENSE)\n[![Types](https://img.shields.io/badge/types-TypeScript-blue.svg?style=flat)](docs/sdk.md)\n[![ESM only](https://img.shields.io/badge/module-ESM%20only-4b32c3.svg?style=flat)](docs/sdk.md)\n[![Coverage](https://img.shields.io/badge/coverage-100%25-brightgreen.svg?style=flat)](docs/testing.md)\n[![Runtime](https://img.shields.io/badge/runtime-Linux%2FKVM-2ea44f.svg?style=flat)](#requirements)\n[![Windows](https://img.shields.io/badge/Windows%20WHP-in%20progress-orange.svg?style=flat)](docs/windows.md)\n\n**Run real Node apps inside small Linux microVMs from TypeScript or JavaScript.**\n\n`node-vmm` is an ESM SDK and CLI that turns OCI images, Dockerfiles, or Git\nrepos into bootable ext4 rootfs images, then runs them through a compact native\nKVM backend. It is built for sandbox-like workloads where Node developers want\nprocess-shaped ergonomics with VM isolation: run code, boot app servers, publish\nports, pause/resume, and throw writable overlays away.\n\nThe npm release target is Linux/KVM today. Windows Hypervisor Platform support\nis in progress: Windows can build/import the JS package and the experimental WHP\nbackend is kept behind a manual native gate.\n\n## Why node-vmm?\n\n- **JS-first API:** import it from TypeScript/JavaScript or use the `node-vmm`\n  CLI.\n- **Real app inputs:** build from OCI images, Dockerfiles, local folders, or Git\n  repos.\n- **No Docker Engine dependency:** the builder pulls OCI layers and assembles\n  rootfs images directly.\n- **Docker-style ports:** publish guest TCP ports such as `3000:3000`.\n- **Fast warm lifecycle:** pause/resume already-running app VMs and reuse\n  prepared rootfs templates for repeated boots.\n- **Release-gated apps:** plain Node, Express, Fastify, Next.js, Vite React, and\n  Vite Vue are booted and checked over HTTP in the KVM gate.\n\n## Performance Snapshot\n\nFast path first, measured on the local Linux/KVM release host. There are two\ndifferent warm paths:\n\n- **Pause/resume:** the VM stays alive; `resumeToHttpMs` measures how long it\n  takes an already-started app server to answer after `resume()`.\n- **Prepared rootfs cache:** `run --image` skips OCI extraction and rootfs\n  materialization, but still boots Linux for each run.\n\nPause/resume timings from real app servers:\n\n| App | Pause/resume to HTTP | Cold boot to HTTP |\n| --- | ---: | ---: |\n| Vite React | 5 ms | 2.01 s |\n| Fastify | 8 ms | 1.16 s |\n| plain Node HTTP | 10 ms | 1.15 s |\n| Express | 16 ms | 1.16 s |\n| Vite Vue | 25 ms | 3.31 s |\n| Next.js hello-world | 50 ms | 2.60 s |\n\nPrepared rootfs cache timings for `run --image` in the `0.1.2` release\ncandidate:\n\n| Path | Image / command | Cache | Wall time |\n| --- | --- | --- | ---: |\n| Warm cached rootfs | `alpine:3.20`, `echo` | hit | 330-519 ms |\n| Warm cached rootfs + `--fast-exit` | `alpine:3.20`, `echo` | hit | 357-416 ms |\n| Warm cached rootfs | `node:22-alpine`, `node -e` | hit | 551-630 ms |\n| Warm cached rootfs + `--fast-exit` | `node:22-alpine`, `node -e` | hit | 776-796 ms |\n| Cold build into cache | `alpine:3.20`, `echo` | miss | 4.091 s |\n| Cold build into cache | `node:22-alpine`, `node -e` | miss | 9.498 s |\n\nCold runs include `mkfs.ext4`, OCI pull/extract, rootfs materialization, guest\nLinux boot, and command execution. Warm runs reuse the prepared ext4 rootfs from\n`--cache-dir/rootfs` and boot it with a temporary sparse overlay, so guest writes\ndo not mutate the cached base.\n\nBuild times depend on network, npm, and OCI cache state; the measured details\nare in [docs/performance.md](docs/performance.md).\n\n## Quick Start\n\n```bash\nnpm install @misaelzapata/node-vmm\n\nexport NODE_VMM_KERNEL=\"$(node-vmm kernel fetch)\"\nsudo -n node-vmm doctor\nsudo node-vmm run \\\n  --image alpine:3.20 \\\n  --cmd \"uname -a \u0026\u0026 echo hello\"\n```\n\nThe Linux runtime needs `/dev/kvm` and root privileges for rootfs mounting,\nnetwork setup, and most VM runs. Use passwordless `sudo -n` in CI/release gates;\ninteractive local runs can use regular `sudo`.\n\n```ts\nimport kvm from \"@misaelzapata/node-vmm\";\n\nconst result = await kvm.run({\n  image: \"alpine:3.20\",\n  cmd: \"echo hello from a VM\",\n  disk: 256,\n  net: \"none\",\n});\n\nconsole.log(result.guestOutput);\n```\n\nFor the Node sandbox use case, run JavaScript directly:\n\n```ts\nconst result = await kvm.runCode({\n  image: \"node:22-alpine\",\n  code: \"console.log(process.version, 40 + 2)\",\n  sandbox: true,\n  memory: 512,\n  net: \"none\",\n});\n```\n\nFor repeated sandbox work, prepare once and run hot:\n\n```ts\nconst template = await kvm.prepare({\n  image: \"alpine:3.20\",\n  cmd: \"true\",\n  disk: 256,\n  net: \"none\",\n});\n\nawait template.run({ cmd: \"echo hot path\" });\nawait template.close();\n```\n\nOr use the sandbox-shaped API:\n\n```ts\nconst sandbox = await kvm.createSandbox({\n  image: \"alpine:3.20\",\n  disk: 256,\n  net: \"none\",\n});\n\nconst result = await sandbox.process.exec(\"echo hello\");\nawait sandbox.delete();\n```\n\n## Docs At A Glance\n\n- [CLI](#cli)\n- [SDK API](docs/sdk.md)\n- [Dockerfile and framework tutorials](docs/tutorials.md)\n- [Snapshots and pause/resume](docs/snapshots.md)\n- [Testing and release gates](docs/testing.md)\n- [Performance measurements](docs/performance.md)\n- [Publishing checklist](docs/publishing.md)\n- [Windows WHP status](docs/windows.md)\n\n## Requirements\n\nLinux runtime:\n\n- Linux with `/dev/kvm`\n- KVM enabled in BIOS/UEFI and loaded by the host kernel\n- Node.js 18.19+\n- Linux x64 npm installs use the bundled native prebuild by default\n- `python3`, `make`, and `g++` are only needed when forcing or falling back to a\n  local `node-gyp` build\n- `mkfs.ext4`, `mount`, `umount`, `truncate`, `install`\n- `ip`, `iptables`, `sysctl` for `network: \"auto\"` / `--net auto`\n- `git` for `--repo` / SDK repo builds\n- root privileges through `sudo` for rootfs creation, mount/umount, and TAP/NAT\n  setup\n- `/dev/kvm` access for VM execution; many workflows are simplest as `sudo`\n- Kernel downloads are SHA-256 checked. For custom kernel URLs, set\n  `NODE_VMM_KERNEL_SHA256` or publish a `.sha256` sidecar next to the `.gz`.\n\n## What Can Run\n\n| Workflow | Works today | Needs KVM | Needs sudo |\n| --- | --- | --- | --- |\n| Import SDK / inspect `features()` | Yes | No | No |\n| Build from OCI image or Dockerfile | Yes | No | Yes, for ext4 mount/chroot |\n| Build from Git repo with `--repo` | Yes | No | Yes, for ext4 mount/chroot |\n| Run a prepared rootfs with `--net none` | Yes | Yes | No, if user can open `/dev/kvm` |\n| Run web apps with `--net auto -p 3000:3000` | Yes | Yes | Yes |\n| Next.js, Vite React/Vue, Express, Fastify app servers | Yes | Yes | Yes |\n| Multi-vCPU runtime with `--cpus \u003e 1` | Yes, Linux/KVM | Yes | Depends on network/rootfs setup |\n| Windows WHP runtime | In progress | Host WHP | No Linux sudo |\n\nWindows work in progress:\n\n- Windows 11 or Windows Server with Windows Hypervisor Platform enabled\n- Node.js 18.19+\n- Visual Studio Build Tools / Windows SDK for `node-gyp`\n- `npm run build` is prepared to compile the native WHP addon\n- `node-vmm doctor` is prepared to check WHP and dirty-page tracking\n\n## Running Without Sudo\n\nYes, VM execution can run without `sudo` when the host user can open `/dev/kvm`\nand the VM does not need root-only setup. The root-only parts are rootfs\ncreation/mounting and `--net auto`.\n\nOne simple workflow is:\n\n```bash\n# One-time host setup. Log out/in afterward if your session does not pick up the group.\nsudo usermod -aG kvm \"$USER\"\nnewgrp kvm\ntest -r /dev/kvm -a -w /dev/kvm\n\n# Build the disk once with root because this mounts an ext4 image.\nexport NODE_VMM_KERNEL=\"$(node-vmm kernel fetch)\"\nsudo node-vmm build --image node:22-alpine --output ./node22.ext4 --disk 512\nsudo chown \"$USER:$USER\" ./node22.ext4\n\n# Run from the prepared disk without sudo. Keep networking off.\nnode-vmm run \\\n  --rootfs ./node22.ext4 \\\n  --cmd \"node -e 'console.log(40 + 2)'\" \\\n  --sandbox \\\n  --net none\n```\n\nUse `--sandbox` for this path: the base rootfs is opened read-only and writes go\nto a user-owned sparse overlay that is deleted afterward. `--net auto` still\nneeds `sudo`; use `--net none` for rootless runs.\n\n## Cache And Reuse\n\n`run --image` keeps two caches under `--cache-dir`:\n\n- OCI blobs in `blobs/`\n- prepared ext4 rootfs images in `rootfs/`\n\nThe default cache directory is `/tmp/node-vmm/oci-cache`. On a cache hit,\n`node-vmm` boots the cached base rootfs with a temporary sparse overlay, so\ncommands like `apk add htop` are fast on repeat runs and do not mutate the base\nimage. Use `node-vmm build --output ./app.ext4` plus `run --rootfs ./app.ext4`\nwhen you want an explicit disk artifact you can move, inspect, or version\noutside the cache.\n\n## Kernel\n\n`node-vmm` can download a default guest kernel artifact for local development:\n\n```bash\nexport NODE_VMM_KERNEL=\"$(node-vmm kernel fetch)\"\nnode-vmm kernel find\n```\n\n`node-vmm kernel fetch` downloads and gunzips the default `vmlinux` into\n`~/.cache/node-vmm/kernels`. You can still pass `--kernel ./vmlinux` explicitly,\nand `NODE_VMM_KERNEL` is the primary environment override. Downloaded kernels\nmust match a built-in SHA-256, `NODE_VMM_KERNEL_SHA256`, or a `.sha256` sidecar.\n`NODE_VMM_KERNEL_MAX_GZIP_BYTES` and `NODE_VMM_KERNEL_MAX_BYTES` cap compressed\nand decompressed kernel size.\n\n## CLI\n\nThe npm package exposes the `node-vmm` CLI through `package.json#bin`. Install it\nglobally, run it with `npx`, or use the local project binary:\n\n```bash\nnpm install -g @misaelzapata/node-vmm\nnode-vmm --help\n\nnpx @misaelzapata/node-vmm kernel fetch\n\nnpm install @misaelzapata/node-vmm\nnpx @misaelzapata/node-vmm features\n```\n\nOn Linux x64 the package includes `prebuilds/linux-x64/node_vmm_native.node`, so\nnormal installs do not compile the KVM addon. Set\n`NODE_VMM_FORCE_NATIVE_BUILD=1` before `npm rebuild @misaelzapata/node-vmm` if\nyou want to rebuild it locally.\n\n```bash\nsudo node-vmm doctor\n\nsudo node-vmm build \\\n  --image alpine:3.20 \\\n  --output ./alpine.ext4 \\\n  --disk 256\n\nsudo node-vmm boot \\\n  --disk ./alpine.ext4 \\\n  --net none\n\nsudo node-vmm run \\\n  --image alpine:3.20 \\\n  --cmd /bin/sh \\\n  --interactive\n\nIn an interactive console, `exit` or `Ctrl-D` exits the guest shell normally.\n`Ctrl-C` is reserved as a host escape: it stops the VM, restores the terminal,\nand runs the usual network/overlay cleanup.\n\nsudo node-vmm run \\\n  --rootfs ./alpine.ext4 \\\n  --cmd \"echo hot path\" \\\n  --sandbox \\\n  --net none\n\nsudo node-vmm run \\\n  --dockerfile ./Dockerfile \\\n  --context . \\\n  --cmd \"node -e 'console.log(process.version)'\"\n\nsudo node-vmm build \\\n  --repo https://github.com/user/app.git \\\n  --ref main \\\n  --subdir apps/web \\\n  --output ./app.ext4 \\\n  --disk 4096\n```\n\n`--repo` clones into temporary storage, uses `--subdir` or `--context` as the\nDocker build context, builds with the repo Dockerfile, and removes the checkout\nafter the rootfs is written.\n\n### Publishing App Ports\n\nFor server apps, use Docker-style TCP publishing with `-p`/`--publish`.\nAutomatic networking uses a TAP device and NAT, so it requires `sudo`:\n\n```bash\nsudo node-vmm run \\\n  --image node:22-alpine \\\n  --cmd \"node -e \\\"require('node:http').createServer((_, r) =\u003e r.end('ok\\\\n')).listen(3000, '0.0.0.0')\\\"\" \\\n  --net auto \\\n  -p 3000:3000\n\ncurl http://127.0.0.1:3000\n```\n\nThe syntax matches Docker's common TCP form:\n\n- `-p 3000` publishes guest port `3000` on a random local port.\n- `-p 8080:3000` publishes `127.0.0.1:8080` to guest port `3000`.\n- `-p 127.0.0.1:8080:3000/tcp` binds an explicit host address.\n\nOnly TCP is proxied in the current Linux/KVM release.\n\n## SDK\n\nThe root export provides the stable high-level SDK:\n\n- default `kvm`\n- `kvm.run(options)`\n- `kvm.start(options)` / `kvm.startVm(options)`\n- `kvm.runCode(options)`\n- `kvm.boot(options)`\n- `kvm.build(options)`\n- `kvm.prepare(options)`\n- `kvm.createSandbox(options)`\n- `kvm.createSnapshot(options)`\n- `kvm.restoreSnapshot(options)`\n- `kvm.doctor()`\n- `kvm.features()`\n- short named exports: `run`, `boot`, `build`, `prepare`, `createSandbox`\n- live VM exports: `start`, `startVm`, `startImage`\n- `createNodeVmmClient(options)`\n- `buildRootfsImage(options)`\n- `bootRootfs(options)`\n- `runImage(options)`\n- `prepareSandbox(options)`\n\n## Next.js\n\nUse `node-vmm` only from server code. For example, an App Router route handler:\n\n```ts\n// app/api/sandbox/route.ts\nimport kvm from \"@misaelzapata/node-vmm\";\n\nexport const runtime = \"nodejs\";\n\nexport async function POST(request: Request) {\n  const { code } = await request.json();\n  const result = await kvm.runCode({\n    image: \"node:22-alpine\",\n    code,\n    language: \"javascript\",\n    sandbox: true,\n    memory: 512,\n    net: \"none\",\n    timeoutMs: 10_000,\n  });\n\n  return Response.json({\n    stdout: result.guestOutput,\n    status: result.guestStatus ?? 0,\n    runs: result.runs,\n  });\n}\n```\n\nRun Next with the kernel resolved once:\n\n```bash\nexport NODE_VMM_KERNEL=\"$(node-vmm kernel fetch)\"\nnpm run dev\n```\n\nDo not import `@misaelzapata/node-vmm` from Client Components or edge routes;\nthe native addon belongs in the Node.js server runtime.\n\n## Framework Apps\n\nFor app servers, build from the app's Dockerfile and publish the HTTP port:\n\n```bash\nsudo node-vmm build --dockerfile Dockerfile --context . --output ./app.ext4 --disk 4096\nsudo node-vmm run --rootfs ./app.ext4 --net auto -p 3000:3000 --sandbox --timeout-ms 0\ncurl http://127.0.0.1:3000\n```\n\nThe release gate boots real generated apps for plain Node HTTP, Express,\nFastify, Next.js, Vite React, and Vite Vue. See\n[Framework tutorials](docs/tutorials.md) for Dockerfile templates and server\ncode.\n\nSubpath exports are also available for advanced users:\n\n```ts\nimport { pullOciImage } from \"@misaelzapata/node-vmm/oci\";\nimport { buildRootfs } from \"@misaelzapata/node-vmm/rootfs\";\nimport { runKvmVm } from \"@misaelzapata/node-vmm/kvm\";\n```\n\n`node-vmm` is ESM-only. CommonJS projects can use dynamic import:\n\n```js\nconst { features } = await import(\"@misaelzapata/node-vmm\");\n```\n\nFor full API notes, Next.js usage, testing, and publishing, see:\n\n- [SDK API](docs/sdk.md)\n- [Feature matrix](docs/features.md)\n- [Framework tutorials](docs/tutorials.md)\n- [Launch demo](docs/demo.md)\n- [Testing and coverage](docs/testing.md)\n- [Performance](docs/performance.md)\n- [Snapshots and fast restore](docs/snapshots.md)\n- [Windows WHP backend](docs/windows.md)\n- [Publishing](docs/publishing.md)\n\n## Current v1 Support\n\n- x86_64 KVM on Linux, 1-64 active vCPUs\n- experimental WHP backend work for Windows\n- ELF `vmlinux` kernels\n- UART console with batch and interactive modes\n- virtio-mmio block root disk at `/dev/vda`\n- sparse copy-on-write `--sandbox` restore mode\n- core `snapshot create` / `snapshot restore` bundle commands\n- virtio-mmio network through TAP/NAT\n- OCI manifest/index resolution, gzip layers, cache, and basic whiteouts\n- Dockerfile builds for common Node/JS app patterns without Docker Engine\n\n`--cpus` / `cpus` is wired through the CLI, SDK, native KVM runtime, ACPI/MP\ntables, and snapshot manifests. Values must be integers from `1` to `64`.\n\nNext release work: full WHP boot parity, broader Dockerfile instruction\ncoverage, migration, jailer, bzImage boot, and the long-lived in-guest exec\nagent.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmisaelzapata%2Fnode-vmm","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmisaelzapata%2Fnode-vmm","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmisaelzapata%2Fnode-vmm/lists"}