The upstream unit has ConditionPathIsReadWrite=/etc, which is always
false on the read-only overlay, so the previous commit alone requires
users that want persistence to override the conditions themselves before
the generated ID can be written back.
Use ConditionFirstBoot instead, with the empty placeholder
first-boot is "no" and commit stays skipped, but when a writable
file containing "uninitialized" is bind-mounted over /etc/machine-id,
first-boot is "yes" once and commit writes the ID through the bind.
The symlink to /var/lib/nixos/machine-id never persists the ID,
systemd-machine-id-commit.service requires a writable /etc, and
machine_id_commit() does not follow symlinks for its mountpoint check.
So the backing file stays "uninitialized", every boot is
ConditionFirstBoot=yes, and the machine-id is random per boot.
Ship an empty regular file instead, systemd then overlays /run/machine-id
for the session, ConditionFirstBoot is correctly "no", commit is
cleanly condition-skipped, and the file is a usable bind target for
users that want persistence.
Fixes#523878
find-etc verified the init= was inside a NixOS toplevel and bailed otherwise,
failing initrd-find-etc.service. The etc-overlay mounts require it, so for a
non-NixOS init= (e.g. init=/bin/sh) the initrd dropped to emergency mode before
initrd-init could switch-root into the target.
Make find-etc skip silently for a non-NixOS init=, leaving the /etc-basedir and
/etc-metadata-image symlinks uncreated, and gate the etc-metadata mount, the
/sysroot/etc overlay and the rw-etc service on those symlinks with
ConditionPathExists so they skip instead of fail. initrd-init then switch-roots
into the non-NixOS init directly. The NixOS path is unchanged: the symlinks
exist, so the conditions hold.
Extend the systemd-initrd-non-nixos test with a second node enabling
system.nixos-init.enable, so both the bash initrd-nixos-activation path and the
nixos-init path are covered.
Assisted-by: Claude:claude-opus-4-8
Regular (non-symlink) /etc entries with an explicit mode are currently
written to the metadata erofs image as stubs that redirect to a
separate basedir layer. Every open() of such a file makes overlayfs open
two real files (the metadata stub and the basedir target).
composefs-dump supports embedding file content directly via the CONTENT
field (raw size limit LCFS_INLINE_CONTENT_MAX = 5000 bytes). Use it for
files up to 4096 bytes so they are served straight from erofs with a
single underlying open. Files above the threshold keep the existing basedir
redirect.
Basedir entries that are provably inlineable are filtered at eval time
(text-backed entries) so changing a small /etc file no longer rebuilds
etc-lowerdir. Other entries are filtered at build time using the
same size check as the dump generator.
On a minimal config the basedir becomes empty and an open+read+close of
/etc/sudoers drops from ~46k to ~12k kernel instructions (-73%).
The metadata image size is unchanged (content fits in erofs block slack).
This resolves#523878 where /etc/ is readonly and /etc/machine-id
cannot be created. Instead, /etc/machine-id is initialized to
"uninitialized" by systemd-tmpfiles in initrd and persisted in
/var/lib/nixos.
Fix by walking the newly-mounted metadata layer before the overlay is
(re)mounted and removing trusted.overlay.opaque from any upperdir
directory that now has a counterpart in the lowerdir. This turns those
directories back into merged views: user-placed files and individual
whiteouts are preserved, only the blanket hiding of lowerdir content
is undone.
Applied in both the activation script (switch-to-configuration) and
the initrd rw-etc service (boot), since the upperdir persists across
reboots. Uses the new clear-etc-opaque entrypoint of nixos-init so the
initrd stays bash-free.
Fixes#505475
When using nixos-enter (and so also nixos-install) on a system with etc-overlay enabled,
he activation script gets called directly, and there is no systemd running.
This violates a couple of assumptions in the etc-overlay activation script which
assumed that it only ever ran when switching into a new generation and that
the very first /etc would always have been set up by the systemd initrd.
As more and more things are being moved into systemd components (initrd services,
mount units, tmpfiles, etc), I think that it is going to become increasingly
difficult to stay compatible with these tools, but at least for now there is
no real alternative and so we probably want to be able to install systems
with etc-overlay enabled.
The assertion is based on the availability of `move-mount-beneath`, which is only used in re-activation logic for switchable systems. Systems that have `system.switch.enable == false` should be allowed to user older kernels.
After final improvements to the official formatter implementation,
this commit now performs the first treewide reformat of Nix files using it.
This is part of the implementation of RFC 166.
Only "inactive" files are reformatted, meaning only files that
aren't being touched by any PR with activity in the past 2 months.
This is to avoid conflicts for PRs that might soon be merged.
Later we can do a full treewide reformat to get the rest,
which should not cause as many conflicts.
A CI check has already been running for some time to ensure that new and
already-formatted files are formatted, so the files being reformatted here
should also stay formatted.
This commit was automatically created and can be verified using
nix-build https://github.com/infinisil/treewide-nixpkgs-reformat-script/archive/a08b3a4d199c6124ac5b36a889d9099b4383463f.tar.gz \
--argstr baseRev 57b193d8dd
result/bin/apply-formatting $NIXPKGS_PATH
After final improvements to the official formatter implementation,
this commit now performs the first treewide reformat of Nix files using it.
This is part of the implementation of RFC 166.
Only "inactive" files are reformatted, meaning only files that
aren't being touched by any PR with activity in the past 2 months.
This is to avoid conflicts for PRs that might soon be merged.
Later we can do a full treewide reformat to get the rest,
which should not cause as many conflicts.
A CI check has already been running for some time to ensure that new and
already-formatted files are formatted, so the files being reformatted here
should also stay formatted.
This commit was automatically created and can be verified using
nix-build https://github.com/infinisil/treewide-nixpkgs-reformat-script/archive/a08b3a4d199c6124ac5b36a889d9099b4383463f.tar.gz \
--argstr baseRev b32a094368
result/bin/apply-formatting $NIXPKGS_PATH
On recent kernels (> 6.12 ?) we get the following warning otherwise:
`mount: /tmp/nixos-etc-metadata.aHpRhO5sC4: WARNING: source write-protected, mounted read-only.`
Before this change, the hash of the etc metadata image was included in
the mount unit that's responsible for mounting this metadata image in the
initrd.
And because this metadata image changes with every change to the etc
contents, the initrd would be rebuild every time as well.
This can lead to a lot of rebuilds (especially when revision info is
included in /etc/os-release) and all these initrd archives use up a lot of
space on the ESP.
With this change, we instead include a symlink to the metadata image in the
top-level directory, in the same way as we already do for things like init and
prepare-root, and we deduce the store path from the init= kernel parameter,
in the same way as we already do to find the path to init and prepare-root.
Doing so avoids rebuilding the initrd all the time.
This should fix errors where /etc is reported to be busy and thus cannot
be unmounted.
Another solution we can consider if this doesn't work out as we expect
is to forcefully unmount /etc.
I've observed that sometimes the overlay mount unit does not get started
when using wantedBy. requiredBy makes this relationship stricter and if
necessary will restart the initrd-fs.target and thus ensure that when
this target is reached /etc has alredy been mounted. This is in line
with the description of initrd-fs.target in systemd.special:
> Thus, once this target is reached the /sysroot/ hierarchy is fully set up
The activation script that remounts the /etc overlay now handles other
mount points on top of /etc by bind mounting them to the new temporary
/etc overlay and then atomically revealing it.