1{
2 config,
3 lib,
4 pkgs,
5 ...
6}:
7
8{
9
10 imports = [ ./etc.nix ];
11
12 config = lib.mkMerge [
13
14 {
15 system.activationScripts.etc = lib.stringAfter [
16 "users"
17 "groups"
18 "specialfs"
19 ] config.system.build.etcActivationCommands;
20 }
21
22 (lib.mkIf config.system.etc.overlay.enable {
23
24 assertions = [
25 {
26 assertion = config.boot.initrd.systemd.enable;
27 message = "`system.etc.overlay.enable` requires `boot.initrd.systemd.enable`";
28 }
29 {
30 assertion =
31 (!config.system.etc.overlay.mutable)
32 -> (config.systemd.sysusers.enable || config.services.userborn.enable);
33 message = "`!system.etc.overlay.mutable` requires `systemd.sysusers.enable` or `services.userborn.enable`";
34 }
35 {
36 assertion =
37 (config.system.switch.enable)
38 -> (lib.versionAtLeast config.boot.kernelPackages.kernel.version "6.6");
39 message = "switchable systems with `system.etc.overlay.enable` require a newer kernel, at least version 6.6";
40 }
41 ];
42
43 boot.initrd.availableKernelModules = [
44 "loop"
45 "erofs"
46 "overlay"
47 ];
48
49 boot.initrd.systemd = {
50 mounts = [
51 {
52 where = "/run/nixos-etc-metadata";
53 what = "/etc-metadata-image";
54 type = "erofs";
55 options = "loop,ro";
56 unitConfig = {
57 # Since this unit depends on the nix store being mounted, it cannot
58 # be a dependency of local-fs.target, because if it did, we'd have
59 # local-fs.target ordered after the nix store mount which would cause
60 # things like network.target to only become active after the nix store
61 # has been mounted.
62 # This breaks for instance setups where sshd needs to be up before
63 # any encrypted disks can be mounted.
64 DefaultDependencies = false;
65 RequiresMountsFor = [
66 "/sysroot/nix/store"
67 ];
68 };
69 requires = [
70 config.boot.initrd.systemd.services.initrd-find-etc.name
71 ];
72 after = [
73 config.boot.initrd.systemd.services.initrd-find-etc.name
74 ];
75 requiredBy = [ "initrd-fs.target" ];
76 before = [ "initrd-fs.target" ];
77 }
78 {
79 where = "/sysroot/etc";
80 what = "overlay";
81 type = "overlay";
82 options = lib.concatStringsSep "," (
83 [
84 "relatime"
85 "redirect_dir=on"
86 "metacopy=on"
87 "lowerdir=/run/nixos-etc-metadata::/etc-basedir"
88 ]
89 ++ lib.optionals config.system.etc.overlay.mutable [
90 "rw"
91 "upperdir=/sysroot/.rw-etc/upper"
92 "workdir=/sysroot/.rw-etc/work"
93 ]
94 ++ lib.optionals (!config.system.etc.overlay.mutable) [
95 "ro"
96 ]
97 );
98 requiredBy = [ "initrd-fs.target" ];
99 before = [ "initrd-fs.target" ];
100 requires =
101 [
102 config.boot.initrd.systemd.services.initrd-find-etc.name
103 ]
104 ++ lib.optionals config.system.etc.overlay.mutable [
105 config.boot.initrd.systemd.services."rw-etc".name
106 ];
107 after =
108 [
109 config.boot.initrd.systemd.services.initrd-find-etc.name
110 ]
111 ++ lib.optionals config.system.etc.overlay.mutable [
112 config.boot.initrd.systemd.services."rw-etc".name
113 ];
114 unitConfig = {
115 RequiresMountsFor = [
116 "/sysroot/nix/store"
117 "/run/nixos-etc-metadata"
118 ];
119 DefaultDependencies = false;
120 };
121 }
122 ];
123 services = lib.mkMerge [
124 (lib.mkIf config.system.etc.overlay.mutable {
125 rw-etc = {
126 requiredBy = [ "initrd-fs.target" ];
127 before = [ "initrd-fs.target" ];
128 unitConfig = {
129 DefaultDependencies = false;
130 RequiresMountsFor = "/sysroot";
131 };
132 serviceConfig = {
133 Type = "oneshot";
134 ExecStart = ''
135 /bin/mkdir -p -m 0755 /sysroot/.rw-etc/upper /sysroot/.rw-etc/work
136 '';
137 };
138 };
139 })
140 {
141 initrd-find-etc = {
142 description = "Find the path to the etc metadata image and based dir";
143 requires = [
144 config.boot.initrd.systemd.services.initrd-find-nixos-closure.name
145 ];
146 after = [
147 config.boot.initrd.systemd.services.initrd-find-nixos-closure.name
148 ];
149 before = [ "shutdown.target" ];
150 conflicts = [ "shutdown.target" ];
151 requiredBy = [ "initrd.target" ];
152 unitConfig = {
153 DefaultDependencies = false;
154 RequiresMountsFor = "/sysroot/nix/store";
155 };
156 serviceConfig = {
157 Type = "oneshot";
158 RemainAfterExit = true;
159 };
160
161 script = # bash
162 ''
163 set -uo pipefail
164
165 closure="$(realpath /nixos-closure)"
166
167 metadata_image="$(${pkgs.chroot-realpath}/bin/chroot-realpath /sysroot "$closure/etc-metadata-image")"
168 ln -s "/sysroot$metadata_image" /etc-metadata-image
169
170 basedir="$(${pkgs.chroot-realpath}/bin/chroot-realpath /sysroot "$closure/etc-basedir")"
171 ln -s "/sysroot$basedir" /etc-basedir
172 '';
173 };
174 }
175 ];
176 };
177
178 })
179
180 ];
181}