NixOS VMs part 4: Managing VMs with home-manager and sway-home

Updated January 31, 2026 7 minutes

This is part 4 of a series on nixos-vm-template:

  1. Running code agents in an immutable NixOS VM
  2. Bootstrapping a Docker server with immutable NixOS on Proxmox
  3. Mutable VMs are cool too
  4. Managing VMs with home-manager and sway-home (this post)

The previous posts covered creating and managing VMs with nixos-vm-template. But how do you actually integrate it into your daily workflow on your workstation? This post covers using home-manager to set up the VM tooling, create shell aliases with tab completion, and manage updates through the Nix ecosystem.

What is home-manager?

Home Manager is a tool for managing user environments using the Nix package manager. Instead of manually installing programs and editing dotfiles, you declare what you want in a Nix configuration file, and home-manager builds and activates that environment.

The key benefits:

  • Declarative: Your entire user environment is defined in code
  • Reproducible: The same configuration produces the same result on any machine
  • Rollback: Every configuration change creates a new “generation” you can switch back to
  • Isolated: Packages are installed in the Nix store, not globally

Home-manager works on any Linux distribution - you don’t need NixOS. You just need the Nix package manager installed.

What is sway-home?

sway-home is a home-manager configuration that sets up a complete development environment. It includes:

  • Shell configuration (bash, with aliases and completions)
  • Editor setup (Emacs)
  • Development tools (git, ripgrep, just, etc.)
  • Window manager config (Sway, if you’re on Wayland)
  • And relevantly: nixos-vm-template integration

When you activate sway-home with home-manager, you get all of this configured and ready to use. The configuration is modular - you can enable or disable components as needed.

The hm-* commands

Sway-home provides shell aliases for common home-manager operations. These are the commands you’ll use to manage your environment:

Command What it does
hm-switch Rebuild and activate your home-manager configuration
hm-update Update flake.lock to fetch latest versions of all inputs
hm-upgrade Update + switch in one step
hm-generations List your configuration history (each activation is a “generation”)
hm-rollback Revert to the previous generation if something breaks
hm-metadata Show the git revisions of all flake inputs
hm-pull Git pull the sway-home repository

The typical workflow: edit your configuration, run hm-switch to apply it. If you want the latest upstream changes from sway-home and its dependencies (including nixos-vm-template), run hm-upgrade.

The nixos-vm-template module

One of sway-home’s modules, nixos-vm-template.nix, integrates the VM tooling directly into your shell. It does three things:

  1. Symlinks the nixos-vm-template repository into ~/nixos-vm-template (bound to the nix store, so it’s read-only but versioned)
  2. Creates a default environment file at ~/.config/nixos-vm-template/env with XDG-compliant paths
  3. Sets up the vm shell alias with tab completion

After activating home-manager, you get a vm command that works like this:

vm list                    # List all VMs
vm create                  # Create a VM (interactive wizard)
vm ssh myvm                # SSH in
vm upgrade myvm            # Upgrade to new image

The vm create command runs an interactive configuration wizard that prompts for the VM name, profiles, memory, CPUs, disk size, and network mode. After configuration, it builds the image, creates the VM, and starts it automatically.

The vm command operates on the local machine’s libvirt backend.

The alias is defined using the _justfile_alias function, which wraps just with a specific Justfile and environment file:

_justfile_alias vm \
  "$HOME/nixos-vm-template/Justfile" \
  "$HOME/.config/nixos-vm-template/env"

This gives you full tab completion for recipes and machine names. Tab completion works for commands like vm ssh, vm status, and vm upgrade that take a VM name as an argument.

The nix store binding

Here’s an important detail: ~/nixos-vm-template is a symlink into the nix store, not a regular git clone. The module declares:

home.file."nixos-vm-template".source = inputs.nixos-vm-template;

This means the repository is read-only. You can’t edit files there directly. The benefit is reproducibility - your VM tooling version is pinned to a specific commit in the sway-home flake.lock.

The downside is that changes to nixos-vm-template upstream won’t appear until you update and rebuild. More on that below.

Where things live

The environment file sets up XDG-compliant paths:

Path Purpose
~/.config/nixos-vm-template/machines/ Machine configs (identity files, profiles)
~/.config/nixos-vm-template/libvirt/ Libvirt XML templates
~/.config/nixos-vm-template/env Backend configuration
~/.local/share/nixos-vm-template/ Built images and VM disks

Your machine configs are in ~/.config, so they’re easy to back up and track. The large image files go in ~/.local/share where they won’t clutter your config backups.

Updating nixos-vm-template

Since ~/nixos-vm-template is bound to the nix store, you need to update home-manager to get new versions:

hm-update    # Update flake.lock (fetches latest nixos-vm-template)
hm-switch    # Rebuild and activate

After this, ~/nixos-vm-template points to the new version and all vm commands use the updated code.

To see what version you’re running:

hm-metadata

This shows the git revisions of all flake inputs, including nixos-vm-template.

Home-manager inside the VM

So far we’ve talked about home-manager on your host workstation - managing your shell, your tools, and the nixos-vm-template integration. But you can also run home-manager inside the VM to manage the user environment there.

To get home-manager inside a VM, include the home-manager profile when creating it. Run vm create and select the home-manager profile along with your other desired profiles (e.g., dev, docker).

The home-manager profile installs sway-home’s configuration inside the VM, giving you the same shell setup, editor config, and tools. This is useful for development VMs where you want a consistent environment.

The profile works in both immutable and mutable VMs, but with slightly different behavior:

  • In immutable VMs, a custom activation service creates symlinks from /home/<user> to the nix store. This handles the complexity of the read-only root and bind-mounted home directories.

  • In mutable VMs, standard home-manager activation runs. The filesystem is writable, so home-manager just does its normal thing.

Note that the dev profile includes basic development tools (neovim, tmux, etc.) but does not include home-manager. If you want the full sway-home experience inside the VM, add the home-manager profile explicitly.

Creating a Proxmox alias

The vm alias points at libvirt by default. If you also have a Proxmox server, you can create a separate alias for it.

First, create an environment file for your Proxmox backend:

cat > ~/.config/nixos-vm-template/env-pve << 'EOF'
BACKEND=proxmox
PVE_HOST=pve
PVE_NODE=pve
PVE_STORAGE=local-zfs
PVE_DISK_FORMAT=raw
PVE_BRIDGE=vmbr0
OUTPUT_DIR=$HOME/.local/share/nixos-vm-template
MACHINES_DIR=$HOME/.config/nixos-vm-template/machines-pve
LIBVIRT_DIR=$HOME/.config/nixos-vm-template/libvirt
EOF

Note the separate MACHINES_DIR - this keeps Proxmox machine configs separate from libvirt ones, avoiding confusion.

Then add the alias to your shell config (e.g., ~/.bashrc or the sway-home config/bash/alias.sh):

_justfile_alias pve \
  "$HOME/nixos-vm-template/Justfile" \
  "$HOME/.config/nixos-vm-template/env-pve"

Now you have two aliases:

vm create       # Creates on local libvirt (interactive)
pve create      # Creates on Proxmox server (interactive)

Both use the same nixos-vm-template codebase and run the same interactive configuration wizard, but target different backends with different configurations.

Disk space and garbage collection

VM images accumulate in ~/.local/share/nixos-vm-template/. Each profile combination produces an image (typically 3-4 GB), and each VM has its own /var disk.

To see disk usage:

du -sh ~/.local/share/nixos-vm-template/

To clean up old images (keeping currently-used ones):

vm clean

For the nix store itself, home-manager profiles accumulate over time. To reclaim space:

nix-collect-garbage --delete-older-than 30d

This removes store paths that aren’t referenced by any generation newer than 30 days. Be careful - this includes home-manager generations, so you’ll lose the ability to roll back to older configs.

To see how much space you’d reclaim:

nix-store --gc --print-dead

The full workflow

Here’s what a typical session looks like:

# Update everything
hm-pull && hm-upgrade

# Check what version of nixos-vm-template you're running
hm-metadata | grep nixos-vm-template

# Create a VM for a new project (interactive wizard)
vm create
# Enter: project-foo, claude/dev/docker/home-manager, 8192, 4, etc.
# The VM starts automatically after creation

# Work on the project...
vm ssh user@project-foo
# ... do things ...

# Later, upgrade the VM to pick up profile changes
vm upgrade project-foo

# Deploy to production on Proxmox (interactive wizard)
pve create
# Enter: project-foo, docker, etc.
# The VM starts automatically

# Clean up old images
vm clean
nix-collect-garbage --delete-older-than 7d

Conclusion

The sway-home integration turns nixos-vm-template into a first-class shell tool. The vm alias gives you quick access to all VM operations with tab completion, and the home-manager binding ensures reproducible tooling versions across machines. Adding backend-specific aliases like pve lets you manage VMs across multiple hypervisors from a single workflow.

The key things to remember:

  • ~/nixos-vm-template is read-only; run hm-upgrade to get updates
  • Machine configs live in ~/.config/nixos-vm-template/machines/
  • Use nix-collect-garbage periodically to reclaim disk space
  • Create separate aliases and machine directories for each backend


You can discuss this blog on Matrix (Element): #blog-rymcg-tech:enigmacurry.com

This blog is copyright EnigmaCurry and dual-licensed CC-BY-SA and MIT. The source is on github: enigmacurry/blog.rymcg.tech and PRs are welcome. ❤️