Run Claude Code in an isolated NixOS microVM via microvm.nix (QEMU+KVM). Your project directory is mounted read-write at /work inside the guest via virtiofs — no root required.
Claude Code starts automatically on boot. Exiting claude shuts down the VM.
- Nix with flakes enabled
- KVM support (
/dev/kvm)
# Build and run with current directory mounted at /work
make vm.run
# Mount a specific project directory
WORK_DIR=/path/to/project make vm.run# From the repo directory
WORK_DIR=. nix run
# From a local checkout
WORK_DIR=/path/to/project nix run /path/to/this/repo
# Directly from git
WORK_DIR=. nix run github:systemstart/claude-microvmnix profile install github:systemstart/claude-microvm
# Now available everywhere
WORK_DIR=/path/to/project microvm-runAdd as a dependency in another project's flake.nix:
{
inputs.claude-vm.url = "github:systemstart/claude-microvm";
outputs = { nixpkgs, claude-vm, ... }:
let system = "x86_64-linux"; in {
devShells.${system}.default = nixpkgs.legacyPackages.${system}.mkShell {
packages = [ claude-vm.packages.${system}.vm ];
};
};
}Then nix develop gives you microvm-run in the shell.
The host WORK_DIR is shared into the VM at /work using virtiofs. A virtiofsd daemon is started automatically as a systemd user service (claude-vm-virtiofsd-<id>, where <id> is derived from the work directory path) — no root or sudo needed. It runs unprivileged in a user namespace with UID/GID translation so files created inside the VM are owned by your host user.
Each work directory gets its own virtiofsd instance, so multiple VMs can run in parallel on different projects. The daemon persists between VM restarts for fast re-launches. Manage it with:
systemctl --user list-units 'claude-vm-virtiofsd-*'
systemctl --user stop claude-vm-virtiofsd-<id>The VM provides strong isolation from the host:
- Filesystem — only
/workis shared; everything else is VM-local and ephemeral - Processes — completely isolated (separate kernel)
- Network — QEMU user-mode NAT; the VM can reach the internet but can't bind host ports
To let Claude Code run fully autonomously inside the VM (no permission prompts), add --dangerously-skip-permissions to the claude invocation in flake.nix.
Exiting Claude Code automatically powers off the VM.
No ports are forwarded by default. To expose ports, edit flake.nix:
microvm.qemu.extraArgs = [
"-netdev" "user,id=usernet,hostfwd=tcp::8080-:8080"
"-device" "virtio-net-device,netdev=usernet"
];
networking.firewall.allowedTCPPorts = [ 8080 ];Rebuild with make vm.
| Resource | Default |
|---|---|
| RAM | 4096 MB |
| vCPUs | 4 |
| Network | User-mode (SLiRP) |
| Work dir | Host directory via virtiofs (read-write) |