Agent skill
vm-provision
Provision and integrate new VMs into the NixOS Proxmox fleet
Install this agent skill to your Project
npx add-skill https://github.com/majiayu000/claude-skill-registry/tree/main/skills/other/other/vm-provision
SKILL.md
VM Provisioning Skill
This skill guides you through creating and integrating new VMs into the NixOS homelab fleet on Proxmox.
Overview
The VM automation system uses:
- Template 9002: Ubuntu cloud image with UEFI for initial bootstrap
- nixos-anywhere: Two-phase deployment (kexec → disko+install)
- Cloud-init: SSH key injection for initial access
- Disko: Declarative disk partitioning
Workflow
Step 1: Define the VM
Add entry to vms/definitions.nix under managed:
managed = {
my-vm = {
vmid = 111; # Unique, check vmidRanges (100-199 for production)
cores = 4;
memory = 8192; # MB
disk = "32G";
storage = "nvmeprom";
nixosConfig = "my-vm"; # Must match hosts/{name}
purpose = "Description of VM purpose";
services = ["service1" "service2"];
};
};
Step 2: Create Host Configuration
Create hosts/{name}/ with four files:
configuration.nix:
{pkgs, inputs, ...}: {
imports = [
inputs.disko.nixosModules.disko
./disko.nix
./hardware-configuration.nix
];
boot = {
loader.systemd-boot.enable = true;
loader.efi.canTouchEfiVariables = true;
};
homelab = {
ssh = {
enable = true;
secure = false; # or true for password auth disabled
};
tailscale.enable = true;
nixCaches = {
enable = true;
profile = "internal";
};
};
services.qemuGuest.enable = true;
system.stateVersion = "25.05";
}
disko.nix (standard for all VMs):
{
disko.devices = {
disk = {
main = {
type = "disk";
device = "/dev/sda";
content = {
type = "gpt";
partitions = {
ESP = {
size = "512M";
type = "EF00";
content = {
type = "filesystem";
format = "vfat";
mountpoint = "/boot";
mountOptions = ["umask=0077"];
};
};
root = {
size = "100%";
content = {
type = "filesystem";
format = "ext4";
mountpoint = "/";
};
};
};
};
};
};
};
}
hardware-configuration.nix:
{config, lib, modulesPath, ...}: {
imports = [(modulesPath + "/profiles/qemu-guest.nix")];
boot.initrd.availableKernelModules = ["ata_piix" "uhci_hcd" "virtio_pci" "virtio_scsi" "sd_mod" "sr_mod"];
boot.initrd.kernelModules = [];
boot.kernelModules = [];
boot.extraModulePackages = [];
# Filesystem definitions handled by disko.nix
swapDevices = [];
nixpkgs.hostPlatform = lib.mkDefault "x86_64-linux";
}
home.nix:
{...}: {
imports = [
../../home/home.nix
../../home/utils/common.nix
];
}
Step 3: Add to hosts.nix
Add placeholder entry to hosts.nix:
my-vm = {
configurationFile = ./hosts/my-vm/configuration.nix;
homeFile = ./hosts/my-vm/home.nix;
user = "abl030";
homeDirectory = "/home/abl030";
hostname = "my-vm";
sshAlias = "my-vm";
sshKeyName = "ssh_key_abl030";
publicKey = "ssh-ed25519 PLACEHOLDER_KEY_WILL_BE_ADDED_DURING_PROVISIONING";
authorizedKeys = masterKeys;
};
Step 4: Provision
nix run .#provision-vm my-vm
This will:
- Clone template 9002
- Configure resources (CPU, RAM, disk)
- Inject SSH keys via cloud-init
- Install NixOS via nixos-anywhere (two-phase)
- Reboot and verify SSH access
Step 5: Post-Provision (Fleet Integration)
After provisioning completes, note the IP address and run:
nix run .#post-provision-vm my-vm <IP> <VMID>
This will:
- Extract SSH host key from VM
- Update hosts.nix with real public key
- Convert SSH key to age key
- Update secrets/.sops.yaml with age key
- Re-encrypt all secrets with new key
- Commit changes to git
Step 6: Deploy with Secrets
nixos-rebuild switch --flake .#my-vm --target-host my-vm
Key Files
| File | Purpose |
|---|---|
vms/definitions.nix |
VM specs (source of truth) |
vms/provision.sh |
Main provisioning script |
vms/post-provision.sh |
Fleet integration |
vms/proxmox-ops.sh |
Proxmox SSH wrapper |
hosts.nix |
Host definitions with SSH keys |
secrets/.sops.yaml |
Age keys for secrets |
Safety Notes
- Imported VMs (in
vms/definitions.nixunderimported) havereadonly = true- automation will refuse to touch them - VMID conflicts are checked before provisioning
- Confirmation prompt required before creating VM
- SSH access is via
abl030user, not root
Troubleshooting
- IP changes during provisioning: Normal - the script handles this automatically via MAC/ARP lookup
- SSH host key verification fails: Run
ssh-keygen -R <ip>to clear old key - sops updatekeys fails: Ensure you're using
--config secrets/.sops.yaml
Recommended Agent Skills
Expand your agent's capabilities with these related and highly-rated skills.
agent-ops-spec
Manage specification documents in .agent/specs/. Use when user provides requirements, acceptance criteria, or feature descriptions that need to be tracked and validated against implementation.
agent-ops-state
Maintain .agent state files. Use at session start, after meaningful steps, and before concluding: read/update constitution/memory/focus/issues/baseline consistently.
agent-ops-spec
Manage specification documents in .agent/specs/. Use when user provides requirements, acceptance criteria, or feature descriptions that need to be tracked and validated against implementation.
agent-ops-testing
Test strategy, execution, and coverage analysis. Use when designing tests, running test suites, or analyzing test results beyond baseline checks.
agent-ops-testing
Test strategy, execution, and coverage analysis. Use when designing tests, running test suites, or analyzing test results beyond baseline checks.
agent-ops-state
Maintain .agent state files. Use at session start, after meaningful steps, and before concluding: read/update constitution/memory/focus/issues/baseline consistently.
Didn't find tool you were looking for?