···397397398398- The `services.trust-dns` module has been renamed to `services.hickory-dns`.
399399400400+- The option `services.prometheus.exporters.pgbouncer.connectionStringFile` has been removed since
401401+ it leaked the connection string (and thus potentially the DB password) into the cmdline
402402+ of process making it effectively world-readable.
403403+404404+ Use [`services.prometheus.exporters.pgbouncer.connectionEnvFile`](#opt-services.prometheus.exporters.pgbouncer.connectionEnvFile) instead.
405405+400406- The `lsh` package and the `services.lshd` module have been removed as they had no maintainer in Nixpkgs and hadn’t seen an upstream release in over a decade. It is recommended to migrate to `openssh` and `services.openssh`.
401407402408- `opencv2` and `opencv3` have been removed, as they are obsolete and
···77 mkPackageOption
88 types
99 optionals
1010- optionalString
1110 getExe
1212- getExe'
1311 escapeShellArg
1414- escapeShellArgs
1512 concatStringsSep
1613 ;
1714in
···2926 };
30273128 connectionString = mkOption {
3232- type = types.str;
3333- default = "";
2929+ type = types.nullOr types.str;
3030+ default = null;
3431 example = "postgres://admin:@localhost:6432/pgbouncer?sslmode=require";
3532 description = ''
3633 Connection string for accessing pgBouncer.
···4340 auth_file if auth_type other than "any" is used.
44414542 WARNING: this secret is stored in the world-readable Nix store!
4646- Use {option}`connectionStringFile` instead.
4343+ Use [](#opt-services.prometheus.exporters.pgbouncer.connectionEnvFile) if the
4444+ URL contains a secret.
4745 '';
4846 };
49475050- connectionStringFile = mkOption {
5151- type = types.nullOr types.path;
4848+ connectionEnvFile = mkOption {
4949+ type = types.nullOr types.str;
5250 default = null;
5353- example = "/run/keys/pgBouncer-connection-string";
5451 description = ''
5555- File that contains pgBouncer connection string in format:
5656- postgres://admin:@localhost:6432/pgbouncer?sslmode=require
5252+ File that must contain the environment variable
5353+ `PGBOUNCER_EXPORTER_CONNECTION_STRING` which is set to the connection
5454+ string used by pgbouncer. I.e. the format is supposed to look like this:
57555858- NOTE: You MUST keep pgbouncer as database name (special internal db)!!!
5656+ ```
5757+ PGBOUNCER_EXPORTER_CONNECTION_STRING="postgres://admin@localhost:6432/pgbouncer?sslmode=require"
5858+ ```
59596060- NOTE: ignore_startup_parameters MUST contain "extra_float_digits".
6161-6262- NOTE: Admin user (with password or passwordless) MUST exist in the
6363- auth_file if auth_type other than "any" is used.
6060+ NOTE: You MUST keep pgbouncer as database name (special internal db)!
6161+ NOTE: `services.pgbouncer.settings.pgbouncer.ignore_startup_parameters`
6262+ MUST contain "extra_float_digits".
64636565- {option}`connectionStringFile` takes precedence over {option}`connectionString`
6464+ Mutually exclusive with [](#opt-services.prometheus.exporters.pgbouncer.connectionString).
6665 '';
6766 };
6867···126125127126 serviceOpts = {
128127 after = [ "pgbouncer.service" ];
129129- script = optionalString (cfg.connectionStringFile != null) ''
130130- connectionString=$(${escapeShellArgs [
131131- (getExe' pkgs.coreutils "cat") "--" cfg.connectionStringFile
132132- ]})
133133- '' + concatStringsSep " " ([
128128+ script = concatStringsSep " " ([
134129 "exec -- ${escapeShellArg (getExe cfg.package)}"
135130 "--web.listen-address ${cfg.listenAddress}:${toString cfg.port}"
136136- "--pgBouncer.connectionString ${if cfg.connectionStringFile != null
137137- then "\"$connectionString\""
138138- else "${escapeShellArg cfg.connectionString}"}"
131131+ ] ++ optionals (cfg.connectionString != null) [
132132+ "--pgBouncer.connectionString ${escapeShellArg cfg.connectionString}"
139133 ] ++ optionals (cfg.telemetryPath != null) [
140134 "--web.telemetry-path ${escapeShellArg cfg.telemetryPath}"
141135 ] ++ optionals (cfg.pidFile != null) [
···151145 ] ++ cfg.extraFlags);
152146153147 serviceConfig.RestrictAddressFamilies = [ "AF_INET" "AF_INET6" "AF_UNIX" ];
148148+ serviceConfig.EnvironmentFile = lib.mkIf (cfg.connectionEnvFile != null) [
149149+ cfg.connectionEnvFile
150150+ ];
154151 };
152152+153153+ imports = [
154154+ (lib.mkRemovedOptionModule [ "connectionStringFile" ] ''
155155+ As replacement, the option `services.prometheus.exporters.pgbouncer.connectionEnvFile`
156156+ has been added. In contrast to `connectionStringFile` it must be an environment file
157157+ with the connection string being set to `PGBOUNCER_EXPORTER_CONNECTION_STRING`.
158158+159159+ The change was necessary since the former option wrote the contents of the file
160160+ into the cmdline of the exporter making the connection string effectively
161161+ world-readable.
162162+ '')
163163+ ({ options.warnings = options.warnings; options.assertions = options.assertions; })
164164+ ];
155165}
···44# `config'. By default, the Nix store is shared read-only with the
55# host, which makes (re)building VMs very efficient.
6677-{
88- config,
99- lib,
1010- pkgs,
1111- options,
1212- ...
1313-}:
77+{ config, lib, pkgs, options, ... }:
148159with lib;
1610···28222923 consoles = lib.concatMapStringsSep " " (c: "console=${c}") cfg.qemu.consoles;
30243131- driveOpts =
3232- { ... }:
3333- {
2525+ driveOpts = { ... }: {
34263535- options = {
2727+ options = {
36283737- file = mkOption {
3838- type = types.str;
3939- description = "The file image used for this drive.";
4040- };
4141-4242- driveExtraOpts = mkOption {
4343- type = types.attrsOf types.str;
4444- default = { };
4545- description = "Extra options passed to drive flag.";
4646- };
2929+ file = mkOption {
3030+ type = types.str;
3131+ description = "The file image used for this drive.";
3232+ };
47334848- deviceExtraOpts = mkOption {
4949- type = types.attrsOf types.str;
5050- default = { };
5151- description = "Extra options passed to device flag.";
5252- };
3434+ driveExtraOpts = mkOption {
3535+ type = types.attrsOf types.str;
3636+ default = {};
3737+ description = "Extra options passed to drive flag.";
3838+ };
53395454- name = mkOption {
5555- type = types.nullOr types.str;
5656- default = null;
5757- description = "A name for the drive. Must be unique in the drives list. Not passed to qemu.";
5858- };
4040+ deviceExtraOpts = mkOption {
4141+ type = types.attrsOf types.str;
4242+ default = {};
4343+ description = "Extra options passed to device flag.";
4444+ };
59454646+ name = mkOption {
4747+ type = types.nullOr types.str;
4848+ default = null;
4949+ description = "A name for the drive. Must be unique in the drives list. Not passed to qemu.";
6050 };
61516252 };
63536464- selectPartitionTableLayout =
6565- { useEFIBoot, useDefaultFilesystems }:
6666- if useDefaultFilesystems then if useEFIBoot then "efi" else "legacy" else "none";
5454+ };
5555+5656+ selectPartitionTableLayout = { useEFIBoot, useDefaultFilesystems }:
5757+ if useDefaultFilesystems then
5858+ if useEFIBoot then "efi" else "legacy"
5959+ else "none";
67606868- driveCmdline =
6969- idx:
7070- {
7171- file,
7272- driveExtraOpts,
7373- deviceExtraOpts,
7474- ...
7575- }:
6161+ driveCmdline = idx: { file, driveExtraOpts, deviceExtraOpts, ... }:
7662 let
7763 drvId = "drive${toString idx}";
7878- mkKeyValue = generators.mkKeyValueDefault { } "=";
6464+ mkKeyValue = generators.mkKeyValueDefault {} "=";
7965 mkOpts = opts: concatStringsSep "," (mapAttrsToList mkKeyValue opts);
8080- driveOpts = mkOpts (
8181- driveExtraOpts
8282- // {
8383- index = idx;
8484- id = drvId;
8585- "if" = "none";
8686- inherit file;
8787- }
8888- );
8989- deviceOpts = mkOpts (
9090- deviceExtraOpts
9191- // {
9292- drive = drvId;
9393- }
9494- );
6666+ driveOpts = mkOpts (driveExtraOpts // {
6767+ index = idx;
6868+ id = drvId;
6969+ "if" = "none";
7070+ inherit file;
7171+ });
7272+ deviceOpts = mkOpts (deviceExtraOpts // {
7373+ drive = drvId;
7474+ });
9575 device =
9676 if cfg.qemu.diskInterface == "scsi" then
9777 "-device lsi53c895a -device scsi-hd,${deviceOpts}"
9878 else
9979 "-device virtio-blk-pci,${deviceOpts}";
10080 in
101101- "-drive ${driveOpts} ${device}";
8181+ "-drive ${driveOpts} ${device}";
1028210383 drivesCmdLine = drives: concatStringsSep "\\\n " (imap1 driveCmdline drives);
1048410585 # Shell script to start the VM.
106106- startVM = ''
107107- #! ${hostPkgs.runtimeShell}
8686+ startVM =
8787+ ''
8888+ #! ${hostPkgs.runtimeShell}
10889109109- export PATH=${makeBinPath [ hostPkgs.coreutils ]}''${PATH:+:}$PATH
110110-111111- set -e
9090+ export PATH=${makeBinPath [ hostPkgs.coreutils ]}''${PATH:+:}$PATH
11291113113- # Create an empty ext4 filesystem image. A filesystem image does not
114114- # contain a partition table but just a filesystem.
115115- createEmptyFilesystemImage() {
116116- local name=$1
117117- local size=$2
118118- local temp=$(mktemp)
119119- ${qemu}/bin/qemu-img create -f raw "$temp" "$size"
120120- ${hostPkgs.e2fsprogs}/bin/mkfs.ext4 -L ${rootFilesystemLabel} "$temp"
121121- ${qemu}/bin/qemu-img convert -f raw -O qcow2 "$temp" "$name"
122122- rm "$temp"
123123- }
9292+ set -e
12493125125- NIX_DISK_IMAGE=$(readlink -f "''${NIX_DISK_IMAGE:-${toString config.virtualisation.diskImage}}") || test -z "$NIX_DISK_IMAGE"
9494+ # Create an empty ext4 filesystem image. A filesystem image does not
9595+ # contain a partition table but just a filesystem.
9696+ createEmptyFilesystemImage() {
9797+ local name=$1
9898+ local size=$2
9999+ local temp=$(mktemp)
100100+ ${qemu}/bin/qemu-img create -f raw "$temp" "$size"
101101+ ${hostPkgs.e2fsprogs}/bin/mkfs.ext4 -L ${rootFilesystemLabel} "$temp"
102102+ ${qemu}/bin/qemu-img convert -f raw -O qcow2 "$temp" "$name"
103103+ rm "$temp"
104104+ }
126105127127- if test -n "$NIX_DISK_IMAGE" && ! test -e "$NIX_DISK_IMAGE"; then
128128- echo "Disk image do not exist, creating the virtualisation disk image..."
106106+ NIX_DISK_IMAGE=$(readlink -f "''${NIX_DISK_IMAGE:-${toString config.virtualisation.diskImage}}") || test -z "$NIX_DISK_IMAGE"
129107130130- ${
131131- if (cfg.useBootLoader && cfg.useDefaultFilesystems) then
132132- ''
133133- # Create a writable qcow2 image using the systemImage as a backing
134134- # image.
108108+ if test -n "$NIX_DISK_IMAGE" && ! test -e "$NIX_DISK_IMAGE"; then
109109+ echo "Disk image do not exist, creating the virtualisation disk image..."
135110136136- # CoW prevent size to be attributed to an image.
137137- # FIXME: raise this issue to upstream.
138138- ${qemu}/bin/qemu-img create \
139139- -f qcow2 \
140140- -b ${systemImage}/nixos.qcow2 \
141141- -F qcow2 \
142142- "$NIX_DISK_IMAGE"
143143- ''
144144- else if cfg.useDefaultFilesystems then
145145- ''
146146- createEmptyFilesystemImage "$NIX_DISK_IMAGE" "${toString cfg.diskSize}M"
147147- ''
148148- else
149149- ''
150150- # Create an empty disk image without a filesystem.
151151- ${qemu}/bin/qemu-img create -f qcow2 "$NIX_DISK_IMAGE" "${toString cfg.diskSize}M"
152152- ''
153153- }
154154- echo "Virtualisation disk image created."
155155- fi
111111+ ${if (cfg.useBootLoader && cfg.useDefaultFilesystems) then ''
112112+ # Create a writable qcow2 image using the systemImage as a backing
113113+ # image.
156114157157- # Create a directory for storing temporary data of the running VM.
158158- if [ -z "$TMPDIR" ] || [ -z "$USE_TMPDIR" ]; then
159159- TMPDIR=$(mktemp -d nix-vm.XXXXXXXXXX --tmpdir)
160160- fi
115115+ # CoW prevent size to be attributed to an image.
116116+ # FIXME: raise this issue to upstream.
117117+ ${qemu}/bin/qemu-img create \
118118+ -f qcow2 \
119119+ -b ${systemImage}/nixos.qcow2 \
120120+ -F qcow2 \
121121+ "$NIX_DISK_IMAGE"
122122+ '' else if cfg.useDefaultFilesystems then ''
123123+ createEmptyFilesystemImage "$NIX_DISK_IMAGE" "${toString cfg.diskSize}M"
124124+ '' else ''
125125+ # Create an empty disk image without a filesystem.
126126+ ${qemu}/bin/qemu-img create -f qcow2 "$NIX_DISK_IMAGE" "${toString cfg.diskSize}M"
127127+ ''
128128+ }
129129+ echo "Virtualisation disk image created."
130130+ fi
161131162162- ${lib.optionalString (cfg.useNixStoreImage) ''
163163- echo "Creating Nix store image..."
132132+ # Create a directory for storing temporary data of the running VM.
133133+ if [ -z "$TMPDIR" ] || [ -z "$USE_TMPDIR" ]; then
134134+ TMPDIR=$(mktemp -d nix-vm.XXXXXXXXXX --tmpdir)
135135+ fi
164136165165- ${hostPkgs.gnutar}/bin/tar --create \
166166- --absolute-names \
167167- --verbatim-files-from \
168168- --transform 'flags=rSh;s|/nix/store/||' \
169169- --files-from ${
170170- hostPkgs.closureInfo {
171171- rootPaths = [
172172- config.system.build.toplevel
173173- regInfo
174174- ];
175175- }
176176- }/store-paths \
177177- | ${hostPkgs.erofs-utils}/bin/mkfs.erofs \
178178- --quiet \
179179- --force-uid=0 \
180180- --force-gid=0 \
181181- -L ${nixStoreFilesystemLabel} \
182182- -U eb176051-bd15-49b7-9e6b-462e0b467019 \
183183- -T 0 \
184184- --tar=f \
185185- "$TMPDIR"/store.img
137137+ ${lib.optionalString (cfg.useNixStoreImage) ''
138138+ echo "Creating Nix store image..."
186139187187- echo "Created Nix store image."
188188- ''}
140140+ ${hostPkgs.gnutar}/bin/tar --create \
141141+ --absolute-names \
142142+ --verbatim-files-from \
143143+ --transform 'flags=rSh;s|/nix/store/||' \
144144+ --files-from ${hostPkgs.closureInfo { rootPaths = [ config.system.build.toplevel regInfo ]; }}/store-paths \
145145+ | ${hostPkgs.erofs-utils}/bin/mkfs.erofs \
146146+ --quiet \
147147+ --force-uid=0 \
148148+ --force-gid=0 \
149149+ -L ${nixStoreFilesystemLabel} \
150150+ -U eb176051-bd15-49b7-9e6b-462e0b467019 \
151151+ -T 0 \
152152+ --tar=f \
153153+ "$TMPDIR"/store.img
189154190190- # Create a directory for exchanging data with the VM.
191191- mkdir -p "$TMPDIR/xchg"
155155+ echo "Created Nix store image."
156156+ ''
157157+ }
192158193193- ${lib.optionalString cfg.useHostCerts ''
194194- mkdir -p "$TMPDIR/certs"
195195- if [ -e "$NIX_SSL_CERT_FILE" ]; then
196196- cp -L "$NIX_SSL_CERT_FILE" "$TMPDIR"/certs/ca-certificates.crt
197197- else
198198- echo \$NIX_SSL_CERT_FILE should point to a valid file if virtualisation.useHostCerts is enabled.
199199- fi
200200- ''}
159159+ # Create a directory for exchanging data with the VM.
160160+ mkdir -p "$TMPDIR/xchg"
201161202202- ${lib.optionalString cfg.useEFIBoot ''
203203- # Expose EFI variables, it's useful even when we are not using a bootloader (!).
204204- # We might be interested in having EFI variable storage present even if we aren't booting via UEFI, hence
205205- # no guard against `useBootLoader`. Examples:
206206- # - testing PXE boot or other EFI applications
207207- # - directbooting LinuxBoot, which `kexec()s` into a UEFI environment that can boot e.g. Windows
208208- NIX_EFI_VARS=$(readlink -f "''${NIX_EFI_VARS:-${config.system.name}-efi-vars.fd}")
209209- # VM needs writable EFI vars
210210- if ! test -e "$NIX_EFI_VARS"; then
211211- ${
212212- if cfg.efi.keepVariables then
213213- # We still need the EFI var from the make-disk-image derivation
214214- # because our "switch-to-configuration" process might
215215- # write into it and we want to keep this data.
216216- ''cp ${systemImage}/efi-vars.fd "$NIX_EFI_VARS"''
162162+ ${lib.optionalString cfg.useHostCerts
163163+ ''
164164+ mkdir -p "$TMPDIR/certs"
165165+ if [ -e "$NIX_SSL_CERT_FILE" ]; then
166166+ cp -L "$NIX_SSL_CERT_FILE" "$TMPDIR"/certs/ca-certificates.crt
217167 else
218218- ''cp ${cfg.efi.variables} "$NIX_EFI_VARS"''
219219- }
220220- chmod 0644 "$NIX_EFI_VARS"
221221- fi
222222- ''}
168168+ echo \$NIX_SSL_CERT_FILE should point to a valid file if virtualisation.useHostCerts is enabled.
169169+ fi
170170+ ''}
223171224224- ${lib.optionalString cfg.tpm.enable ''
225225- NIX_SWTPM_DIR=$(readlink -f "''${NIX_SWTPM_DIR:-${config.system.name}-swtpm}")
226226- mkdir -p "$NIX_SWTPM_DIR"
227227- ${lib.getExe cfg.tpm.package} \
228228- socket \
229229- --tpmstate dir="$NIX_SWTPM_DIR" \
230230- --ctrl type=unixio,path="$NIX_SWTPM_DIR"/socket,terminate \
231231- --pid file="$NIX_SWTPM_DIR"/pid --daemon \
232232- --tpm2 \
233233- --log file="$NIX_SWTPM_DIR"/stdout,level=6
172172+ ${lib.optionalString cfg.useEFIBoot
173173+ ''
174174+ # Expose EFI variables, it's useful even when we are not using a bootloader (!).
175175+ # We might be interested in having EFI variable storage present even if we aren't booting via UEFI, hence
176176+ # no guard against `useBootLoader`. Examples:
177177+ # - testing PXE boot or other EFI applications
178178+ # - directbooting LinuxBoot, which `kexec()s` into a UEFI environment that can boot e.g. Windows
179179+ NIX_EFI_VARS=$(readlink -f "''${NIX_EFI_VARS:-${config.system.name}-efi-vars.fd}")
180180+ # VM needs writable EFI vars
181181+ if ! test -e "$NIX_EFI_VARS"; then
182182+ ${if cfg.efi.keepVariables then
183183+ # We still need the EFI var from the make-disk-image derivation
184184+ # because our "switch-to-configuration" process might
185185+ # write into it and we want to keep this data.
186186+ ''cp ${systemImage}/efi-vars.fd "$NIX_EFI_VARS"''
187187+ else
188188+ ''cp ${cfg.efi.variables} "$NIX_EFI_VARS"''
189189+ }
190190+ chmod 0644 "$NIX_EFI_VARS"
191191+ fi
192192+ ''}
234193235235- # Enable `fdflags` builtin in Bash
236236- # We will need it to perform surgical modification of the file descriptor
237237- # passed in the coprocess to remove `FD_CLOEXEC`, i.e. close the file descriptor
238238- # on exec.
239239- # If let alone, it will trigger the coprocess to read EOF when QEMU is `exec`
240240- # at the end of this script. To work around that, we will just clear
241241- # the `FD_CLOEXEC` bits as a first step.
242242- enable -f ${hostPkgs.bash}/lib/bash/fdflags fdflags
243243- # leave a dangling subprocess because the swtpm ctrl socket has
244244- # "terminate" when the last connection disconnects, it stops swtpm.
245245- # When qemu stops, or if the main shell process ends, the coproc will
246246- # get signaled by virtue of the pipe between main and coproc ending.
247247- # Which in turns triggers a socat connect-disconnect to swtpm which
248248- # will stop it.
249249- coproc waitingswtpm {
250250- read || :
251251- echo "" | ${lib.getExe hostPkgs.socat} STDIO UNIX-CONNECT:"$NIX_SWTPM_DIR"/socket
252252- }
253253- # Clear `FD_CLOEXEC` on the coprocess' file descriptor stdin.
254254- fdflags -s-cloexec ''${waitingswtpm[1]}
255255- ''}
194194+ ${lib.optionalString cfg.tpm.enable ''
195195+ NIX_SWTPM_DIR=$(readlink -f "''${NIX_SWTPM_DIR:-${config.system.name}-swtpm}")
196196+ mkdir -p "$NIX_SWTPM_DIR"
197197+ ${lib.getExe cfg.tpm.package} \
198198+ socket \
199199+ --tpmstate dir="$NIX_SWTPM_DIR" \
200200+ --ctrl type=unixio,path="$NIX_SWTPM_DIR"/socket,terminate \
201201+ --pid file="$NIX_SWTPM_DIR"/pid --daemon \
202202+ --tpm2 \
203203+ --log file="$NIX_SWTPM_DIR"/stdout,level=6
256204257257- cd "$TMPDIR"
205205+ # Enable `fdflags` builtin in Bash
206206+ # We will need it to perform surgical modification of the file descriptor
207207+ # passed in the coprocess to remove `FD_CLOEXEC`, i.e. close the file descriptor
208208+ # on exec.
209209+ # If let alone, it will trigger the coprocess to read EOF when QEMU is `exec`
210210+ # at the end of this script. To work around that, we will just clear
211211+ # the `FD_CLOEXEC` bits as a first step.
212212+ enable -f ${hostPkgs.bash}/lib/bash/fdflags fdflags
213213+ # leave a dangling subprocess because the swtpm ctrl socket has
214214+ # "terminate" when the last connection disconnects, it stops swtpm.
215215+ # When qemu stops, or if the main shell process ends, the coproc will
216216+ # get signaled by virtue of the pipe between main and coproc ending.
217217+ # Which in turns triggers a socat connect-disconnect to swtpm which
218218+ # will stop it.
219219+ coproc waitingswtpm {
220220+ read || :
221221+ echo "" | ${lib.getExe hostPkgs.socat} STDIO UNIX-CONNECT:"$NIX_SWTPM_DIR"/socket
222222+ }
223223+ # Clear `FD_CLOEXEC` on the coprocess' file descriptor stdin.
224224+ fdflags -s-cloexec ''${waitingswtpm[1]}
225225+ ''}
258226259259- ${lib.optionalString (cfg.emptyDiskImages != [ ]) "idx=0"}
260260- ${flip concatMapStrings cfg.emptyDiskImages (size: ''
261261- if ! test -e "empty$idx.qcow2"; then
262262- ${qemu}/bin/qemu-img create -f qcow2 "empty$idx.qcow2" "${toString size}M"
263263- fi
264264- idx=$((idx + 1))
265265- '')}
227227+ cd "$TMPDIR"
228228+229229+ ${lib.optionalString (cfg.emptyDiskImages != []) "idx=0"}
230230+ ${flip concatMapStrings cfg.emptyDiskImages (size: ''
231231+ if ! test -e "empty$idx.qcow2"; then
232232+ ${qemu}/bin/qemu-img create -f qcow2 "empty$idx.qcow2" "${toString size}M"
233233+ fi
234234+ idx=$((idx + 1))
235235+ '')}
236236+237237+ # Start QEMU.
238238+ exec ${qemu-common.qemuBinary qemu} \
239239+ -name ${config.system.name} \
240240+ -m ${toString config.virtualisation.memorySize} \
241241+ -smp ${toString config.virtualisation.cores} \
242242+ -device virtio-rng-pci \
243243+ ${concatStringsSep " " config.virtualisation.qemu.networkingOptions} \
244244+ ${concatStringsSep " \\\n "
245245+ (mapAttrsToList
246246+ (tag: share: "-virtfs local,path=${share.source},security_model=${share.securityModel},mount_tag=${tag}")
247247+ config.virtualisation.sharedDirectories)} \
248248+ ${drivesCmdLine config.virtualisation.qemu.drives} \
249249+ ${concatStringsSep " \\\n " config.virtualisation.qemu.options} \
250250+ $QEMU_OPTS \
251251+ "$@"
252252+ '';
266253267267- # Start QEMU.
268268- exec ${qemu-common.qemuBinary qemu} \
269269- -name ${config.system.name} \
270270- -m ${toString config.virtualisation.memorySize} \
271271- -smp ${toString config.virtualisation.cores} \
272272- -device virtio-rng-pci \
273273- ${concatStringsSep " " config.virtualisation.qemu.networkingOptions} \
274274- ${
275275- concatStringsSep " \\\n " (
276276- mapAttrsToList (
277277- tag: share:
278278- "-virtfs local,path=${share.source},security_model=${share.securityModel},mount_tag=${tag}"
279279- ) config.virtualisation.sharedDirectories
280280- )
281281- } \
282282- ${drivesCmdLine config.virtualisation.qemu.drives} \
283283- ${concatStringsSep " \\\n " config.virtualisation.qemu.options} \
284284- $QEMU_OPTS \
285285- "$@"
286286- '';
287254288255 regInfo = hostPkgs.closureInfo { rootPaths = config.virtualisation.additionalPaths; };
289256···325292 OVMF = cfg.efi.OVMF;
326293 };
327294328328- virtualisationOptions = import ./virtualisation-options.nix;
329329-330295in
331296332297{
333298 imports = [
334334- ./virtualisation-options.nix
335299 ../profiles/qemu-guest.nix
336336- virtualisationOptions.diskSize
337337- (mkRenamedOptionModule
338338- [
339339- "virtualisation"
340340- "pathsInNixDB"
341341- ]
342342- [
343343- "virtualisation"
344344- "additionalPaths"
345345- ]
346346- )
347347- (mkRemovedOptionModule
348348- [
349349- "virtualisation"
350350- "bootDevice"
351351- ]
352352- "This option was renamed to `virtualisation.rootDevice`, as it was incorrectly named and misleading. Take the time to review what you want to do and look at the new options like `virtualisation.{bootLoaderDevice, bootPartition}`, open an issue in case of issues."
353353- )
354354- (mkRemovedOptionModule
355355- [
356356- "virtualisation"
357357- "efiVars"
358358- ]
359359- "This option was removed, it is possible to provide a template UEFI variable with `virtualisation.efi.variables` ; if this option is important to you, open an issue"
360360- )
361361- (mkRemovedOptionModule
362362- [
363363- "virtualisation"
364364- "persistBootDevice"
365365- ]
366366- "Boot device is always persisted if you use a bootloader through the root disk image ; if this does not work for your usecase, please examine carefully what `virtualisation.{bootDevice, rootDevice, bootPartition}` options offer you and open an issue explaining your need.`"
367367- )
300300+ (mkRenamedOptionModule [ "virtualisation" "pathsInNixDB" ] [ "virtualisation" "additionalPaths" ])
301301+ (mkRemovedOptionModule [ "virtualisation" "bootDevice" ] "This option was renamed to `virtualisation.rootDevice`, as it was incorrectly named and misleading. Take the time to review what you want to do and look at the new options like `virtualisation.{bootLoaderDevice, bootPartition}`, open an issue in case of issues.")
302302+ (mkRemovedOptionModule [ "virtualisation" "efiVars" ] "This option was removed, it is possible to provide a template UEFI variable with `virtualisation.efi.variables` ; if this option is important to you, open an issue")
303303+ (mkRemovedOptionModule [ "virtualisation" "persistBootDevice" ] "Boot device is always persisted if you use a bootloader through the root disk image ; if this does not work for your usecase, please examine carefully what `virtualisation.{bootDevice, rootDevice, bootPartition}` options offer you and open an issue explaining your need.`")
368304 ];
369305370306 options = {
371307372308 virtualisation.fileSystems = options.fileSystems;
373309374374- virtualisation.memorySize = mkOption {
375375- type = types.ints.positive;
376376- default = 1024;
377377- description = ''
378378- The memory size in megabytes of the virtual machine.
379379- '';
380380- };
310310+ virtualisation.memorySize =
311311+ mkOption {
312312+ type = types.ints.positive;
313313+ default = 1024;
314314+ description = ''
315315+ The memory size in megabytes of the virtual machine.
316316+ '';
317317+ };
381318382382- virtualisation.msize = mkOption {
383383- type = types.ints.positive;
384384- default = 16384;
385385- description = ''
386386- The msize (maximum packet size) option passed to 9p file systems, in
387387- bytes. Increasing this should increase performance significantly,
388388- at the cost of higher RAM usage.
389389- '';
390390- };
319319+ virtualisation.msize =
320320+ mkOption {
321321+ type = types.ints.positive;
322322+ default = 16384;
323323+ description = ''
324324+ The msize (maximum packet size) option passed to 9p file systems, in
325325+ bytes. Increasing this should increase performance significantly,
326326+ at the cost of higher RAM usage.
327327+ '';
328328+ };
391329392392- virtualisation.diskImage = mkOption {
393393- type = types.nullOr types.str;
394394- default = "./${config.system.name}.qcow2";
395395- defaultText = literalExpression ''"./''${config.system.name}.qcow2"'';
396396- description = ''
397397- Path to the disk image containing the root filesystem.
398398- The image will be created on startup if it does not
399399- exist.
330330+ virtualisation.diskSize =
331331+ mkOption {
332332+ type = types.ints.positive;
333333+ default = 1024;
334334+ description = ''
335335+ The disk size in megabytes of the virtual machine.
336336+ '';
337337+ };
400338401401- If null, a tmpfs will be used as the root filesystem and
402402- the VM's state will not be persistent.
403403- '';
404404- };
339339+ virtualisation.diskImage =
340340+ mkOption {
341341+ type = types.nullOr types.str;
342342+ default = "./${config.system.name}.qcow2";
343343+ defaultText = literalExpression ''"./''${config.system.name}.qcow2"'';
344344+ description = ''
345345+ Path to the disk image containing the root filesystem.
346346+ The image will be created on startup if it does not
347347+ exist.
405348406406- virtualisation.bootLoaderDevice = mkOption {
407407- type = types.path;
408408- default = "/dev/disk/by-id/virtio-${rootDriveSerialAttr}";
409409- defaultText = literalExpression ''/dev/disk/by-id/virtio-${rootDriveSerialAttr}'';
410410- example = "/dev/disk/by-id/virtio-boot-loader-device";
411411- description = ''
412412- The path (inside th VM) to the device to boot from when legacy booting.
413413- '';
414414- };
349349+ If null, a tmpfs will be used as the root filesystem and
350350+ the VM's state will not be persistent.
351351+ '';
352352+ };
415353416416- virtualisation.bootPartition = mkOption {
417417- type = types.nullOr types.path;
418418- default = if cfg.useEFIBoot then "/dev/disk/by-label/${espFilesystemLabel}" else null;
419419- defaultText = literalExpression ''if cfg.useEFIBoot then "/dev/disk/by-label/${espFilesystemLabel}" else null'';
420420- example = "/dev/disk/by-label/esp";
421421- description = ''
422422- The path (inside the VM) to the device containing the EFI System Partition (ESP).
354354+ virtualisation.bootLoaderDevice =
355355+ mkOption {
356356+ type = types.path;
357357+ default = "/dev/disk/by-id/virtio-${rootDriveSerialAttr}";
358358+ defaultText = literalExpression ''/dev/disk/by-id/virtio-${rootDriveSerialAttr}'';
359359+ example = "/dev/disk/by-id/virtio-boot-loader-device";
360360+ description = ''
361361+ The path (inside th VM) to the device to boot from when legacy booting.
362362+ '';
363363+ };
423364424424- If you are *not* booting from a UEFI firmware, this value is, by
425425- default, `null`. The ESP is mounted to `boot.loader.efi.efiSysMountpoint`.
426426- '';
427427- };
365365+ virtualisation.bootPartition =
366366+ mkOption {
367367+ type = types.nullOr types.path;
368368+ default = if cfg.useEFIBoot then "/dev/disk/by-label/${espFilesystemLabel}" else null;
369369+ defaultText = literalExpression ''if cfg.useEFIBoot then "/dev/disk/by-label/${espFilesystemLabel}" else null'';
370370+ example = "/dev/disk/by-label/esp";
371371+ description = ''
372372+ The path (inside the VM) to the device containing the EFI System Partition (ESP).
428373429429- virtualisation.rootDevice = mkOption {
430430- type = types.nullOr types.path;
431431- default = "/dev/disk/by-label/${rootFilesystemLabel}";
432432- defaultText = literalExpression ''/dev/disk/by-label/${rootFilesystemLabel}'';
433433- example = "/dev/disk/by-label/nixos";
434434- description = ''
435435- The path (inside the VM) to the device containing the root filesystem.
436436- '';
437437- };
374374+ If you are *not* booting from a UEFI firmware, this value is, by
375375+ default, `null`. The ESP is mounted to `boot.loader.efi.efiSysMountpoint`.
376376+ '';
377377+ };
438378439439- virtualisation.emptyDiskImages = mkOption {
440440- type = types.listOf types.ints.positive;
441441- default = [ ];
442442- description = ''
443443- Additional disk images to provide to the VM. The value is
444444- a list of size in megabytes of each disk. These disks are
445445- writeable by the VM.
446446- '';
447447- };
379379+ virtualisation.rootDevice =
380380+ mkOption {
381381+ type = types.nullOr types.path;
382382+ default = "/dev/disk/by-label/${rootFilesystemLabel}";
383383+ defaultText = literalExpression ''/dev/disk/by-label/${rootFilesystemLabel}'';
384384+ example = "/dev/disk/by-label/nixos";
385385+ description = ''
386386+ The path (inside the VM) to the device containing the root filesystem.
387387+ '';
388388+ };
448389449449- virtualisation.graphics = mkOption {
450450- type = types.bool;
451451- default = true;
452452- description = ''
453453- Whether to run QEMU with a graphics window, or in nographic mode.
454454- Serial console will be enabled on both settings, but this will
455455- change the preferred console.
456456- '';
457457- };
390390+ virtualisation.emptyDiskImages =
391391+ mkOption {
392392+ type = types.listOf types.ints.positive;
393393+ default = [];
394394+ description = ''
395395+ Additional disk images to provide to the VM. The value is
396396+ a list of size in megabytes of each disk. These disks are
397397+ writeable by the VM.
398398+ '';
399399+ };
458400459459- virtualisation.resolution = mkOption {
460460- type = options.services.xserver.resolutions.type.nestedTypes.elemType;
461461- default = {
462462- x = 1024;
463463- y = 768;
401401+ virtualisation.graphics =
402402+ mkOption {
403403+ type = types.bool;
404404+ default = true;
405405+ description = ''
406406+ Whether to run QEMU with a graphics window, or in nographic mode.
407407+ Serial console will be enabled on both settings, but this will
408408+ change the preferred console.
409409+ '';
464410 };
465465- description = ''
466466- The resolution of the virtual machine display.
467467- '';
468468- };
469411470470- virtualisation.cores = mkOption {
471471- type = types.ints.positive;
472472- default = 1;
473473- description = ''
474474- Specify the number of cores the guest is permitted to use.
475475- The number can be higher than the available cores on the
476476- host system.
477477- '';
478478- };
412412+ virtualisation.resolution =
413413+ mkOption {
414414+ type = options.services.xserver.resolutions.type.nestedTypes.elemType;
415415+ default = { x = 1024; y = 768; };
416416+ description = ''
417417+ The resolution of the virtual machine display.
418418+ '';
419419+ };
479420480480- virtualisation.sharedDirectories = mkOption {
481481- type = types.attrsOf (
482482- types.submodule {
483483- options.source = mkOption {
484484- type = types.str;
485485- description = "The path of the directory to share, can be a shell variable";
486486- };
487487- options.target = mkOption {
488488- type = types.path;
489489- description = "The mount point of the directory inside the virtual machine";
490490- };
491491- options.securityModel = mkOption {
492492- type = types.enum [
493493- "passthrough"
494494- "mapped-xattr"
495495- "mapped-file"
496496- "none"
497497- ];
498498- default = "mapped-xattr";
499499- description = ''
500500- The security model to use for this share:
421421+ virtualisation.cores =
422422+ mkOption {
423423+ type = types.ints.positive;
424424+ default = 1;
425425+ description = ''
426426+ Specify the number of cores the guest is permitted to use.
427427+ The number can be higher than the available cores on the
428428+ host system.
429429+ '';
430430+ };
501431502502- - `passthrough`: files are stored using the same credentials as they are created on the guest (this requires QEMU to run as root)
503503- - `mapped-xattr`: some of the file attributes like uid, gid, mode bits and link target are stored as file attributes
504504- - `mapped-file`: the attributes are stored in the hidden .virtfs_metadata directory. Directories exported by this security model cannot interact with other unix tools
505505- - `none`: same as "passthrough" except the sever won't report failures if it fails to set file attributes like ownership
506506- '';
507507- };
508508- }
509509- );
510510- default = { };
511511- example = {
512512- my-share = {
513513- source = "/path/to/be/shared";
514514- target = "/mnt/shared";
432432+ virtualisation.sharedDirectories =
433433+ mkOption {
434434+ type = types.attrsOf
435435+ (types.submodule {
436436+ options.source = mkOption {
437437+ type = types.str;
438438+ description = "The path of the directory to share, can be a shell variable";
439439+ };
440440+ options.target = mkOption {
441441+ type = types.path;
442442+ description = "The mount point of the directory inside the virtual machine";
443443+ };
444444+ options.securityModel = mkOption {
445445+ type = types.enum [ "passthrough" "mapped-xattr" "mapped-file" "none" ];
446446+ default = "mapped-xattr";
447447+ description = ''
448448+ The security model to use for this share:
449449+450450+ - `passthrough`: files are stored using the same credentials as they are created on the guest (this requires QEMU to run as root)
451451+ - `mapped-xattr`: some of the file attributes like uid, gid, mode bits and link target are stored as file attributes
452452+ - `mapped-file`: the attributes are stored in the hidden .virtfs_metadata directory. Directories exported by this security model cannot interact with other unix tools
453453+ - `none`: same as "passthrough" except the sever won't report failures if it fails to set file attributes like ownership
454454+ '';
455455+ };
456456+ });
457457+ default = { };
458458+ example = {
459459+ my-share = { source = "/path/to/be/shared"; target = "/mnt/shared"; };
515460 };
461461+ description = ''
462462+ An attributes set of directories that will be shared with the
463463+ virtual machine using VirtFS (9P filesystem over VirtIO).
464464+ The attribute name will be used as the 9P mount tag.
465465+ '';
516466 };
517517- description = ''
518518- An attributes set of directories that will be shared with the
519519- virtual machine using VirtFS (9P filesystem over VirtIO).
520520- The attribute name will be used as the 9P mount tag.
521521- '';
522522- };
523467524524- virtualisation.additionalPaths = mkOption {
525525- type = types.listOf types.path;
526526- default = [ ];
527527- description = ''
528528- A list of paths whose closure should be made available to
529529- the VM.
468468+ virtualisation.additionalPaths =
469469+ mkOption {
470470+ type = types.listOf types.path;
471471+ default = [];
472472+ description = ''
473473+ A list of paths whose closure should be made available to
474474+ the VM.
530475531531- When 9p is used, the closure is registered in the Nix
532532- database in the VM. All other paths in the host Nix store
533533- appear in the guest Nix store as well, but are considered
534534- garbage (because they are not registered in the Nix
535535- database of the guest).
476476+ When 9p is used, the closure is registered in the Nix
477477+ database in the VM. All other paths in the host Nix store
478478+ appear in the guest Nix store as well, but are considered
479479+ garbage (because they are not registered in the Nix
480480+ database of the guest).
536481537537- When {option}`virtualisation.useNixStoreImage` is
538538- set, the closure is copied to the Nix store image.
539539- '';
540540- };
482482+ When {option}`virtualisation.useNixStoreImage` is
483483+ set, the closure is copied to the Nix store image.
484484+ '';
485485+ };
541486542487 virtualisation.forwardPorts = mkOption {
543543- type = types.listOf (
544544- types.submodule {
488488+ type = types.listOf
489489+ (types.submodule {
545490 options.from = mkOption {
546546- type = types.enum [
547547- "host"
548548- "guest"
549549- ];
491491+ type = types.enum [ "host" "guest" ];
550492 default = "host";
551493 description = ''
552552- Controls the direction in which the ports are mapped:
494494+ Controls the direction in which the ports are mapped:
553495554554- - `"host"` means traffic from the host ports
555555- is forwarded to the given guest port.
556556- - `"guest"` means traffic from the guest ports
557557- is forwarded to the given host port.
558558- '';
496496+ - `"host"` means traffic from the host ports
497497+ is forwarded to the given guest port.
498498+ - `"guest"` means traffic from the guest ports
499499+ is forwarded to the given host port.
500500+ '';
559501 };
560502 options.proto = mkOption {
561561- type = types.enum [
562562- "tcp"
563563- "udp"
564564- ];
503503+ type = types.enum [ "tcp" "udp" ];
565504 default = "tcp";
566505 description = "The protocol to forward.";
567506 };
···583522 type = types.port;
584523 description = "The guest port to be mapped.";
585524 };
586586- }
587587- );
588588- default = [ ];
589589- example = lib.literalExpression ''
525525+ });
526526+ default = [];
527527+ example = lib.literalExpression
528528+ ''
590529 [ # forward local port 2222 -> 22, to ssh into the VM
591530 { from = "host"; host.port = 2222; guest.port = 22; }
592531···596535 host.address = "127.0.0.1"; host.port = 80;
597536 }
598537 ]
599599- '';
538538+ '';
600539 description = ''
601601- When using the SLiRP user networking (default), this option allows to
602602- forward ports to/from the host/guest.
540540+ When using the SLiRP user networking (default), this option allows to
541541+ forward ports to/from the host/guest.
603542604604- ::: {.warning}
605605- If the NixOS firewall on the virtual machine is enabled, you also
606606- have to open the guest ports to enable the traffic between host and
607607- guest.
608608- :::
543543+ ::: {.warning}
544544+ If the NixOS firewall on the virtual machine is enabled, you also
545545+ have to open the guest ports to enable the traffic between host and
546546+ guest.
547547+ :::
609548610610- ::: {.note}
611611- Currently QEMU supports only IPv4 forwarding.
612612- :::
613613- '';
549549+ ::: {.note}
550550+ Currently QEMU supports only IPv4 forwarding.
551551+ :::
552552+ '';
614553 };
615554616616- virtualisation.restrictNetwork = mkOption {
617617- type = types.bool;
618618- default = false;
619619- example = true;
620620- description = ''
621621- If this option is enabled, the guest will be isolated, i.e. it will
622622- not be able to contact the host and no guest IP packets will be
623623- routed over the host to the outside. This option does not affect
624624- any explicitly set forwarding rules.
625625- '';
626626- };
555555+ virtualisation.restrictNetwork =
556556+ mkOption {
557557+ type = types.bool;
558558+ default = false;
559559+ example = true;
560560+ description = ''
561561+ If this option is enabled, the guest will be isolated, i.e. it will
562562+ not be able to contact the host and no guest IP packets will be
563563+ routed over the host to the outside. This option does not affect
564564+ any explicitly set forwarding rules.
565565+ '';
566566+ };
627567628628- virtualisation.vlans = mkOption {
629629- type = types.listOf types.ints.unsigned;
630630- default = if config.virtualisation.interfaces == { } then [ 1 ] else [ ];
631631- defaultText = lib.literalExpression ''if config.virtualisation.interfaces == {} then [ 1 ] else [ ]'';
632632- example = [
633633- 1
634634- 2
635635- ];
636636- description = ''
637637- Virtual networks to which the VM is connected. Each
638638- number «N» in this list causes
639639- the VM to have a virtual Ethernet interface attached to a
640640- separate virtual network on which it will be assigned IP
641641- address
642642- `192.168.«N».«M»`,
643643- where «M» is the index of this VM
644644- in the list of VMs.
645645- '';
646646- };
568568+ virtualisation.vlans =
569569+ mkOption {
570570+ type = types.listOf types.ints.unsigned;
571571+ default = if config.virtualisation.interfaces == {} then [ 1 ] else [ ];
572572+ defaultText = lib.literalExpression ''if config.virtualisation.interfaces == {} then [ 1 ] else [ ]'';
573573+ example = [ 1 2 ];
574574+ description = ''
575575+ Virtual networks to which the VM is connected. Each
576576+ number «N» in this list causes
577577+ the VM to have a virtual Ethernet interface attached to a
578578+ separate virtual network on which it will be assigned IP
579579+ address
580580+ `192.168.«N».«M»`,
581581+ where «M» is the index of this VM
582582+ in the list of VMs.
583583+ '';
584584+ };
647585648586 virtualisation.interfaces = mkOption {
649649- default = { };
587587+ default = {};
650588 example = {
651589 enp1s0.vlan = 1;
652590 };
653591 description = ''
654592 Network interfaces to add to the VM.
655593 '';
656656- type =
657657- with types;
658658- attrsOf (submodule {
659659- options = {
660660- vlan = mkOption {
661661- type = types.ints.unsigned;
662662- description = ''
663663- VLAN to which the network interface is connected.
664664- '';
665665- };
594594+ type = with types; attrsOf (submodule {
595595+ options = {
596596+ vlan = mkOption {
597597+ type = types.ints.unsigned;
598598+ description = ''
599599+ VLAN to which the network interface is connected.
600600+ '';
601601+ };
666602667667- assignIP = mkOption {
668668- type = types.bool;
669669- default = false;
670670- description = ''
671671- Automatically assign an IP address to the network interface using the same scheme as
672672- virtualisation.vlans.
673673- '';
674674- };
603603+ assignIP = mkOption {
604604+ type = types.bool;
605605+ default = false;
606606+ description = ''
607607+ Automatically assign an IP address to the network interface using the same scheme as
608608+ virtualisation.vlans.
609609+ '';
675610 };
676676- });
611611+ };
612612+ });
677613 };
678614679679- virtualisation.writableStore = mkOption {
680680- type = types.bool;
681681- default = cfg.mountHostNixStore;
682682- defaultText = literalExpression "cfg.mountHostNixStore";
683683- description = ''
684684- If enabled, the Nix store in the VM is made writable by
685685- layering an overlay filesystem on top of the host's Nix
686686- store.
615615+ virtualisation.writableStore =
616616+ mkOption {
617617+ type = types.bool;
618618+ default = cfg.mountHostNixStore;
619619+ defaultText = literalExpression "cfg.mountHostNixStore";
620620+ description = ''
621621+ If enabled, the Nix store in the VM is made writable by
622622+ layering an overlay filesystem on top of the host's Nix
623623+ store.
687624688688- By default, this is enabled if you mount a host Nix store.
689689- '';
690690- };
625625+ By default, this is enabled if you mount a host Nix store.
626626+ '';
627627+ };
691628692692- virtualisation.writableStoreUseTmpfs = mkOption {
693693- type = types.bool;
694694- default = true;
695695- description = ''
696696- Use a tmpfs for the writable store instead of writing to the VM's
697697- own filesystem.
698698- '';
699699- };
629629+ virtualisation.writableStoreUseTmpfs =
630630+ mkOption {
631631+ type = types.bool;
632632+ default = true;
633633+ description = ''
634634+ Use a tmpfs for the writable store instead of writing to the VM's
635635+ own filesystem.
636636+ '';
637637+ };
700638701701- networking.primaryIPAddress = mkOption {
702702- type = types.str;
703703- default = "";
704704- internal = true;
705705- description = "Primary IP address used in /etc/hosts.";
706706- };
639639+ networking.primaryIPAddress =
640640+ mkOption {
641641+ type = types.str;
642642+ default = "";
643643+ internal = true;
644644+ description = "Primary IP address used in /etc/hosts.";
645645+ };
707646708708- networking.primaryIPv6Address = mkOption {
709709- type = types.str;
710710- default = "";
711711- internal = true;
712712- description = "Primary IPv6 address used in /etc/hosts.";
713713- };
647647+ networking.primaryIPv6Address =
648648+ mkOption {
649649+ type = types.str;
650650+ default = "";
651651+ internal = true;
652652+ description = "Primary IPv6 address used in /etc/hosts.";
653653+ };
714654715655 virtualisation.host.pkgs = mkOption {
716656 type = options.nixpkgs.pkgs.type;
···726666 };
727667728668 virtualisation.qemu = {
729729- package = mkOption {
730730- type = types.package;
731731- default =
732732- if hostPkgs.stdenv.hostPlatform.qemuArch == pkgs.stdenv.hostPlatform.qemuArch then
733733- hostPkgs.qemu_kvm
734734- else
735735- hostPkgs.qemu;
736736- defaultText = literalExpression "if hostPkgs.stdenv.hostPlatform.qemuArch == pkgs.stdenv.hostPlatform.qemuArch then config.virtualisation.host.pkgs.qemu_kvm else config.virtualisation.host.pkgs.qemu";
737737- example = literalExpression "pkgs.qemu_test";
738738- description = "QEMU package to use.";
739739- };
669669+ package =
670670+ mkOption {
671671+ type = types.package;
672672+ default = if hostPkgs.stdenv.hostPlatform.qemuArch == pkgs.stdenv.hostPlatform.qemuArch then hostPkgs.qemu_kvm else hostPkgs.qemu;
673673+ defaultText = literalExpression "if hostPkgs.stdenv.hostPlatform.qemuArch == pkgs.stdenv.hostPlatform.qemuArch then config.virtualisation.host.pkgs.qemu_kvm else config.virtualisation.host.pkgs.qemu";
674674+ example = literalExpression "pkgs.qemu_test";
675675+ description = "QEMU package to use.";
676676+ };
740677741741- options = mkOption {
742742- type = types.listOf types.str;
743743- default = [ ];
744744- example = [ "-vga std" ];
745745- description = ''
746746- Options passed to QEMU.
747747- See [QEMU User Documentation](https://www.qemu.org/docs/master/system/qemu-manpage) for a complete list.
748748- '';
749749- };
678678+ options =
679679+ mkOption {
680680+ type = types.listOf types.str;
681681+ default = [];
682682+ example = [ "-vga std" ];
683683+ description = ''
684684+ Options passed to QEMU.
685685+ See [QEMU User Documentation](https://www.qemu.org/docs/master/system/qemu-manpage) for a complete list.
686686+ '';
687687+ };
750688751689 consoles = mkOption {
752690 type = types.listOf types.str;
753753- default =
754754- let
755755- consoles = [
756756- "${qemu-common.qemuSerialDevice},115200n8"
757757- "tty0"
758758- ];
759759- in
760760- if cfg.graphics then consoles else reverseList consoles;
691691+ default = let
692692+ consoles = [ "${qemu-common.qemuSerialDevice},115200n8" "tty0" ];
693693+ in if cfg.graphics then consoles else reverseList consoles;
761694 example = [ "console=tty1" ];
762695 description = ''
763696 The output console devices to pass to the kernel command line via the
···770703 '';
771704 };
772705773773- networkingOptions = mkOption {
774774- type = types.listOf types.str;
775775- default = [ ];
776776- example = [
777777- "-net nic,netdev=user.0,model=virtio"
778778- "-netdev user,id=user.0,\${QEMU_NET_OPTS:+,$QEMU_NET_OPTS}"
779779- ];
780780- description = ''
781781- Networking-related command-line options that should be passed to qemu.
782782- The default is to use userspace networking (SLiRP).
783783- See the [QEMU Wiki on Networking](https://wiki.qemu.org/Documentation/Networking) for details.
706706+ networkingOptions =
707707+ mkOption {
708708+ type = types.listOf types.str;
709709+ default = [ ];
710710+ example = [
711711+ "-net nic,netdev=user.0,model=virtio"
712712+ "-netdev user,id=user.0,\${QEMU_NET_OPTS:+,$QEMU_NET_OPTS}"
713713+ ];
714714+ description = ''
715715+ Networking-related command-line options that should be passed to qemu.
716716+ The default is to use userspace networking (SLiRP).
717717+ See the [QEMU Wiki on Networking](https://wiki.qemu.org/Documentation/Networking) for details.
784718785785- If you override this option, be advised to keep
786786- `''${QEMU_NET_OPTS:+,$QEMU_NET_OPTS}` (as seen in the example)
787787- to keep the default runtime behaviour.
788788- '';
789789- };
719719+ If you override this option, be advised to keep
720720+ `''${QEMU_NET_OPTS:+,$QEMU_NET_OPTS}` (as seen in the example)
721721+ to keep the default runtime behaviour.
722722+ '';
723723+ };
790724791791- drives = mkOption {
792792- type = types.listOf (types.submodule driveOpts);
793793- description = "Drives passed to qemu.";
794794- };
725725+ drives =
726726+ mkOption {
727727+ type = types.listOf (types.submodule driveOpts);
728728+ description = "Drives passed to qemu.";
729729+ };
795730796796- diskInterface = mkOption {
797797- type = types.enum [
798798- "virtio"
799799- "scsi"
800800- "ide"
801801- ];
802802- default = "virtio";
803803- example = "scsi";
804804- description = "The interface used for the virtual hard disks.";
805805- };
731731+ diskInterface =
732732+ mkOption {
733733+ type = types.enum [ "virtio" "scsi" "ide" ];
734734+ default = "virtio";
735735+ example = "scsi";
736736+ description = "The interface used for the virtual hard disks.";
737737+ };
738738+739739+ guestAgent.enable =
740740+ mkOption {
741741+ type = types.bool;
742742+ default = true;
743743+ description = ''
744744+ Enable the Qemu guest agent.
745745+ '';
746746+ };
806747807807- guestAgent.enable = mkOption {
748748+ virtioKeyboard =
749749+ mkOption {
750750+ type = types.bool;
751751+ default = true;
752752+ description = ''
753753+ Enable the virtio-keyboard device.
754754+ '';
755755+ };
756756+ };
757757+758758+ virtualisation.useNixStoreImage =
759759+ mkOption {
808760 type = types.bool;
809809- default = true;
761761+ default = false;
810762 description = ''
811811- Enable the Qemu guest agent.
763763+ Build and use a disk image for the Nix store, instead of
764764+ accessing the host's one through 9p.
765765+766766+ For applications which do a lot of reads from the store,
767767+ this can drastically improve performance, but at the cost of
768768+ disk space and image build time.
769769+770770+ The Nix store image is built just-in-time right before the VM is
771771+ started. Because it does not produce another derivation, the image is
772772+ not cached between invocations and never lands in the store or binary
773773+ cache.
774774+775775+ If you want a full disk image with a partition table and a root
776776+ filesystem instead of only a store image, enable
777777+ {option}`virtualisation.useBootLoader` instead.
812778 '';
813779 };
814780815815- virtioKeyboard = mkOption {
781781+ virtualisation.mountHostNixStore =
782782+ mkOption {
816783 type = types.bool;
817817- default = true;
784784+ default = !cfg.useNixStoreImage && !cfg.useBootLoader;
785785+ defaultText = literalExpression "!cfg.useNixStoreImage && !cfg.useBootLoader";
818786 description = ''
819819- Enable the virtio-keyboard device.
787787+ Mount the host Nix store as a 9p mount.
820788 '';
821789 };
822822- };
823790824824- virtualisation.useNixStoreImage = mkOption {
825825- type = types.bool;
826826- default = false;
827827- description = ''
828828- Build and use a disk image for the Nix store, instead of
829829- accessing the host's one through 9p.
791791+ virtualisation.directBoot = {
792792+ enable =
793793+ mkOption {
794794+ type = types.bool;
795795+ default = !cfg.useBootLoader;
796796+ defaultText = "!cfg.useBootLoader";
797797+ description = ''
798798+ If enabled, the virtual machine will boot directly into the kernel instead of through a bootloader.
799799+ Read more about this feature in the [QEMU documentation on Direct Linux Boot](https://qemu-project.gitlab.io/qemu/system/linuxboot.html)
830800831831- For applications which do a lot of reads from the store,
832832- this can drastically improve performance, but at the cost of
833833- disk space and image build time.
801801+ This is enabled by default.
802802+ If you want to test netboot, consider disabling this option.
803803+ Enable a bootloader with {option}`virtualisation.useBootLoader` if you need.
834804835835- The Nix store image is built just-in-time right before the VM is
836836- started. Because it does not produce another derivation, the image is
837837- not cached between invocations and never lands in the store or binary
838838- cache.
805805+ Relevant parameters such as those set in `boot.initrd` and `boot.kernelParams` are also passed to QEMU.
806806+ Additional parameters can be supplied on invocation through the environment variable `$QEMU_KERNEL_PARAMS`.
807807+ They are added to the `-append` option, see [QEMU User Documentation](https://www.qemu.org/docs/master/system/qemu-manpage) for details
808808+ For example, to let QEMU use the parent terminal as the serial console, set `QEMU_KERNEL_PARAMS="console=ttyS0"`.
839809840840- If you want a full disk image with a partition table and a root
841841- filesystem instead of only a store image, enable
842842- {option}`virtualisation.useBootLoader` instead.
843843- '';
844844- };
810810+ This will not (re-)boot correctly into a system that has switched to a different configuration on disk.
811811+ '';
812812+ };
813813+ initrd =
814814+ mkOption {
815815+ type = types.str;
816816+ default = "${config.system.build.initialRamdisk}/${config.system.boot.loader.initrdFile}";
817817+ defaultText = "\${config.system.build.initialRamdisk}/\${config.system.boot.loader.initrdFile}";
818818+ description = ''
819819+ In direct boot situations, you may want to influence the initrd to load
820820+ to use your own customized payload.
845821846846- virtualisation.mountHostNixStore = mkOption {
847847- type = types.bool;
848848- default = !cfg.useNixStoreImage && !cfg.useBootLoader;
849849- defaultText = literalExpression "!cfg.useNixStoreImage && !cfg.useBootLoader";
850850- description = ''
851851- Mount the host Nix store as a 9p mount.
852852- '';
822822+ This is useful if you want to test the netboot image without
823823+ testing the firmware or the loading part.
824824+ '';
825825+ };
853826 };
854827855855- virtualisation.directBoot = {
856856- enable = mkOption {
828828+ virtualisation.useBootLoader =
829829+ mkOption {
857830 type = types.bool;
858858- default = !cfg.useBootLoader;
859859- defaultText = "!cfg.useBootLoader";
831831+ default = false;
860832 description = ''
861861- If enabled, the virtual machine will boot directly into the kernel instead of through a bootloader.
862862- Read more about this feature in the [QEMU documentation on Direct Linux Boot](https://qemu-project.gitlab.io/qemu/system/linuxboot.html)
833833+ Use a boot loader to boot the system.
834834+ This allows, among other things, testing the boot loader.
863835864864- This is enabled by default.
865865- If you want to test netboot, consider disabling this option.
866866- Enable a bootloader with {option}`virtualisation.useBootLoader` if you need.
867867-868868- Relevant parameters such as those set in `boot.initrd` and `boot.kernelParams` are also passed to QEMU.
869869- Additional parameters can be supplied on invocation through the environment variable `$QEMU_KERNEL_PARAMS`.
870870- They are added to the `-append` option, see [QEMU User Documentation](https://www.qemu.org/docs/master/system/qemu-manpage) for details
871871- For example, to let QEMU use the parent terminal as the serial console, set `QEMU_KERNEL_PARAMS="console=ttyS0"`.
836836+ If disabled, the kernel and initrd are directly booted,
837837+ forgoing any bootloader.
872838873873- This will not (re-)boot correctly into a system that has switched to a different configuration on disk.
874874- '';
839839+ Check the documentation on {option}`virtualisation.directBoot.enable` for details.
840840+ '';
875841 };
876876- initrd = mkOption {
877877- type = types.str;
878878- default = "${config.system.build.initialRamdisk}/${config.system.boot.loader.initrdFile}";
879879- defaultText = "\${config.system.build.initialRamdisk}/\${config.system.boot.loader.initrdFile}";
842842+843843+ virtualisation.useEFIBoot =
844844+ mkOption {
845845+ type = types.bool;
846846+ default = false;
880847 description = ''
881881- In direct boot situations, you may want to influence the initrd to load
882882- to use your own customized payload.
883883-884884- This is useful if you want to test the netboot image without
885885- testing the firmware or the loading part.
886886- '';
887887- };
888888- };
889889-890890- virtualisation.useBootLoader = mkOption {
891891- type = types.bool;
892892- default = false;
893893- description = ''
894894- Use a boot loader to boot the system.
895895- This allows, among other things, testing the boot loader.
896896-897897- If disabled, the kernel and initrd are directly booted,
898898- forgoing any bootloader.
899899-900900- Check the documentation on {option}`virtualisation.directBoot.enable` for details.
901901- '';
902902- };
903903-904904- virtualisation.useEFIBoot = mkOption {
905905- type = types.bool;
906906- default = false;
907907- description = ''
908908- If enabled, the virtual machine will provide a EFI boot
909909- manager.
910910- useEFIBoot is ignored if useBootLoader == false.
911911- '';
912912- };
848848+ If enabled, the virtual machine will provide a EFI boot
849849+ manager.
850850+ useEFIBoot is ignored if useBootLoader == false.
851851+ '';
852852+ };
913853914854 virtualisation.efi = {
915855 OVMF = mkOption {
916856 type = types.package;
917917- default =
918918- (pkgs.OVMF.override {
919919- secureBoot = cfg.useSecureBoot;
920920- }).fd;
921921- defaultText = ''
922922- (pkgs.OVMF.override {
923923- secureBoot = cfg.useSecureBoot;
924924- }).fd'';
857857+ default = (pkgs.OVMF.override {
858858+ secureBoot = cfg.useSecureBoot;
859859+ }).fd;
860860+ defaultText = ''(pkgs.OVMF.override {
861861+ secureBoot = cfg.useSecureBoot;
862862+ }).fd'';
925863 description = "OVMF firmware package, defaults to OVMF configured with secure boot if needed.";
926864 };
927865···930868 default = cfg.efi.OVMF.firmware;
931869 defaultText = literalExpression "cfg.efi.OVMF.firmware";
932870 description = ''
933933- Firmware binary for EFI implementation, defaults to OVMF.
934934- '';
871871+ Firmware binary for EFI implementation, defaults to OVMF.
872872+ '';
935873 };
936874937875 variables = mkOption {
···939877 default = cfg.efi.OVMF.variables;
940878 defaultText = literalExpression "cfg.efi.OVMF.variables";
941879 description = ''
942942- Platform-specific flash binary for EFI variables, implementation-dependent to the EFI firmware.
943943- Defaults to OVMF.
944944- '';
880880+ Platform-specific flash binary for EFI variables, implementation-dependent to the EFI firmware.
881881+ Defaults to OVMF.
882882+ '';
945883 };
946884947885 keepVariables = mkOption {
···959897960898 deviceModel = mkOption {
961899 type = types.str;
962962- default = (
963963- {
964964- "i686-linux" = "tpm-tis";
965965- "x86_64-linux" = "tpm-tis";
966966- "ppc64-linux" = "tpm-spapr";
967967- "armv7-linux" = "tpm-tis-device";
968968- "aarch64-linux" = "tpm-tis-device";
969969- }
970970- .${pkgs.stdenv.hostPlatform.system} or (throw "Unsupported system for TPM2 emulation in QEMU")
971971- );
900900+ default = ({
901901+ "i686-linux" = "tpm-tis";
902902+ "x86_64-linux" = "tpm-tis";
903903+ "ppc64-linux" = "tpm-spapr";
904904+ "armv7-linux" = "tpm-tis-device";
905905+ "aarch64-linux" = "tpm-tis-device";
906906+ }.${pkgs.stdenv.hostPlatform.system} or (throw "Unsupported system for TPM2 emulation in QEMU"));
972907 defaultText = ''
973908 Based on the guest platform Linux system:
974909···981916 };
982917 };
983918984984- virtualisation.useDefaultFilesystems = mkOption {
985985- type = types.bool;
986986- default = true;
987987- description = ''
988988- If enabled, the boot disk of the virtual machine will be
989989- formatted and mounted with the default filesystems for
990990- testing. Swap devices and LUKS will be disabled.
919919+ virtualisation.useDefaultFilesystems =
920920+ mkOption {
921921+ type = types.bool;
922922+ default = true;
923923+ description = ''
924924+ If enabled, the boot disk of the virtual machine will be
925925+ formatted and mounted with the default filesystems for
926926+ testing. Swap devices and LUKS will be disabled.
991927992992- If disabled, a root filesystem has to be specified and
993993- formatted (for example in the initial ramdisk).
994994- '';
995995- };
928928+ If disabled, a root filesystem has to be specified and
929929+ formatted (for example in the initial ramdisk).
930930+ '';
931931+ };
996932997997- virtualisation.useSecureBoot = mkOption {
998998- type = types.bool;
999999- default = false;
10001000- description = ''
10011001- Enable Secure Boot support in the EFI firmware.
10021002- '';
10031003- };
933933+ virtualisation.useSecureBoot =
934934+ mkOption {
935935+ type = types.bool;
936936+ default = false;
937937+ description = ''
938938+ Enable Secure Boot support in the EFI firmware.
939939+ '';
940940+ };
100494110051005- virtualisation.bios = mkOption {
10061006- type = types.nullOr types.package;
10071007- default = null;
10081008- description = ''
10091009- An alternate BIOS (such as `qboot`) with which to start the VM.
10101010- Should contain a file named `bios.bin`.
10111011- If `null`, QEMU's builtin SeaBIOS will be used.
10121012- '';
10131013- };
942942+ virtualisation.bios =
943943+ mkOption {
944944+ type = types.nullOr types.package;
945945+ default = null;
946946+ description = ''
947947+ An alternate BIOS (such as `qboot`) with which to start the VM.
948948+ Should contain a file named `bios.bin`.
949949+ If `null`, QEMU's builtin SeaBIOS will be used.
950950+ '';
951951+ };
101495210151015- virtualisation.useHostCerts = mkOption {
10161016- type = types.bool;
10171017- default = false;
10181018- description = ''
10191019- If enabled, when `NIX_SSL_CERT_FILE` is set on the host,
10201020- pass the CA certificates from the host to the VM.
10211021- '';
10221022- };
953953+ virtualisation.useHostCerts =
954954+ mkOption {
955955+ type = types.bool;
956956+ default = false;
957957+ description = ''
958958+ If enabled, when `NIX_SSL_CERT_FILE` is set on the host,
959959+ pass the CA certificates from the host to the VM.
960960+ '';
961961+ };
10239621024963 };
10259641026965 config = {
10279661028967 assertions =
10291029- lib.concatLists (
10301030- lib.flip lib.imap cfg.forwardPorts (
10311031- i: rule: [
10321032- {
10331033- assertion = rule.from == "guest" -> rule.proto == "tcp";
10341034- message = ''
968968+ lib.concatLists (lib.flip lib.imap cfg.forwardPorts (i: rule:
969969+ [
970970+ { assertion = rule.from == "guest" -> rule.proto == "tcp";
971971+ message =
972972+ ''
1035973 Invalid virtualisation.forwardPorts.<entry ${toString i}>.proto:
1036974 Guest forwarding supports only TCP connections.
1037975 '';
10381038- }
10391039- {
10401040- assertion = rule.from == "guest" -> lib.hasPrefix "10.0.2." rule.guest.address;
10411041- message = ''
976976+ }
977977+ { assertion = rule.from == "guest" -> lib.hasPrefix "10.0.2." rule.guest.address;
978978+ message =
979979+ ''
1042980 Invalid virtualisation.forwardPorts.<entry ${toString i}>.guest.address:
1043981 The address must be in the default VLAN (10.0.2.0/24).
1044982 '';
10451045- }
10461046- ]
10471047- )
10481048- )
10491049- ++ [
10501050- {
10511051- assertion = pkgs.stdenv.hostPlatform.is32bit -> cfg.memorySize < 2047;
10521052- message = ''
10531053- virtualisation.memorySize is above 2047, but qemu is only able to allocate 2047MB RAM on 32bit max.
10541054- '';
10551055- }
10561056- {
10571057- assertion =
10581058- cfg.directBoot.enable || cfg.directBoot.initrd == options.virtualisation.directBoot.initrd.default;
10591059- message = ''
10601060- You changed the default of `virtualisation.directBoot.initrd` but you are not
10611061- using QEMU direct boot. This initrd will not be used in your current
10621062- boot configuration.
983983+ }
984984+ ])) ++ [
985985+ { assertion = pkgs.stdenv.hostPlatform.is32bit -> cfg.memorySize < 2047;
986986+ message = ''
987987+ virtualisation.memorySize is above 2047, but qemu is only able to allocate 2047MB RAM on 32bit max.
988988+ '';
989989+ }
990990+ { assertion = cfg.directBoot.enable || cfg.directBoot.initrd == options.virtualisation.directBoot.initrd.default;
991991+ message =
992992+ ''
993993+ You changed the default of `virtualisation.directBoot.initrd` but you are not
994994+ using QEMU direct boot. This initrd will not be used in your current
995995+ boot configuration.
106399610641064- Either do not mutate `virtualisation.directBoot.initrd` or enable direct boot.
997997+ Either do not mutate `virtualisation.directBoot.initrd` or enable direct boot.
106599810661066- If you have a more advanced usecase, please open an issue or a pull request.
10671067- '';
10681068- }
10691069- ];
999999+ If you have a more advanced usecase, please open an issue or a pull request.
10001000+ '';
10011001+ }
10021002+ ];
1070100310711071- warnings = optional (cfg.directBoot.enable && cfg.useBootLoader) ''
10721072- You enabled direct boot and a bootloader, QEMU will not boot your bootloader, rendering
10731073- `useBootLoader` useless. You might want to disable one of those options.
10741074- '';
10041004+ warnings =
10051005+ optional (cfg.directBoot.enable && cfg.useBootLoader)
10061006+ ''
10071007+ You enabled direct boot and a bootloader, QEMU will not boot your bootloader, rendering
10081008+ `useBootLoader` useless. You might want to disable one of those options.
10091009+ '';
1075101010761011 # In UEFI boot, we use a EFI-only partition table layout, thus GRUB will fail when trying to install
10771012 # legacy and UEFI. In order to avoid this, we have to put "nodev" to force UEFI-only installs.
···10891024 # allow `system.build.toplevel' to be included. (If we had a direct
10901025 # reference to ${regInfo} here, then we would get a cyclic
10911026 # dependency.)
10921092- boot.postBootCommands = lib.mkIf config.nix.enable ''
10931093- if [[ "$(cat /proc/cmdline)" =~ regInfo=([^ ]*) ]]; then
10941094- ${config.nix.package.out}/bin/nix-store --load-db < ''${BASH_REMATCH[1]}
10951095- fi
10961096- '';
10271027+ boot.postBootCommands = lib.mkIf config.nix.enable
10281028+ ''
10291029+ if [[ "$(cat /proc/cmdline)" =~ regInfo=([^ ]*) ]]; then
10301030+ ${config.nix.package.out}/bin/nix-store --load-db < ''${BASH_REMATCH[1]}
10311031+ fi
10321032+ '';
1097103310981034 boot.initrd.availableKernelModules =
10991035 optional (cfg.qemu.diskInterface == "scsi") "sym53c8xx"
···1130106611311067 virtualisation.qemu.networkingOptions =
11321068 let
11331133- forwardingOptions = flip concatMapStrings cfg.forwardPorts (
11341134- {
11351135- proto,
11361136- from,
11371137- host,
11381138- guest,
11391139- }:
11401140- if from == "host" then
11411141- "hostfwd=${proto}:${host.address}:${toString host.port}-"
11421142- + "${guest.address}:${toString guest.port},"
11431143- else
11441144- "'guestfwd=${proto}:${guest.address}:${toString guest.port}-"
11451145- + "cmd:${pkgs.netcat}/bin/nc ${host.address} ${toString host.port}',"
11461146- );
10691069+ forwardingOptions = flip concatMapStrings cfg.forwardPorts
10701070+ ({ proto, from, host, guest }:
10711071+ if from == "host"
10721072+ then "hostfwd=${proto}:${host.address}:${toString host.port}-" +
10731073+ "${guest.address}:${toString guest.port},"
10741074+ else "'guestfwd=${proto}:${guest.address}:${toString guest.port}-" +
10751075+ "cmd:${pkgs.netcat}/bin/nc ${host.address} ${toString host.port}',"
10761076+ );
11471077 restrictNetworkOption = lib.optionalString cfg.restrictNetwork "restrict=on,";
11481078 in
11491079 [
···11561086 "-device virtio-keyboard"
11571087 ])
11581088 (mkIf pkgs.stdenv.hostPlatform.isx86 [
11591159- "-usb"
11601160- "-device usb-tablet,bus=usb-bus.0"
10891089+ "-usb" "-device usb-tablet,bus=usb-bus.0"
11611090 ])
11621091 (mkIf pkgs.stdenv.hostPlatform.isAarch [
11631163- "-device virtio-gpu-pci"
11641164- "-device usb-ehci,id=usb0"
11651165- "-device usb-kbd"
11661166- "-device usb-tablet"
10921092+ "-device virtio-gpu-pci" "-device usb-ehci,id=usb0" "-device usb-kbd" "-device usb-tablet"
11671093 ])
11681168- (
11691169- let
11701170- alphaNumericChars = lowerChars ++ upperChars ++ (map toString (range 0 9));
11711171- # Replace all non-alphanumeric characters with underscores
11721172- sanitizeShellIdent =
11731173- s:
11741174- concatMapStrings (c: if builtins.elem c alphaNumericChars then c else "_") (stringToCharacters s);
11751175- in
11761176- mkIf cfg.directBoot.enable [
11771177- "-kernel \${NIXPKGS_QEMU_KERNEL_${sanitizeShellIdent config.system.name}:-${config.system.build.toplevel}/kernel}"
11781178- "-initrd ${cfg.directBoot.initrd}"
11791179- ''-append "$(cat ${config.system.build.toplevel}/kernel-params) init=${config.system.build.toplevel}/init regInfo=${regInfo}/registration ${consoles} $QEMU_KERNEL_PARAMS"''
11801180- ]
11811181- )
10941094+ (let
10951095+ alphaNumericChars = lowerChars ++ upperChars ++ (map toString (range 0 9));
10961096+ # Replace all non-alphanumeric characters with underscores
10971097+ sanitizeShellIdent = s: concatMapStrings (c: if builtins.elem c alphaNumericChars then c else "_") (stringToCharacters s);
10981098+ in mkIf cfg.directBoot.enable [
10991099+ "-kernel \${NIXPKGS_QEMU_KERNEL_${sanitizeShellIdent config.system.name}:-${config.system.build.toplevel}/kernel}"
11001100+ "-initrd ${cfg.directBoot.initrd}"
11011101+ ''-append "$(cat ${config.system.build.toplevel}/kernel-params) init=${config.system.build.toplevel}/init regInfo=${regInfo}/registration ${consoles} $QEMU_KERNEL_PARAMS"''
11021102+ ])
11821103 (mkIf cfg.useEFIBoot [
11831104 "-drive if=pflash,format=raw,unit=0,readonly=on,file=${cfg.efi.firmware}"
11841105 "-drive if=pflash,format=raw,unit=1,readonly=off,file=$NIX_EFI_VARS"
···11951116 "-device ${cfg.tpm.deviceModel},tpmdev=tpm_dev_0"
11961117 ])
11971118 (mkIf (pkgs.stdenv.hostPlatform.isx86 && cfg.efi.OVMF.systemManagementModeRequired) [
11981198- "-machine"
11991199- "q35,smm=on"
12001200- "-global"
12011201- "driver=cfi.pflash01,property=secure,value=on"
11191119+ "-machine" "q35,smm=on"
11201120+ "-global" "driver=cfi.pflash01,property=secure,value=on"
12021121 ])
12031122 ];
1204112312051124 virtualisation.qemu.drives = mkMerge [
12061206- (mkIf (cfg.diskImage != null) [
12071207- {
12081208- name = "root";
12091209- file = ''"$NIX_DISK_IMAGE"'';
12101210- driveExtraOpts.cache = "writeback";
12111211- driveExtraOpts.werror = "report";
12121212- deviceExtraOpts.bootindex = "1";
12131213- deviceExtraOpts.serial = rootDriveSerialAttr;
12141214- }
12151215- ])
12161216- (mkIf cfg.useNixStoreImage [
12171217- {
12181218- name = "nix-store";
12191219- file = ''"$TMPDIR"/store.img'';
12201220- deviceExtraOpts.bootindex = "2";
12211221- driveExtraOpts.format = "raw";
12221222- }
12231223- ])
11251125+ (mkIf (cfg.diskImage != null) [{
11261126+ name = "root";
11271127+ file = ''"$NIX_DISK_IMAGE"'';
11281128+ driveExtraOpts.cache = "writeback";
11291129+ driveExtraOpts.werror = "report";
11301130+ deviceExtraOpts.bootindex = "1";
11311131+ deviceExtraOpts.serial = rootDriveSerialAttr;
11321132+ }])
11331133+ (mkIf cfg.useNixStoreImage [{
11341134+ name = "nix-store";
11351135+ file = ''"$TMPDIR"/store.img'';
11361136+ deviceExtraOpts.bootindex = "2";
11371137+ driveExtraOpts.format = "raw";
11381138+ }])
12241139 (imap0 (idx: _: {
12251140 file = "$(pwd)/empty${toString idx}.qcow2";
12261141 driveExtraOpts.werror = "report";
···12341149 # override by setting `virtualisation.fileSystems = lib.mkForce { };`.
12351150 fileSystems = lib.mkIf (cfg.fileSystems != { }) (mkVMOverride cfg.fileSystems);
1236115112371237- virtualisation.diskSizeAutoSupported = false;
12381238-12391239- virtualisation.fileSystems =
12401240- let
12411241- mkSharedDir = tag: share: {
11521152+ virtualisation.fileSystems = let
11531153+ mkSharedDir = tag: share:
11541154+ {
12421155 name = share.target;
12431156 value.device = tag;
12441157 value.fsType = "9p";
12451158 value.neededForBoot = true;
12461246- value.options = [
12471247- "trans=virtio"
12481248- "version=9p2000.L"
12491249- "msize=${toString cfg.msize}"
12501250- ] ++ lib.optional (tag == "nix-store") "cache=loose";
11591159+ value.options =
11601160+ [ "trans=virtio" "version=9p2000.L" "msize=${toString cfg.msize}" ]
11611161+ ++ lib.optional (tag == "nix-store") "cache=loose";
11621162+ };
11631163+ in lib.mkMerge [
11641164+ (lib.mapAttrs' mkSharedDir cfg.sharedDirectories)
11651165+ {
11661166+ "/" = lib.mkIf cfg.useDefaultFilesystems (if cfg.diskImage == null then {
11671167+ device = "tmpfs";
11681168+ fsType = "tmpfs";
11691169+ } else {
11701170+ device = cfg.rootDevice;
11711171+ fsType = "ext4";
11721172+ });
11731173+ "/tmp" = lib.mkIf config.boot.tmp.useTmpfs {
11741174+ device = "tmpfs";
11751175+ fsType = "tmpfs";
11761176+ neededForBoot = true;
11771177+ # Sync with systemd's tmp.mount;
11781178+ options = [ "mode=1777" "strictatime" "nosuid" "nodev" "size=${toString config.boot.tmp.tmpfsSize}" ];
12511179 };
12521252- in
12531253- lib.mkMerge [
12541254- (lib.mapAttrs' mkSharedDir cfg.sharedDirectories)
12551255- {
12561256- "/" = lib.mkIf cfg.useDefaultFilesystems (
12571257- if cfg.diskImage == null then
12581258- {
12591259- device = "tmpfs";
12601260- fsType = "tmpfs";
12611261- }
12621262- else
12631263- {
12641264- device = cfg.rootDevice;
12651265- fsType = "ext4";
12661266- }
12671267- );
12681268- "/tmp" = lib.mkIf config.boot.tmp.useTmpfs {
12691269- device = "tmpfs";
12701270- fsType = "tmpfs";
12711271- neededForBoot = true;
12721272- # Sync with systemd's tmp.mount;
12731273- options = [
12741274- "mode=1777"
12751275- "strictatime"
12761276- "nosuid"
12771277- "nodev"
12781278- "size=${toString config.boot.tmp.tmpfsSize}"
12791279- ];
11801180+ "/nix/store" = lib.mkIf (cfg.useNixStoreImage || cfg.mountHostNixStore) (if cfg.writableStore then {
11811181+ overlay = {
11821182+ lowerdir = [ "/nix/.ro-store" ];
11831183+ upperdir = "/nix/.rw-store/upper";
11841184+ workdir = "/nix/.rw-store/work";
12801185 };
12811281- "/nix/store" = lib.mkIf (cfg.useNixStoreImage || cfg.mountHostNixStore) (
12821282- if cfg.writableStore then
12831283- {
12841284- overlay = {
12851285- lowerdir = [ "/nix/.ro-store" ];
12861286- upperdir = "/nix/.rw-store/upper";
12871287- workdir = "/nix/.rw-store/work";
12881288- };
12891289- }
12901290- else
12911291- {
12921292- device = "/nix/.ro-store";
12931293- options = [ "bind" ];
12941294- }
12951295- );
12961296- "/nix/.ro-store" = lib.mkIf cfg.useNixStoreImage {
12971297- device = "/dev/disk/by-label/${nixStoreFilesystemLabel}";
12981298- fsType = "erofs";
12991299- neededForBoot = true;
13001300- options = [ "ro" ];
13011301- };
13021302- "/nix/.rw-store" = lib.mkIf (cfg.writableStore && cfg.writableStoreUseTmpfs) {
13031303- fsType = "tmpfs";
13041304- options = [ "mode=0755" ];
13051305- neededForBoot = true;
13061306- };
13071307- "${config.boot.loader.efi.efiSysMountPoint}" =
13081308- lib.mkIf (cfg.useBootLoader && cfg.bootPartition != null)
13091309- {
13101310- device = cfg.bootPartition;
13111311- fsType = "vfat";
13121312- };
13131313- }
13141314- ];
11861186+ } else {
11871187+ device = "/nix/.ro-store";
11881188+ options = [ "bind" ];
11891189+ });
11901190+ "/nix/.ro-store" = lib.mkIf cfg.useNixStoreImage {
11911191+ device = "/dev/disk/by-label/${nixStoreFilesystemLabel}";
11921192+ fsType = "erofs";
11931193+ neededForBoot = true;
11941194+ options = [ "ro" ];
11951195+ };
11961196+ "/nix/.rw-store" = lib.mkIf (cfg.writableStore && cfg.writableStoreUseTmpfs) {
11971197+ fsType = "tmpfs";
11981198+ options = [ "mode=0755" ];
11991199+ neededForBoot = true;
12001200+ };
12011201+ "${config.boot.loader.efi.efiSysMountPoint}" = lib.mkIf (cfg.useBootLoader && cfg.bootPartition != null) {
12021202+ device = cfg.bootPartition;
12031203+ fsType = "vfat";
12041204+ };
12051205+ }
12061206+ ];
1315120713161208 swapDevices = (if cfg.useDefaultFilesystems then mkVMOverride else mkDefault) [ ];
13171317- boot.initrd.luks.devices = (if cfg.useDefaultFilesystems then mkVMOverride else mkDefault) { };
12091209+ boot.initrd.luks.devices = (if cfg.useDefaultFilesystems then mkVMOverride else mkDefault) {};
1318121013191211 # Don't run ntpd in the guest. It should get the correct time from KVM.
13201212 services.timesyncd.enable = false;
1321121313221214 services.qemuGuest.enable = cfg.qemu.guestAgent.enable;
1323121513241324- system.build.vm =
13251325- hostPkgs.runCommand "nixos-vm"
13261326- {
13271327- preferLocalBuild = true;
13281328- meta.mainProgram = "run-${config.system.name}-vm";
13291329- }
13301330- ''
13311331- mkdir -p $out/bin
13321332- ln -s ${config.system.build.toplevel} $out/system
13331333- ln -s ${hostPkgs.writeScript "run-nixos-vm" startVM} $out/bin/run-${config.system.name}-vm
13341334- '';
12161216+ system.build.vm = hostPkgs.runCommand "nixos-vm" {
12171217+ preferLocalBuild = true;
12181218+ meta.mainProgram = "run-${config.system.name}-vm";
12191219+ }
12201220+ ''
12211221+ mkdir -p $out/bin
12221222+ ln -s ${config.system.build.toplevel} $out/system
12231223+ ln -s ${hostPkgs.writeScript "run-nixos-vm" startVM} $out/bin/run-${config.system.name}-vm
12241224+ '';
1335122513361226 # When building a regular system configuration, override whatever
13371227 # video driver the host uses.
13381228 services.xserver.videoDrivers = mkVMOverride [ "modesetting" ];
13391229 services.xserver.defaultDepth = mkVMOverride 0;
13401230 services.xserver.resolutions = mkVMOverride [ cfg.resolution ];
13411341- services.xserver.monitorSection = ''
13421342- # Set a higher refresh rate so that resolutions > 800x600 work.
13431343- HorizSync 30-140
13441344- VertRefresh 50-160
13451345- '';
12311231+ services.xserver.monitorSection =
12321232+ ''
12331233+ # Set a higher refresh rate so that resolutions > 800x600 work.
12341234+ HorizSync 30-140
12351235+ VertRefresh 50-160
12361236+ '';
1346123713471238 # Wireless won't work in the VM.
13481239 networking.wireless.enable = mkVMOverride false;
···1353124413541245 networking.usePredictableInterfaceNames = false;
1355124613561356- system.requiredKernelConfig =
13571357- with config.lib.kernelConfig;
13581358- [
13591359- (isEnabled "VIRTIO_BLK")
12471247+ system.requiredKernelConfig = with config.lib.kernelConfig;
12481248+ [ (isEnabled "VIRTIO_BLK")
13601249 (isEnabled "VIRTIO_PCI")
13611250 (isEnabled "VIRTIO_NET")
13621251 (isEnabled "EXT4_FS")
···13681257 (isYes "NET_CORE")
13691258 (isYes "INET")
13701259 (isYes "NETWORK_FILESYSTEMS")
13711371- ]
13721372- ++ optionals (!cfg.graphics) [
12601260+ ] ++ optionals (!cfg.graphics) [
13731261 (isYes "SERIAL_8250_CONSOLE")
13741262 (isYes "SERIAL_8250")
13751375- ]
13761376- ++ optionals (cfg.writableStore) [
12631263+ ] ++ optionals (cfg.writableStore) [
13771264 (isEnabled "OVERLAY_FS")
13781265 ];
13791266
···11-# This modules declares shared options for virtual machines,
22-# containers and anything else in `virtualisation`.
33-#
44-# This is useful to declare e.g. defaults for
55-# `virtualisation.diskSize` once, while building multiple
66-# different image formats of a NixOS configuration.
77-#
88-# Additional options can be migrated over time from
99-# `modules/virtualisation/qemu-vm.nix` and others.
1010-# Please keep defaults and descriptions here generic
1111-# and independent of i.e. hypervisor-specific notes
1212-# and defaults where.
1313-# Those can be added in the consuming modules where needed.
1414-# needed.
1515-let
1616- _file = ./virtualisation-options.nix;
1717- key = _file;
1818-in
1919-{
2020- diskSize =
2121- { lib, config, ... }:
2222- let
2323- t = lib.types;
2424- in
2525- {
2626- inherit _file key;
2727-2828- options = {
2929- virtualisation.diskSizeAutoSupported = lib.mkOption {
3030- type = t.bool;
3131- default = true;
3232- description = ''
3333- Whether the current image builder or vm runner supports `virtualisation.diskSize = "auto".`
3434- '';
3535- internal = true;
3636- };
3737-3838- virtualisation.diskSize = lib.mkOption {
3939- type = t.either (t.enum [ "auto" ]) t.ints.positive;
4040- default = "auto";
4141- description = ''
4242- The disk size in megabytes of the virtual machine.
4343- '';
4444- };
4545- };
4646-4747- config =
4848- let
4949- inherit (config.virtualisation) diskSize diskSizeAutoSupported;
5050- in
5151- {
5252- assertions = [
5353- {
5454- assertion = diskSize != "auto" || diskSizeAutoSupported;
5555- message = "Setting virtualisation.diskSize to `auto` is not supported by the current image build or vm runner; use an explicit size.";
5656- }
5757- ];
5858- };
5959- };
6060-}