1 { config, lib, pkgs, ... }:
6 cfg = config.systemd.tmpfiles;
7 initrdCfg = config.boot.initrd.systemd.tmpfiles;
8 systemd = config.systemd.package;
10 attrsWith' = placeholder: elemType: types.attrsWith {
11 inherit elemType placeholder;
16 Declare systemd-tmpfiles rules to create, delete, and clean up volatile
17 and temporary files and directories.
19 Even though the service is called `*tmp*files` you can also create
24 "/var/lib/my-service/statefolder".d = {
32 type = attrsWith' "config-name" (attrsWith' "tmpfiles-type" (attrsWith' "path" (types.submodule ({ name, config, ... }: {
33 options.type = mkOption {
38 The type of operation to perform on the file.
40 The type consists of a single letter and optionally one or more
43 Please see the upstream documentation for the available types and
45 <https://www.freedesktop.org/software/systemd/man/tmpfiles.d>
48 options.mode = mkOption {
53 The file access mode to use when creating this file or directory.
56 options.user = mkOption {
63 This may either be a numeric ID or a user/group name.
65 If omitted or when set to `"-"`, the user and group of the user who
66 invokes systemd-tmpfiles is used.
69 options.group = mkOption {
74 The group of the file.
76 This may either be a numeric ID or a user/group name.
78 If omitted or when set to `"-"`, the user and group of the user who
79 invokes systemd-tmpfiles is used.
82 options.age = mkOption {
87 Delete a file when it reaches a certain age.
89 If a file or directory is older than the current time minus the age
92 If set to `"-"` no automatic clean-up is done.
95 options.argument = mkOption {
100 An argument whose meaning depends on the type of operation.
102 Please see the upstream documentation for the meaning of this
103 parameter in different situations:
104 <https://www.freedesktop.org/software/systemd/man/tmpfiles.d>
110 # generates a single entry for a tmpfiles.d rule
111 settingsEntryToRule = path: entry: ''
112 '${entry.type}' '${path}' '${entry.mode}' '${entry.user}' '${entry.group}' '${entry.age}' ${entry.argument}
115 # generates a list of tmpfiles.d rules from the attrs (paths) under tmpfiles.settings.<name>
116 pathsToRules = mapAttrsToList (path: types:
118 mapAttrsToList (_type: settingsEntryToRule path) types
122 mkRuleFileContent = paths: concatStrings (pathsToRules paths);
126 systemd.tmpfiles.rules = mkOption {
127 type = types.listOf types.str;
129 example = [ "d /tmp 1777 root root 10d" ];
131 Rules for creation, deletion and cleaning of volatile and temporary files
133 {manpage}`tmpfiles.d(5)`
134 for the exact format.
138 systemd.tmpfiles.settings = mkOption settingsOption;
140 boot.initrd.systemd.tmpfiles.settings = mkOption (settingsOption // {
142 Similar to {option}`systemd.tmpfiles.settings` but the rules are
143 only applied by systemd-tmpfiles before `initrd-switch-root.target`.
145 See {manpage}`bootup(7)`.
149 systemd.tmpfiles.packages = mkOption {
150 type = types.listOf types.package;
152 example = literalExpression "[ pkgs.lvm2 ]";
155 List of packages containing {command}`systemd-tmpfiles` rules.
157 All files ending in .conf found in
158 {file}`«pkg»/lib/tmpfiles.d`
160 If this folder does not exist or does not contain any files an error will be returned instead.
162 If a {file}`lib` output is available, rules are searched there and only there.
163 If there is no {file}`lib` output it will fall back to {file}`out`
164 and if that does not exist either, the default output will be used.
172 paths = lib.filter (path:
173 path != null && lib.hasPrefix "/etc/tmpfiles.d/" path
174 ) (map (path: path.target) config.boot.initrd.systemd.storePaths);
176 lib.optional (lib.length paths > 0) (lib.concatStringsSep " " [
177 "Files inside /etc/tmpfiles.d in the initrd need to be created with"
178 "boot.initrd.systemd.tmpfiles.settings."
179 "Creating them by hand using boot.initrd.systemd.contents or"
180 "boot.initrd.systemd.storePaths will lead to errors in the future."
181 "Found these problematic files: ${lib.concatStringsSep ", " paths}"
184 systemd.additionalUpstreamSystemUnits = [
185 "systemd-tmpfiles-clean.service"
186 "systemd-tmpfiles-clean.timer"
187 "systemd-tmpfiles-setup-dev-early.service"
188 "systemd-tmpfiles-setup-dev.service"
189 "systemd-tmpfiles-setup.service"
192 systemd.additionalUpstreamUserUnits = [
193 "systemd-tmpfiles-clean.service"
194 "systemd-tmpfiles-clean.timer"
195 "systemd-tmpfiles-setup.service"
198 # Allow systemd-tmpfiles to be restarted by switch-to-configuration. This
199 # service is not pulled into the normal boot process. It only exists for
200 # switch-to-configuration.
202 # This needs to be a separate unit because it does not execute
203 # systemd-tmpfiles with `--boot` as that is supposed to only be executed
206 # Keep this aligned with the upstream `systemd-tmpfiles-setup.service` unit.
207 systemd.services."systemd-tmpfiles-resetup" = {
208 description = "Re-setup tmpfiles on a system that is already running.";
210 requiredBy = [ "sysinit-reactivation.target" ];
211 after = [ "local-fs.target" "systemd-sysusers.service" "systemd-journald.service" ];
212 before = [ "sysinit-reactivation.target" "shutdown.target" ];
213 conflicts = [ "shutdown.target" ];
214 restartTriggers = [ config.environment.etc."tmpfiles.d".source ];
216 unitConfig.DefaultDependencies = false;
220 RemainAfterExit = true;
221 ExecStart = "systemd-tmpfiles --create --remove --exclude-prefix=/dev";
222 SuccessExitStatus = "DATAERR CANTCREAT";
228 "ssh.authorized_keys.root"
234 "tmpfiles.d".source = (pkgs.symlinkJoin {
236 paths = map (p: p + "/lib/tmpfiles.d") cfg.packages;
238 for i in $(cat $pathsPath); do
239 (test -d "$i" && test $(ls "$i"/*.conf | wc -l) -ge 1) || (
240 echo "ERROR: The path '$i' from systemd.tmpfiles.packages contains no *.conf files."
244 '' + concatMapStrings (name: optionalString (hasPrefix "tmpfiles.d/" name) ''
245 rm -f $out/${removePrefix "tmpfiles.d/" name}
246 '') config.system.build.etc.passthru.targets;
249 mode = "direct-symlink";
250 source = "/proc/mounts";
254 systemd.tmpfiles.packages = [
255 # Default tmpfiles rules provided by systemd
256 (pkgs.runCommand "systemd-default-tmpfiles" {} ''
257 mkdir -p $out/lib/tmpfiles.d
258 cd $out/lib/tmpfiles.d
260 ln -s "${systemd}/example/tmpfiles.d/home.conf"
261 ln -s "${systemd}/example/tmpfiles.d/journal-nocow.conf"
262 ln -s "${systemd}/example/tmpfiles.d/portables.conf"
263 ln -s "${systemd}/example/tmpfiles.d/static-nodes-permissions.conf"
264 ln -s "${systemd}/example/tmpfiles.d/systemd.conf"
265 ln -s "${systemd}/example/tmpfiles.d/systemd-nologin.conf"
266 ln -s "${systemd}/example/tmpfiles.d/systemd-nspawn.conf"
267 ln -s "${systemd}/example/tmpfiles.d/systemd-tmp.conf"
268 ln -s "${systemd}/example/tmpfiles.d/tmp.conf"
269 ln -s "${systemd}/example/tmpfiles.d/var.conf"
270 ln -s "${systemd}/example/tmpfiles.d/x11.conf"
272 # User-specified tmpfiles rules
273 (pkgs.writeTextFile {
274 name = "nixos-tmpfiles.d";
275 destination = "/lib/tmpfiles.d/00-nixos.conf";
277 # This file is created automatically and should not be modified.
278 # Please change the option ‘systemd.tmpfiles.rules’ instead.
280 ${concatStringsSep "\n" cfg.rules}
283 ] ++ (mapAttrsToList (name: paths:
284 pkgs.writeTextDir "lib/tmpfiles.d/${name}.conf" (mkRuleFileContent paths)
287 systemd.tmpfiles.rules = [
288 "d /run/lock 0755 root root - -"
289 "d /var/db 0755 root root - -"
290 "L /var/lock - - - - ../run/lock"
291 ] ++ lib.optionals config.nix.enable [
292 "d /nix/var 0755 root root - -"
293 "L+ /nix/var/nix/gcroots/booted-system 0755 root root - /run/booted-system"
297 "R! /etc/group.lock - - - - -"
298 "R! /etc/passwd.lock - - - - -"
299 "R! /etc/shadow.lock - - - - -"
300 ] ++ lib.optionals config.nix.enable [
301 "R! /nix/var/nix/gcroots/tmp - - - - -"
302 "R! /nix/var/nix/temproots - - - - -"
305 boot.initrd.systemd = {
306 additionalUpstreamUnits = [
307 "systemd-tmpfiles-setup-dev-early.service"
308 "systemd-tmpfiles-setup-dev.service"
309 "systemd-tmpfiles-setup.service"
312 # override to exclude the prefix /sysroot, because it is not necessarily set up when the unit starts
313 services.systemd-tmpfiles-setup.serviceConfig = {
316 "systemd-tmpfiles --create --remove --boot --exclude-prefix=/dev --exclude-prefix=/sysroot"
320 # sets up files under the prefix /sysroot, after the hierarchy is available and before nixos activation
321 services.systemd-tmpfiles-setup-sysroot = {
322 description = "Create Volatile Files and Directories in the Real Root";
323 after = [ "initrd-fs.target" ];
325 "initrd-nixos-activation.service"
326 "shutdown.target" "initrd-switch-root.target"
328 conflicts = [ "shutdown.target" "initrd-switch-root.target" ];
329 wantedBy = [ "initrd.target" ];
332 RemainAfterExit = true;
333 ExecStart = "systemd-tmpfiles --create --remove --boot --exclude-prefix=/dev --prefix=/sysroot";
334 SuccessExitStatus = [ "DATAERR CANTCREAT" ];
340 "ssh.authorized_keys.root"
344 DefaultDependencies = false;
345 RefuseManualStop = true;
350 contents."/etc/tmpfiles.d" = mkIf (initrdCfg.settings != { }) {
351 source = pkgs.linkFarm "initrd-tmpfiles.d" (
354 name = "${name}.conf";
355 path = pkgs.writeText "${name}.conf" (mkRuleFileContent paths);