Frequently Asked Questions

Can I support the development and maintenance of this project?

❤ Sponsor

How to centralize logging with journald?

That is possible without even requiring a network transport by just making the journals available to the host as a share. Because journald identifies hosts by their /etc/machine-id, we propose to use static content for that file. Add a NixOS module like the following to your MicroVM configuration:

environment.etc."machine-id" = {
  mode = "0644";
  text =
    # change this to suit your flake's interface
    self.lib.addresses.machineId.${config.networking.hostName} + "\n";
};

microvm.shares = [ {
  # On the host
  source = "/var/lib/microvms/${config.networking.hostName}/journal";
  # In the MicroVM
  mountPoint = "/var/log/journal";
  tag = "journal";
  proto = "virtiofs";
  socket = "journal.sock";
} ];

Last, make the MicroVM journals available to your host. The machine-id must be available.

systemd.tmpfiles.rules = map (vmHost:
  let
    machineId = self.lib.addresses.machineId.${vmHost};
  in
    # creates a symlink of each MicroVM's journal under the host's /var/log/journal
    "L+ /var/log/journal/${machineId} - - - - /var/lib/microvms/${vmHost}/journal/${machineId}"
) (builtins.attrNames self.lib.addresses.machineId);

Once your MicroVM's journal data is visible in the /var/log/journal/$machineId/ directories, journalctl can pick it up using the -m/--merge switch.

Can I build with hypervisors from the host's nixpkgs instead of the MicroVM's?

Yes. This scenario is enabled through the flake's lib.buildRunner function. See the nix run microvm#build-microvm script that you will need to customize to fit your deployment scenario.

How can I deploy imperatively from Continuous Integration?

Do this by integrating into your automation what the microvm command does.

environment.systemPackages = [ (
  # Provide a manual updating script that fetches the latest
  # updated+built system from Hydra
  pkgs.writeScriptBin "update-microvm" ''
    #! ${pkgs.runtimeShell} -e

    if [ $# -lt 1 ]; then
      NAMES="$(ls -1 /var/lib/microvms)"
    else
      NAMES="$@"
    fi

    for NAME in $NAMES; do
      echo MicroVM $NAME
      cd /var/lib/microvms/$NAME
      # Is this truly the flake that is being built on Hydra?
      if [ "$(cat flake)" = "git+https://gitea.example.org/org/nix-config?ref=flake-update" ]; then
        NEW=$(curl -sLH "Accept: application/json" https://hydra.example.org/job/org/nix-config/$NAME/latest | ${pkgs.jq}/bin/jq -er .buildoutputs.out.path)
        nix copy --from https://nix-cache.example.org $NEW

        if [ -e booted ]; then
          nix store diff-closures $(readlink booted) $NEW
        elif [ -e current ]; then
          echo "NOT BOOTED! Diffing to old current:"
          nix store diff-closures $(readlink current) $NEW
        else
          echo "NOT BOOTED?"
        fi

        CHANGED=no
        if ! [ -e current ]; then
          ln -s $NEW current
          CHANGED=yes
        elif [ "$(readlink current)" != $NEW ]; then
          rm -f old
          cp --no-dereference current old
          rm -f current
          ln -s $NEW current
          CHANGED=yes
        fi
      fi

      if [ "$CHANGED" = "yes" ]; then
        systemctl restart microvm@$NAME
      fi
      echo
    done
  ''
) ];

Can I include my host's <nixpkgs> channel when building the VM?

Use the following configuration if you build your MicroVM with --impure from channels, not Flakes:

nix.nixPath = [
  "nixpkgs=${builtins.storePath <nixpkgs>}"
];

How do I let the microvm user access block devices?

You can re-add the following line to your host's NixOS configuration which was removed from microvm.nix:

users.users.microvm.extraGroups = [ "disk" ];

The more secure solution would be writing custom services.udev.extraRules that assign ownership/permissions to the individually used block devices.