Deploying a NixOS configuration into a QEMU VM

Posted on October 9, 2020

Here is a simple way to deploy a NixOS configuration to a QEMU VM.

The system configuration

For demo purposes, we will be deploying a system that has OpenSSH, forwarding port 22 to the host so as to be able to ssh into the machine.

# demo.nix
{ pkgs, lib, config, ... }:

with lib;
{
  imports = [
    <nixpkgs/nixos/modules/profiles/qemu-guest.nix>
    <nixpkgs/nixos/modules/virtualisation/qemu-vm.nix>
    # import your own NixOS modules here if needed
  ];

  config = {
    nixpkgs.overlays = [
	  # define or import overlays here
	  #
	  (self: super: { })
	  # (import ./my-overlay.nix)
    ];

    services.qemuGuest.enable = true;

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

    boot = {
      growPartition = true;
      kernelParams = [ "console=ttyS0 boot.shell_on_fail" ];
      loader.timeout = 5;
    };

    virtualisation = {
      diskSize = 8000; # MB
      memorySize = 2048; # MB
      writableStoreUseTmpfs = false;
    };

    services.openssh.enable = true;
    services.openssh.permitRootLogin = "yes";

    environment.systemPackages = with pkgs;
      [ # some relevant packages here
        pkgs.ghc
      ];

    # we could alternatively hook root or a custom user
    # to some ssh key pair
    users.extraUsers.root.password = ""; # oops
    users.mutableUsers = false;
  };
}

Building the system

We want to pass this configuration when instantiating the entire NixOS system, and say that we want to derive a VM from it. This can all be accomplished with this reasonably simple command:

$ nix-build '<nixpkgs/nixos>' -A vm --arg configuration ./tde-host.nix
these derivations will be built:
  /nix/store/vlbc7mpmbmcalanbwmj1mwprfrya4izz-system-path.drv
  /nix/store/7srbcqni6sxpncnvn68015m9vmmp74ni-dbus-1.drv
  /nix/store/brax9nap3sbvqhvj6z8x6jx3r5gjwr5x-unit-dbus.service.drv
  /nix/store/ma234k9859hn4krv3f1h95m4mxb2aq8l-unit-polkit.service.drv
  /nix/store/ylrnqqm8vkrfcrgxb4g3fshslfnk8p3f-unit-systemd-fsck-.service.drv
  /nix/store/3prjamgmjhkw6slqgpnsgk3qpm1zjfia-system-units.drv
  /nix/store/jjzhp7x7hfw5sdlmysn4cbwjphrywjfg-unit-dbus.service.drv
  /nix/store/ls6kib0gqngpn4nb3bwfwrid4ig06l1y-user-units.drv
  /nix/store/gkqb5gpcldnjnppy4g2qh17xswlqxvv5-etc.drv
  /nix/store/zs8w34pglgq3iqka86fldklijqcp745q-nixos-system-nixos-20.03.1619.ab3adfe1c76.drv
  /nix/store/jcgk4sbr2ir4sbjvvkd63xrjis55c0ki-closure-info.drv
  /nix/store/l5srdimiiav2ix5ggcfcf8yzprs4c653-run-nixos-vm.drv
  /nix/store/8swgy4mkx9wkayy0yk0lyr07144c1fzs-nixos-vm.drv
building '/nix/store/vlbc7mpmbmcalanbwmj1mwprfrya4izz-system-path.drv'...
created 5331 symlinks in user environment
building '/nix/store/7srbcqni6sxpncnvn68015m9vmmp74ni-dbus-1.drv'...
building '/nix/store/ma234k9859hn4krv3f1h95m4mxb2aq8l-unit-polkit.service.drv'...
building '/nix/store/ylrnqqm8vkrfcrgxb4g3fshslfnk8p3f-unit-systemd-fsck-.service.drv'...
building '/nix/store/brax9nap3sbvqhvj6z8x6jx3r5gjwr5x-unit-dbus.service.drv'...
building '/nix/store/jjzhp7x7hfw5sdlmysn4cbwjphrywjfg-unit-dbus.service.drv'...
building '/nix/store/3prjamgmjhkw6slqgpnsgk3qpm1zjfia-system-units.drv'...
building '/nix/store/ls6kib0gqngpn4nb3bwfwrid4ig06l1y-user-units.drv'...
building '/nix/store/gkqb5gpcldnjnppy4g2qh17xswlqxvv5-etc.drv'...
building '/nix/store/zs8w34pglgq3iqka86fldklijqcp745q-nixos-system-nixos-20.03.1619.ab3adfe1c76.drv'...
building '/nix/store/jcgk4sbr2ir4sbjvvkd63xrjis55c0ki-closure-info.drv'...
building '/nix/store/l5srdimiiav2ix5ggcfcf8yzprs4c653-run-nixos-vm.drv'...
building '/nix/store/8swgy4mkx9wkayy0yk0lyr07144c1fzs-nixos-vm.drv'...
/nix/store/4ylbbc8c99f1l1k7x6k584xa018hi216-nixos-vm

(That last path is the one ./result points to as well.)

Running the VM

You can then use /nix/store/4ylbbc8c99f1l1k7x6k584xa018hi216-nixos-vm/bin/run-nixos-vm or result/bin/run-nixos-vm to boot the resulting VM through QEMU. You will quite likely want to forward some ports, to be able to ssh into the VM and perhaps talk to some other services listening on some ports left open by the firewall. To accomplish this, you can set the QEMU_NET_OPTS env var before running the VM.

For example, to forward the VM’s SSH port (22) to the host’s port 2221, and the VM’s HTTP port (80) to the host’s 8080, you could do (after making sure that 80 and 22 are in the VM configuration’s networking.firewall.allowedTCPPorts):

$ export QEMU_NET_OPTS="hostfwd=tcp::2221-:22,hostfwd=tcp::8080-:80"
$ result/bin/run-nixos-vm

SSH-ing into the VM

run-nixos-vm waits for the VM process to be over, so fire up another terminal and write:

$ ssh -oUserKnownHostsFile=/dev/null -oStrictHostKeyChecking=no root@localhost -p 2221

When prompted for a password, just hit Enter (because of users.extraUsers.root.password = ""), and you should now be logged in as root in the VM.

Powered by Hakyll - RSS feed - servant paper