nixos/envfs: fix compatibility with systemd in initrd

This module disables the activation scripts that would normally create
`/usr/bin/env` and `/bin/sh` (and their parent directories). When
systemd is used in the initrd, the absence of these directories causes
2 problems:
- During switch-root, base_filesystem_create_fd() creates an empty /usr
  directory. Later at stage2, systemd's initialize_runtime() aborts
  booting with "Refusing to run in unsupported environment where /usr/
  is not populated."
- Once /usr/bin is restored (by the fix above), base_filesystem_create_fd()
  creates a symlink /bin -> /usr/bin. systemd-fstab-generator then
  canonicalizes the /bin fstab entry through this symlink, producing a
  duplicate usr-bin.mount.

We create both /sysroot/usr/bin and /sysroot/bin via initrd tmpfiles
to prevent both issues.

Resolves NixOS#462556

Co-authored-by: Lin Yinfeng <lin.yinfeng@outlook.com>
This commit is contained in:
İlkecan Bozdoğan
2026-03-08 22:37:29 +03:00
co-authored by Lin Yinfeng
parent 73bbf0dc60
commit 3a6f7a60ac
4 changed files with 54 additions and 4 deletions
+34
View File
@@ -74,5 +74,39 @@ in
# We no longer need those when using envfs
system.activationScripts.usrbinenv = lib.mkForce "";
system.activationScripts.binsh = lib.mkForce "";
# Disabling the activation scripts above prevents the creation of 2
# directories, which would normally be created just before switch-root at
# stage1. This causes problems when systemd is used in the initrd.
#
# This only affects fresh installations or systems using impermanence/tmpfs
# root, where these directories don't persist from a previous activation.
boot.initrd.systemd.tmpfiles.settings = lib.mkIf config.boot.initrd.systemd.enable {
"50-envfs" = {
# During switch-root, systemd's base_filesystem_create_fd() creates an
# empty /usr. Later at stage2, systemd initialize_runtime() checks
# dir_is_empty("/usr/"), which returns 1 for an empty but existing
# directory causing a fatal boot failure: "Refusing to run in
# unsupported environment where /usr/ is not populated."
"/sysroot/usr/bin" = {
d = {
group = "root";
mode = "0755";
user = "root";
};
};
# During switch-root, base_filesystem_create_fd() creates a symlink
# /bin -> /usr/bin if /bin does not exist. systemd-fstab-generator then
# canonicalizes the /bin fstab entry through this symlink, producing a
# duplicate usr-bin.mount. Create /bin to avoid this behaviour.
"/sysroot/bin" = {
d = {
group = "root";
mode = "0755";
user = "root";
};
};
};
};
};
}
+8 -1
View File
@@ -518,7 +518,14 @@ in
enlightenment = runTest ./enlightenment.nix;
ente = runTest ./ente;
env = runTest ./env.nix;
envfs = runTest ./envfs.nix;
envfs = runTest {
imports = [ ./envfs.nix ];
_module.args.systemdStage1 = false;
};
envfs-systemd-stage-1 = runTest {
imports = [ ./envfs.nix ];
_module.args.systemdStage1 = true;
};
envoy = runTest {
imports = [ ./envoy.nix ];
_module.args.envoyPackage = pkgs.envoy;
+11 -2
View File
@@ -1,4 +1,9 @@
{ lib, pkgs, ... }:
{
pkgs,
systemdStage1 ? false,
...
}:
let
pythonShebang = pkgs.writeScript "python-shebang" ''
#!/usr/bin/python
@@ -12,7 +17,11 @@ let
in
{
name = "envfs";
nodes.machine.services.envfs.enable = true;
nodes.machine = {
services.envfs.enable = true;
boot.initrd.systemd.enable = systemdStage1;
};
testScript = ''
start_all()
+1 -1
View File
@@ -25,7 +25,7 @@ rustPlatform.buildRustPackage (finalAttrs: {
passthru = {
tests = {
envfs = nixosTests.envfs;
inherit (nixosTests) envfs envfs-systemd-stage-1;
};
updateScript = nix-update-script { };