nixos: convert boot.postBootCommands that load the nix DB to systemd services (#496852)

This commit is contained in:
Florian Klink
2026-04-06 22:43:07 +00:00
committed by GitHub
8 changed files with 278 additions and 96 deletions
+44 -14
View File
@@ -54,19 +54,49 @@ in
};
# Provide the NixOS/Nixpkgs sources in /etc/nixos. This is required
# for nixos-install.
boot.postBootCommands = lib.mkAfter ''
if ! [ -e /var/lib/nixos/did-channel-init ]; then
echo "unpacking the NixOS/Nixpkgs sources..."
mkdir -p /nix/var/nix/profiles/per-user/root
${config.nix.package.out}/bin/nix-env -p /nix/var/nix/profiles/per-user/root/channels \
-i ${channelSources} --quiet --option build-use-substitutes false \
${lib.optionalString config.boot.initrd.systemd.enable "--option sandbox false"} # There's an issue with pivot_root
mkdir -m 0700 -p /root/.nix-defexpr
ln -s /nix/var/nix/profiles/per-user/root/channels /root/.nix-defexpr/channels
mkdir -m 0755 -p /var/lib/nixos
touch /var/lib/nixos/did-channel-init
fi
'';
# for nixos-install. We use a systemd service rather than
# boot.postBootCommands so that ordering relative to other
# early-boot services (e.g. register-nix-paths in QEMU VMs) is
# explicit.
systemd.services.nix-channel-init = {
description = "Initialize NixOS Channel";
# Run early so the channel is available before regular services.
# nix-env is invoked before nix-daemon.socket is up, so it
# accesses the store directly (we are root).
unitConfig.DefaultDependencies = false;
wantedBy = [ "sysinit.target" ];
before = [
"sysinit.target"
"shutdown.target"
"nix-daemon.socket"
"nix-daemon.service"
];
after = [
"local-fs.target"
# In QEMU VMs the store DB is populated by register-nix-paths.
# On real hardware this unit does not exist and the dependency
# is silently ignored by systemd.
"register-nix-paths.service"
];
conflicts = [ "shutdown.target" ];
restartIfChanged = false;
serviceConfig = {
Type = "oneshot";
RemainAfterExit = true;
};
script = ''
if ! [ -e /var/lib/nixos/did-channel-init ]; then
echo "unpacking the NixOS/Nixpkgs sources..."
mkdir -p /nix/var/nix/profiles/per-user/root
${lib.getExe' config.nix.package.out "nix-env"} -p /nix/var/nix/profiles/per-user/root/channels \
-i ${channelSources} --quiet --option build-use-substitutes false \
${lib.optionalString config.boot.initrd.systemd.enable "--option sandbox false"} # There's an issue with pivot_root
mkdir -m 0700 -p /root/.nix-defexpr
ln -s /nix/var/nix/profiles/per-user/root/channels /root/.nix-defexpr/channels
mkdir -m 0755 -p /var/lib/nixos
touch /var/lib/nixos/did-channel-init
fi
'';
};
};
}
+26 -9
View File
@@ -1055,16 +1055,33 @@ in
}
);
boot.postBootCommands = ''
# After booting, register the contents of the Nix store on the
# CD in the Nix database in the tmpfs.
${config.nix.package.out}/bin/nix-store --load-db < /nix/store/nix-path-registration
systemd.services.register-nix-paths = {
description = "Register Nix Store Paths";
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;
};
script = ''
# After booting, register the contents of the Nix store on the
# CD in the Nix database in the tmpfs.
${lib.getExe' config.nix.package.out "nix-store"} --load-db < /nix/store/nix-path-registration
# nixos-rebuild also requires a "system" profile and an
# /etc/NIXOS tag.
touch /etc/NIXOS
${config.nix.package.out}/bin/nix-env -p /nix/var/nix/profiles/system --set /run/current-system
'';
# nixos-rebuild also requires a "system" profile and an /etc/NIXOS tag.
touch /etc/NIXOS
${lib.getExe' config.nix.package.out "nix-env"} -p /nix/var/nix/profiles/system --set /run/current-system
'';
};
# Add vfat support to the initrd to enable people to copy the
# contents of the CD to a bootable USB stick.
+30 -11
View File
@@ -166,27 +166,46 @@ with lib;
boot.loader.timeout = 10;
systemd.services.register-nix-paths = {
description = "Register Nix Store Paths";
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;
};
script = ''
# After booting, register the contents of the Nix store
# in the Nix database in the tmpfs.
${lib.getExe' config.nix.package "nix-store"} --load-db < /nix/store/nix-path-registration
# nixos-rebuild also requires a "system" profile and an /etc/NIXOS tag.
touch /etc/NIXOS
${lib.getExe' config.nix.package "nix-env"} -p /nix/var/nix/profiles/system --set /run/current-system
'';
};
boot.postBootCommands = ''
# After booting, register the contents of the Nix store
# in the Nix database in the tmpfs.
${config.nix.package}/bin/nix-store --load-db < /nix/store/nix-path-registration
# nixos-rebuild also requires a "system" profile and an
# /etc/NIXOS tag.
touch /etc/NIXOS
${config.nix.package}/bin/nix-env -p /nix/var/nix/profiles/system --set /run/current-system
# Set password for user nixos if specified on cmdline
# Allows using nixos-anywhere in headless environments
for o in $(</proc/cmdline); do
case "$o" in
live.nixos.passwordHash=*)
set -- $(IFS==; echo $o)
${pkgs.gnugrep}/bin/grep -q "root::" /etc/shadow && ${pkgs.shadow}/bin/usermod -p "$2" root
${lib.getExe pkgs.gnugrep} -q "root::" /etc/shadow && ${lib.getExe' pkgs.shadow "usermod"} -p "$2" root
;;
live.nixos.password=*)
set -- $(IFS==; echo $o)
${pkgs.gnugrep}/bin/grep -q "root::" /etc/shadow && echo "root:$2" | ${pkgs.shadow}/bin/chpasswd
${lib.getExe pkgs.gnugrep} -q "root::" /etc/shadow && echo "root:$2" | ${lib.getExe' pkgs.shadow "chpasswd"}
;;
esac
done
+59 -26
View File
@@ -377,39 +377,72 @@ in
}
) { };
boot.postBootCommands =
systemd.services.expand-root-partition = lib.mkIf config.sdImage.expandOnBoot {
description = "Grow the root partition and filesystem to fill the SD card";
unitConfig = {
DefaultDependencies = false;
ConditionPathExists = config.sdImage.nixPathRegistrationFile;
};
wantedBy = [ "sysinit.target" ];
before = [
"sysinit.target"
"shutdown.target"
"register-nix-paths.service"
];
after = [ "local-fs.target" ];
conflicts = [ "shutdown.target" ];
restartIfChanged = false;
serviceConfig = {
Type = "oneshot";
RemainAfterExit = true;
};
script = ''
# Figure out device names for the boot device and root filesystem.
rootPart=$(${lib.getExe' pkgs.util-linux "findmnt"} -n -o SOURCE /)
bootDevice=$(${lib.getExe' pkgs.util-linux "lsblk"} -npo PKNAME $rootPart)
partNum=$(${lib.getExe' pkgs.util-linux "lsblk"} -npo MAJ:MIN $rootPart | ${lib.getExe pkgs.gawk} -F: '{print $2}')
# Resize the root partition and the filesystem to fit the disk
echo ",+," | ${lib.getExe' pkgs.util-linux "sfdisk"} -N$partNum --no-reread $bootDevice
${lib.getExe' pkgs.parted "partprobe"}
${lib.getExe' pkgs.e2fsprogs "resize2fs"} $rootPart
'';
};
systemd.services.register-nix-paths =
let
expandOnBoot = lib.optionalString config.sdImage.expandOnBoot ''
# Figure out device names for the boot device and root filesystem.
rootPart=$(${pkgs.util-linux}/bin/findmnt -n -o SOURCE /)
bootDevice=$(lsblk -npo PKNAME $rootPart)
partNum=$(lsblk -npo MAJ:MIN $rootPart | ${pkgs.gawk}/bin/awk -F: '{print $2}')
# Resize the root partition and the filesystem to fit the disk
echo ",+," | sfdisk -N$partNum --no-reread $bootDevice
${pkgs.parted}/bin/partprobe
${pkgs.e2fsprogs}/bin/resize2fs $rootPart
'';
nixPathRegistrationFile = config.sdImage.nixPathRegistrationFile;
inherit (config.sdImage) nixPathRegistrationFile;
in
''
# On the first boot do some maintenance tasks
if [ -f ${nixPathRegistrationFile} ]; then
set -euo pipefail
set -x
${expandOnBoot}
# Register the contents of the initial Nix store
${config.nix.package.out}/bin/nix-store --load-db < ${nixPathRegistrationFile}
{
description = "Register Nix Store Paths";
unitConfig = {
DefaultDependencies = false;
ConditionPathExists = nixPathRegistrationFile;
};
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;
};
script = ''
${lib.getExe' config.nix.package.out "nix-store"} --load-db < ${nixPathRegistrationFile}
# nixos-rebuild also requires a "system" profile and an /etc/NIXOS tag.
touch /etc/NIXOS
${config.nix.package.out}/bin/nix-env -p /nix/var/nix/profiles/system --set /run/current-system
${lib.getExe' config.nix.package.out "nix-env"} -p /nix/var/nix/profiles/system --set /run/current-system
# Prevents this from running on later boots.
rm -f ${nixPathRegistrationFile}
fi
'';
'';
};
};
}
+32 -10
View File
@@ -1,4 +1,9 @@
{ config, pkgs, ... }:
{
config,
lib,
pkgs,
...
}:
let
inherit (pkgs) writeScript;
@@ -45,17 +50,34 @@ in
};
boot.isContainer = true;
boot.postBootCommands = ''
# After booting, register the contents of the Nix store in the Nix
# database.
if [ -f /nix-path-registration ]; then
${config.nix.package.out}/bin/nix-store --load-db < /nix-path-registration &&
systemd.services.register-nix-paths = {
description = "Register Nix Store Paths";
unitConfig = {
DefaultDependencies = false;
ConditionPathExists = "/nix-path-registration";
};
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;
};
script = ''
${lib.getExe' config.nix.package.out "nix-store"} --load-db < /nix-path-registration
rm /nix-path-registration
fi
# nixos-rebuild also requires a "system" profile
${config.nix.package.out}/bin/nix-env -p /nix/var/nix/profiles/system --set /run/current-system
'';
# nixos-rebuild also requires a "system" profile
${lib.getExe' config.nix.package.out "nix-env"} -p /nix/var/nix/profiles/system --set /run/current-system
'';
};
# Install new init script
system.activationScripts.installInitScript = ''
+26 -9
View File
@@ -31,17 +31,34 @@
{
boot.isContainer = true;
boot.postBootCommands = ''
# After booting, register the contents of the Nix store in the Nix
# database.
if [ -f /nix-path-registration ]; then
${config.nix.package.out}/bin/nix-store --load-db < /nix-path-registration &&
systemd.services.register-nix-paths = {
description = "Register Nix Store Paths";
unitConfig = {
DefaultDependencies = false;
ConditionPathExists = "/nix-path-registration";
};
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;
};
script = ''
${lib.getExe' config.nix.package.out "nix-store"} --load-db < /nix-path-registration
rm /nix-path-registration
fi
# nixos-rebuild also requires a "system" profile
${config.nix.package.out}/bin/nix-env -p /nix/var/nix/profiles/system --set /run/current-system
'';
# nixos-rebuild also requires a "system" profile
${lib.getExe' config.nix.package.out "nix-env"} -p /nix/var/nix/profiles/system --set /run/current-system
'';
};
# supplement 99-ethernet-default-dhcp which excludes veth
systemd.network = lib.mkIf config.networking.useDHCP {
+29 -12
View File
@@ -76,18 +76,6 @@ with lib;
extraCommands = "mkdir -p root etc/systemd/network";
};
boot.postBootCommands = ''
# After booting, register the contents of the Nix store in the Nix
# database.
if [ -f /nix-path-registration ]; then
${config.nix.package.out}/bin/nix-store --load-db < /nix-path-registration &&
rm /nix-path-registration
fi
# nixos-rebuild also requires a "system" profile
${config.nix.package.out}/bin/nix-env -p /nix/var/nix/profiles/system --set /run/current-system
'';
boot = {
isContainer = true;
loader.initScript.enable = true;
@@ -117,6 +105,35 @@ with lib;
};
systemd = {
services.register-nix-paths = {
description = "Register Nix Store Paths";
unitConfig = {
DefaultDependencies = false;
ConditionPathExists = "/nix-path-registration";
};
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;
};
script = ''
${lib.getExe' config.nix.package.out "nix-store"} --load-db < /nix-path-registration
rm /nix-path-registration
# nixos-rebuild also requires a "system" profile
${lib.getExe' config.nix.package.out "nix-env"} -p /nix/var/nix/profiles/system --set /run/current-system
'';
};
mounts = mkIf (!cfg.privileged) [
{
enable = false;
+32 -5
View File
@@ -1236,11 +1236,38 @@ in
# allow `system.build.toplevel' to be included. (If we had a direct
# reference to ${regInfo} here, then we would get a cyclic
# dependency.)
boot.postBootCommands = lib.mkIf config.nix.enable ''
if [[ "$(cat /proc/cmdline)" =~ regInfo=([^ ]*) ]]; then
${config.nix.package.out}/bin/nix-store --load-db < ''${BASH_REMATCH[1]}
fi
'';
systemd.services.register-nix-paths = lib.mkIf config.nix.enable {
# Run early during boot so the nix store DB is populated before any
# service (or test backdoor) tries to use nix commands.
# nix-store --load-db writes to the SQLite DB directly, so it does not
# need the nix-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;
};
script = ''
if [[ "$(cat /proc/cmdline)" =~ regInfo=([^ ]*) ]]; then
${lib.getExe' config.nix.package.out "nix-store"} --load-db < "''${BASH_REMATCH[1]}"
fi
'';
};
boot.initrd.availableKernelModules =
optional (cfg.qemu.diskInterface == "scsi") "sym53c8xx" ++ optional (cfg.tpm.enable) "tpm_tis";