mirror of
https://github.com/NixOS/nixpkgs.git
synced 2026-08-27 02:34:53 +00:00
205 lines
6.2 KiB
Nix
205 lines
6.2 KiB
Nix
# Backend-neutral pieces of running a NixOS guest in a VM.
|
|
#
|
|
# `qemu-vm.nix` still carries its own copies of these options: the two modules are
|
|
# never imported together because a configuration has exactly one VM backend, so
|
|
# the duplicated declarations cannot collide.
|
|
# Deduping `qemu-vm.nix` onto this module is a refactor left for later.
|
|
{
|
|
config,
|
|
lib,
|
|
pkgs,
|
|
...
|
|
}:
|
|
|
|
let
|
|
cfg = config.virtualisation;
|
|
in
|
|
{
|
|
options = {
|
|
|
|
virtualisation.memorySize = lib.mkOption {
|
|
type = lib.types.ints.positive;
|
|
default = 1024;
|
|
description = ''
|
|
The memory size in megabytes of the virtual machine.
|
|
'';
|
|
};
|
|
|
|
virtualisation.cores = lib.mkOption {
|
|
type = lib.types.ints.positive;
|
|
default = 1;
|
|
description = ''
|
|
Specify the number of cores the guest is permitted to use.
|
|
The number can be higher than the available cores on the
|
|
host system.
|
|
'';
|
|
};
|
|
|
|
# `virtualisation.diskSize` comes from `disk-size-option.nix` in the default module list.
|
|
|
|
virtualisation.additionalPaths = lib.mkOption {
|
|
type = lib.types.listOf lib.types.path;
|
|
default = [ ];
|
|
description = ''
|
|
A list of paths whose closure should be made available to the VM.
|
|
|
|
The closure is copied into the VM's Nix store image and registered in
|
|
the guest's Nix database.
|
|
'';
|
|
};
|
|
|
|
virtualisation.writableStore = lib.mkOption {
|
|
type = lib.types.bool;
|
|
default = true;
|
|
description = ''
|
|
If enabled, the Nix store in the VM is made writable by layering an
|
|
overlay filesystem on top of the (read-only) store image.
|
|
'';
|
|
};
|
|
|
|
virtualisation.writableStoreUseTmpfs = lib.mkOption {
|
|
type = lib.types.bool;
|
|
default = true;
|
|
description = ''
|
|
Use a tmpfs for the writable store instead of writing to a disk image.
|
|
|
|
Turning this off makes store writes survive a reboot, at the cost of
|
|
needing a disk to put them on.
|
|
'';
|
|
};
|
|
|
|
virtualisation.useHostCerts = lib.mkOption {
|
|
type = lib.types.bool;
|
|
default = false;
|
|
description = ''
|
|
If enabled, when `NIX_SSL_CERT_FILE` is set on the host,
|
|
pass the CA certificates from the host to the VM.
|
|
'';
|
|
};
|
|
|
|
virtualisation.sharedDirectories = lib.mkOption {
|
|
type = lib.types.attrsOf (
|
|
lib.types.submodule (
|
|
{ name, ... }:
|
|
{
|
|
options.source = lib.mkOption {
|
|
type = lib.types.str;
|
|
description = "The path of the directory to share, can be a shell variable";
|
|
};
|
|
options.target = lib.mkOption {
|
|
type = lib.types.path;
|
|
description = "The mount point of the directory inside the virtual machine";
|
|
};
|
|
options.tag = lib.mkOption {
|
|
type = lib.types.str;
|
|
default = name;
|
|
description = ''
|
|
The tag the guest mounts this share by. Defaults to the attribute
|
|
name. Backends impose their own length limits on tags.
|
|
'';
|
|
};
|
|
}
|
|
)
|
|
);
|
|
default = { };
|
|
example = {
|
|
my-share = {
|
|
source = "/path/to/be/shared";
|
|
target = "/mnt/shared";
|
|
};
|
|
};
|
|
description = ''
|
|
An attribute set of directories that will be shared with the virtual
|
|
machine. The attribute name is used as the mount tag.
|
|
'';
|
|
};
|
|
|
|
virtualisation.host.pkgs = lib.mkOption {
|
|
type = lib.types.pkgs;
|
|
default = pkgs;
|
|
defaultText = lib.literalExpression "pkgs";
|
|
example = lib.literalExpression ''
|
|
import pkgs.path { system = "aarch64-darwin"; }
|
|
'';
|
|
description = ''
|
|
Package set to use for the host-side tooling that launches the VM.
|
|
|
|
This is not the guest's package set: the host may well be a different
|
|
platform than the guest, which is the entire point of running a VM.
|
|
'';
|
|
};
|
|
};
|
|
|
|
config = {
|
|
|
|
# Passed on the kernel command line: a direct reference would make the closure self-referential.
|
|
systemd.services.register-nix-paths = lib.mkIf config.nix.enable {
|
|
# Runs early so the store DB is populated first; `--load-db` needs no daemon.
|
|
unitConfig.DefaultDependencies = false;
|
|
wantedBy = [ "sysinit.target" ];
|
|
before = [
|
|
"sysinit.target"
|
|
"shutdown.target"
|
|
"nix-daemon.socket"
|
|
"nix-daemon.service"
|
|
];
|
|
after = [ "local-fs.target" ];
|
|
conflicts = [ "shutdown.target" ];
|
|
restartIfChanged = false;
|
|
serviceConfig = {
|
|
Type = "oneshot";
|
|
RemainAfterExit = true;
|
|
User = lib.mkIf (config.nix.daemonUser != "root") config.nix.daemonUser;
|
|
Group = lib.mkIf (config.nix.daemonGroup != "root") config.nix.daemonGroup;
|
|
};
|
|
script = ''
|
|
if [[ "$(cat /proc/cmdline)" =~ regInfo=([^ ]*) ]]; then
|
|
${lib.getExe' config.nix.package.out "nix-store"} --load-db < "''${BASH_REMATCH[1]}"
|
|
fi
|
|
'';
|
|
};
|
|
|
|
virtualisation.additionalPaths = [ config.system.build.toplevel ];
|
|
|
|
# Read-only erofs store, overlaid when writable. Override per entry: `mkVMOverride` on
|
|
# the whole set would drop other modules' filesystems, including the Rosetta share.
|
|
fileSystems = {
|
|
"/nix/.ro-store" = lib.mkVMOverride {
|
|
device = "/dev/disk/by-label/nix-store";
|
|
fsType = "erofs";
|
|
neededForBoot = true;
|
|
options = [ "ro" ];
|
|
};
|
|
"/nix/store" = lib.mkVMOverride (
|
|
if cfg.writableStore then
|
|
{
|
|
overlay = {
|
|
lowerdir = [ "/nix/.ro-store" ];
|
|
upperdir = "/nix/.rw-store/upper";
|
|
workdir = "/nix/.rw-store/work";
|
|
};
|
|
}
|
|
else
|
|
{
|
|
device = "/nix/.ro-store";
|
|
fsType = "none";
|
|
options = [ "bind" ];
|
|
}
|
|
);
|
|
"/nix/.rw-store" = lib.mkIf (cfg.writableStore && cfg.writableStoreUseTmpfs) (
|
|
lib.mkVMOverride {
|
|
fsType = "tmpfs";
|
|
options = [ "mode=0755" ];
|
|
neededForBoot = true;
|
|
}
|
|
);
|
|
};
|
|
|
|
swapDevices = lib.mkVMOverride [ ];
|
|
boot.initrd.luks.devices = lib.mkVMOverride { };
|
|
|
|
# The host keeps time for us.
|
|
services.timesyncd.enable = false;
|
|
};
|
|
}
|