Nixos based Proxmox VMs

Nixos based Proxmox VMs

After two years with NixOS, I have grown to love having my entire system declared in one place. When I started building Proxmox VMs, I wanted the same thing. I wanted neovim, tmux, and all my settings ready from the first boot, not bolted on afterwards.

NixOS makes this surprisingly simple. With nixos-rebuild build-image, what used to require external tools like nixos-generators is now built in. In this post I will show how I set up a minimalistic VM that can be used as a boilerplate for the coming VMs that will be built.

Bare minimum configuration

To create a Proxmox .vma file there are a few things we need to have in order, we need to import the correct virtualisation module, we need to configure the baseline for Proxmox resources, for remote builds we need a user with SSH-keys and sudo privileges without password, and finally since I use Home Manager, we need to set the hostname explicitly since in my user configuration I import by hostname.

Things not displayed in the configuration below is the virtualisation import, that lives in the configuration.nix, and my user configuration, which is in hosts/common/users

{
  config,
  pkgs,
  lib,
  ...
}: {
  # Platform 
  nixpkgs.hostPlatform = "x86_64-linux";

  # Hostname 
  networking.hostName = lib.mkOverride 40 "test-vm"; # need to take precedence over proxmox-img which has mkdefault with a value of 50

  # Locale 
  console.keyMap = "no";
  i18n.defaultLocale = "en_US.UTF-8";
  i18n.extraLocaleSettings = {
    LC_TIME = "nb_NO.UTF-8";
  };
  time.timeZone = "Europe/Oslo";

  # Boot 
  boot.growPartition = true;
  boot.loader.grub = {
    efiSupport = true;
    efiInstallAsRemovable = true;
    device = "nodev";
  };

  # Filesystems 
  fileSystems."/" = {
    device = "/dev/disk/by-label/nixos";
    autoResize = true;
    fsType = "ext4";
  };

  # Proxmox VMA Image Settings 
  proxmox.qemuConf = {
    cores = 2;
    memory = 4096;
    name = "test-vm";
    net0 = "virtio,bridge=vmbr0";
    bios = "ovmf";
    ostype = "l26";
  };
  virtualisation.diskSize = 16384;

  # SSH 
  services.openssh = {
    enable = true;
    settings.PasswordAuthentication = false;
    settings.KbdInteractiveAuthentication = false;
  };

  security.sudo.wheelNeedsPassword = false;

  # Packages 
  environment.systemPackages = with pkgs; [
    vim
    curl
    git
  ];

  # System State Version 
  system.stateVersion = "24.11";
  home-manager.users.fredrik.home.stateVersion = lib.mkDefault "24.11";
}

Building and applying the VMA file

With the configuration ready we only need to generate a VMA file and move it to the Proxmox server!

Creating and moving the image:

# creating the image
nixos-rebuild build-image --image-variant proxmox --flake .#test-vm

# moving the image
scp result/vzdump-test-vm.vma.zst root@<proxmox-ip>:/var/lib/vz/template/cache/

With the image safely transported to the Proxmox server, we need to restore it and apply cloud-init configurations:

# SSH to the server
ssh root@<proxmox-ip>

# restore the VM
qmrestore /var/lib/vz/template/cache/vzdump-test-vm.vma.zst <vm-id> --unique true --storage local-lvm

# Set up IP addresses, static IPv4 and SLAAC ipv6
qm set <vm-id> --ipconfig0 ip=192.168.10.233/24,gw=192.168.10.1,ip6=auto # Note that I set this up in a test network before I converted to the new IP schema

# start the VM
qm start <vm-id>

Now we have a running VM with sshd server running and our keys in authorized keys! Since I have the same username on my desktop as the new VM I can SSH by just specifying the IP

ssh 192.168.10.233

With this we now have a fully... well, functional VM! With it now built we can use nixos-rebuild with --target-host to update it! The big benefit is that we can use our main machine to build from with beefier specs and one cache that can build for all our VMs

Remote build

During my little exploration in to building VM images I quickly saw the great benefits of the remote building. My beefy main computer to build and push, utilizing the cache for each subsequent build. And this is the part where we need the sudo to be passwordless!

nixos-rebuild switch --flake .#test-vm --target-host fredrik@<ip> --sudo

Conclusion

After going down the rabbit hole of building images for Proxmox and remote builds I am very happy with the outcome, and the ease of use when I need to set up a new VM from my normal flake setup. Having the ability to quickly add in a new template that pulls my user and Home Manager configuration is really a nice way to do it. And it stays declarative in case I need to set up a new machine with the same setup. I have said it before, and can't say it enough, I love having things declaratively