Compare commits
2 commits
5fbdd36107
...
e196160aac
| Author | SHA1 | Date | |
|---|---|---|---|
| e196160aac | |||
| f997b72f40 |
2 changed files with 134 additions and 3 deletions
131
README.md
Normal file
131
README.md
Normal file
|
|
@ -0,0 +1,131 @@
|
||||||
|
# vmix
|
||||||
|
|
||||||
|
Composable QEMU VM image building and orchestration for NixOS.
|
||||||
|
|
||||||
|
## What it does
|
||||||
|
|
||||||
|
vmix provides:
|
||||||
|
- **Image building** — reproducible Linux (Debian) and Windows qcow2 images via Nix derivations
|
||||||
|
- **NixOS module** — declarative VM management with QEMU in network namespaces
|
||||||
|
- **CLI tool** — build, copy-to-disk, and run images from the command line
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
### As a flake input
|
||||||
|
|
||||||
|
```nix
|
||||||
|
# flake.nix
|
||||||
|
inputs.vmix.url = "git+https://git.sagar.ch/dotfiles/vmix.nix.git";
|
||||||
|
|
||||||
|
# In your NixOS configuration (or globally via serverFunctions)
|
||||||
|
imports = [ inputs.vmix.nixosModules.default ];
|
||||||
|
|
||||||
|
# Use vmixLib via overlay
|
||||||
|
nixpkgs.overlays = [ inputs.vmix.overlays.default ];
|
||||||
|
# Then: pkgs.vmixLib.linux, pkgs.vmixLib.windows, pkgs.vmixLib.network
|
||||||
|
```
|
||||||
|
|
||||||
|
### As a CLI
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Enter dev shell
|
||||||
|
nix develop
|
||||||
|
|
||||||
|
# Build an image
|
||||||
|
vmix build --image windows.images.win10.laptop \
|
||||||
|
--generalize username=User,password=secret,hostname=PC
|
||||||
|
|
||||||
|
# Write to local disk
|
||||||
|
vmix copy --image windows.images.win10.laptop \
|
||||||
|
--generalize username=User,password=secret \
|
||||||
|
--to-disk /dev/sda
|
||||||
|
|
||||||
|
# Write to remote disk (streamed via SSH + LZ4)
|
||||||
|
vmix copy --image windows.images.win10.laptop \
|
||||||
|
--generalize username=User,password=secret \
|
||||||
|
--to-remote-disk root@10.10.10.100:/dev/nvme0n1
|
||||||
|
|
||||||
|
# Boot a built image with QEMU
|
||||||
|
vmix run ./result --mem 8192 --smp 8 --ahci
|
||||||
|
```
|
||||||
|
|
||||||
|
## Flake outputs
|
||||||
|
|
||||||
|
| Output | Description |
|
||||||
|
|--------|-------------|
|
||||||
|
| `overlays.default` | Nix overlay exposing `pkgs.vmixLib` |
|
||||||
|
| `nixosModules.default` | NixOS module for declarative VM management |
|
||||||
|
| `lib.x86_64-linux` | Library functions (images + network utilities) |
|
||||||
|
| `packages.x86_64-linux.default` | vmix CLI tool |
|
||||||
|
| `apps.x86_64-linux.default` | Runnable vmix app |
|
||||||
|
|
||||||
|
## Repository structure
|
||||||
|
|
||||||
|
```
|
||||||
|
flake.nix # Flake entry point
|
||||||
|
module.nix # NixOS module export
|
||||||
|
overlay.nix # Nix overlay (vmixLib pinned to nixpkgs 25-11)
|
||||||
|
cli.nix # CLI tool (build, copy, run)
|
||||||
|
|
||||||
|
lib/
|
||||||
|
default.nix # Exports: images (linux + windows) + network
|
||||||
|
network.nix # IPv4/CIDR utilities
|
||||||
|
images/
|
||||||
|
linux/ # Debian image building + customization
|
||||||
|
windows/ # Windows image building + customization
|
||||||
|
helpers/ # makeImage, customizeImage, makeWinISO, etc.
|
||||||
|
templates/ # Registry tweaks, app installers, essentials
|
||||||
|
drivers/ # VirtIO, AMD GPU drivers
|
||||||
|
win10/ # Windows 10 LTSC images
|
||||||
|
win11/ # Windows 11 images
|
||||||
|
|
||||||
|
nixos/
|
||||||
|
default.nix # NixOS module entry point
|
||||||
|
networks/ # Network namespace management (LAN, WAN, macvtap)
|
||||||
|
vms/ # VM lifecycle management (QEMU, tap devices, DHCP)
|
||||||
|
```
|
||||||
|
|
||||||
|
## Image building
|
||||||
|
|
||||||
|
### Windows pipeline
|
||||||
|
|
||||||
|
1. `makeImage` — unattended install from upstream ISO via QEMU
|
||||||
|
2. `customizeImageFold` — apply modular templates (registry, apps, drivers)
|
||||||
|
3. `generalize` — sysprep + OOBE for deployment to real hardware
|
||||||
|
|
||||||
|
### Linux pipeline
|
||||||
|
|
||||||
|
1. Fetch upstream Debian cloud image
|
||||||
|
2. `customizeImageFold` — apply templates via `virt-customize`
|
||||||
|
|
||||||
|
### Key patterns
|
||||||
|
|
||||||
|
- **`customizeImageFold`** — `builtins.foldl'` over templates for composable layered images
|
||||||
|
- **`_vmixOsType`** — all images carry `"linux"` or `"windows"` metadata for auto-detection
|
||||||
|
- **Offline registry** — Windows templates use `ControlSet001` for offline `virt-win-reg --merge`
|
||||||
|
|
||||||
|
## NixOS module
|
||||||
|
|
||||||
|
Declare VMs and networks:
|
||||||
|
|
||||||
|
```nix
|
||||||
|
vmix.namespaces."lab" = {
|
||||||
|
networks.lan1 = {
|
||||||
|
subnet = "10.99.1.0/24";
|
||||||
|
dhcp.enable = true;
|
||||||
|
};
|
||||||
|
vms.myvm = {
|
||||||
|
image = pkgs.vmixLib.linux.images.debian.v12.upstream;
|
||||||
|
memory = 2048;
|
||||||
|
cores = 2;
|
||||||
|
interfaces.lan1 = { ip = "10.99.1.10"; };
|
||||||
|
autostart = true;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
Features:
|
||||||
|
- Network namespaces with WAN (veth), LAN (bridge + dnsmasq), macvtap
|
||||||
|
- ACPI graceful shutdown via QMP socket
|
||||||
|
- 9p shares with auto-created mount targets
|
||||||
|
- Conditional macvtap service (only created when macvtaps are configured)
|
||||||
|
|
@ -231,7 +231,7 @@ let
|
||||||
in
|
in
|
||||||
lib.optionalAttrs (cfg.enable) {
|
lib.optionalAttrs (cfg.enable) {
|
||||||
"vm.vmix@${vmCfg.name}" = rec {
|
"vm.vmix@${vmCfg.name}" = rec {
|
||||||
bindsTo = [ "net.vmix@${spaceName}.target" "macvtaps.vm.vmix@${vmCfg.name}.service" ];
|
bindsTo = [ "net.vmix@${spaceName}.target" ] ++ lib.optional (allMacvtaps != []) "macvtaps.vm.vmix@${vmCfg.name}.service";
|
||||||
unitConfig.JoinsNamespaceOf = "ns.net.vmix@${spaceName}.service";
|
unitConfig.JoinsNamespaceOf = "ns.net.vmix@${spaceName}.service";
|
||||||
after = bindsTo;
|
after = bindsTo;
|
||||||
path = with pkgs; [ iproute2 qemu gawk coreutils ];
|
path = with pkgs; [ iproute2 qemu gawk coreutils ];
|
||||||
|
|
@ -248,9 +248,9 @@ let
|
||||||
ProtectSystem = lib.mkForce false;
|
ProtectSystem = lib.mkForce false;
|
||||||
SupplementaryGroups = [ "kvm" ];
|
SupplementaryGroups = [ "kvm" ];
|
||||||
};
|
};
|
||||||
wantedBy = lib.mkIf vmCfg.autostart [ "multi-user.target" ];
|
wantedBy = lib.mkIf vmCfg.autostart [ "multi-user.target" ];
|
||||||
};
|
};
|
||||||
|
} // lib.optionalAttrs (allMacvtaps != []) {
|
||||||
"macvtaps.vm.vmix@${vmCfg.name}" = rec {
|
"macvtaps.vm.vmix@${vmCfg.name}" = rec {
|
||||||
bindsTo = [ "net.vmix@${spaceName}.target" ];
|
bindsTo = [ "net.vmix@${spaceName}.target" ];
|
||||||
after = bindsTo;
|
after = bindsTo;
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue