A NixOS-based Qubes-like app isolation system based on VMs
Find a file
Davíð Steinn Geirsson 7275698636 feat: add _qubesLite options interface to guest config
Adds options._qubesLite.{disposable, idleTimeout} for host to pass
auto-shutdown configuration to guest.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-04 00:47:35 +00:00
docs/plans Add design doc for on-demand VM launch with auto-shutdown 2026-02-04 00:35:46 +00:00
mktuntap Import local copy of mktuntap 2024-06-29 09:51:38 +01:00
modules feat: add disposable and idleTimeout VM options 2026-02-04 00:42:34 +00:00
rootfs Update to NixOS 25.11 2025-12-01 12:24:02 +00:00
rootfs-nixos feat: add _qubesLite options interface to guest config 2026-02-04 00:47:35 +00:00
.gitignore Initial import 2022-05-23 12:26:58 +01:00
default.nix WIP: Add rootfs-nixos package for NixOS-based VM images 2026-02-03 21:32:14 +00:00
flake.lock Use my crosvm fork for nVidia compat, home paths 2026-02-03 19:18:07 +00:00
flake.nix Add declarative NixOS module for VM configuration 2026-02-03 23:09:56 +00:00
README.md Add declarative NixOS module for VM configuration 2026-02-03 23:09:56 +00:00

qubes-lite

A lightweight virtualization system inspired by Qubes OS. Runs isolated VMs using crosvm (Chrome OS VMM) with different security domains.

Quick Start

Add to your flake inputs:

{
  inputs.qubes-lite.url = "github:your-user/qubes-lite";
}

Import the module and configure VMs in your NixOS configuration:

{ config, pkgs, ... }: {
  imports = [ inputs.qubes-lite.nixosModules.default ];

  programs.qubes-lite = {
    enable = true;
    user = "david";
    vmNetwork = "172.16.200.0/24";
    natEnable = true;
    natInterface = "eth0";

    guestPrograms = with pkgs; [
      firefox
      xfce.xfce4-terminal
    ];

    nixosVms = [
      {
        id = 3;
        name = "banking";
        memory = 4096;
        cpus = 4;
        guestPrograms = [ ];
      }
      {
        id = 5;
        name = "shopping";
        memory = 2048;
        cpus = 2;
      }
      {
        id = 7;
        name = "personal";
        memory = 4096;
        cpus = 4;
        network = false;  # Offline VM
      }
    ];
  };
}

Configuration Options

programs.qubes-lite

Option Type Default Description
enable bool false Enable qubes-lite VM management
user string required User who owns TAP interfaces and runs VMs
vmNetwork string "172.16.200.0/24" Network CIDR for VM networking
natEnable bool false Enable NAT for VM internet access
natInterface string "" External interface for NAT (required if natEnable)
guestPrograms list of packages [] Packages included in all VM rootfs images
nixosVms list of VM configs [] List of NixOS-based VMs to create

VM Configuration (nixosVms items)

Option Type Default Description
id int required VM ID (odd number 3-255). Used for IP and vsock CID
name string required VM name for scripts and TAP interface
memory int 1024 Memory allocation in MB
cpus int 2 Number of virtual CPUs
network bool true Enable networking for this VM
autoStart bool false Automatically start VM with user session
disks list of strings [] Additional disk paths (qcow2 or block devices)
guestPrograms list of packages [] VM-specific packages
guestConfig attrs {} VM-specific NixOS configuration

Commands

After rebuilding NixOS, the following commands are available:

Start a VM

qubes-lite-start-<name>

Example: qubes-lite-start-banking

VMs require a Wayland session. The command uses $XDG_RUNTIME_DIR and $WAYLAND_DISPLAY environment variables.

Run command in VM

run-in-vm <name> <command>

Example: run-in-vm banking firefox

Commands are sent via vsock to the guest's command listener on port 5000.

Systemd services

VMs are also available as user services:

systemctl --user start qubes-lite-banking
systemctl --user status qubes-lite-banking

Network Architecture

IP Addressing

VMs use /31 point-to-point links:

  • VM IP: <network-base>.<id> (e.g., 172.16.200.3)
  • Host TAP IP: <network-base>.<id-1> (e.g., 172.16.200.2)

The host TAP IP acts as the gateway for the VM.

ID Requirements

VM IDs must be:

  • Odd numbers (3, 5, 7, 9, ...)
  • In range 3-255
  • Unique across all VMs

This ensures non-overlapping /31 networks and valid vsock CIDs.

NAT

When natEnable = true, the module configures:

  • IP forwarding (net.ipv4.ip_forward = 1)
  • NAT masquerading on natInterface
  • Internal IPs set to VM IPs

Advanced Configuration

Custom VM NixOS Config

{
  id = 3;
  name = "dev";
  guestConfig = {
    # Mount persistent home directory
    fileSystems."/home/user" = {
      device = "/dev/vdb";
      fsType = "ext4";
    };

    # Enable SSH
    services.openssh.enable = true;
  };
  disks = [ "/dev/mapper/main-dev-home" ];
}

Offline VMs

For sensitive data that should never touch the network:

{
  id = 13;
  name = "vault";
  network = false;
  memory = 2048;
}

Building

# Build the default package (s6-based, for backwards compatibility)
nix build .#

# Build NixOS rootfs only
nix build .#rootfs-nixos

Architecture

Each NixOS VM gets:

  • A dedicated qcow2 rootfs image with packages baked in
  • Overlayfs root (read-only ext4 lower + tmpfs upper)
  • wayland-proxy-virtwl for GPU passthrough
  • vsock command listener for host communication
  • Systemd-based init

The host provides:

  • Persistent TAP interfaces via NixOS networking
  • NAT for internet access (optional)
  • Launcher scripts and systemd services