Boot.initrd.systemd.tmpfiles does not create directories when user attribute is set

boot.initrd.systemd.tmpfiles will create directories owned by root as expected when boot.initrd.systemd.tmpfiles.settings.<config-name>.<path>.<tmpfiles-type>.user is not specified in the configuration. However, setting boot.initrd.systemd.tmpfiles.settings....user to my regular user so that the created directory will be owned by that user results in no directory being created.

This creates a directory owned by root:

  boot.initrd.systemd.tmpfiles.settings."persist" = {
    "/sysroot/user/foo".d = {
    };
  };

This creates nothing:

  boot.initrd.systemd.tmpfiles.settings."persist" = {
    "/sysroot/user/foo".d = {
      user = "user";
    };
  };

My user configuration:

  users.users.user = {
    isNormalUser = true;
    extraGroups = [
      "wheel"  # Enable ‘sudo’ for the user.
      "libvirtd"
      "pipewire"
    ];
  };

The tmpfiles.d manpage says this about the user and group fields of its rules:

This field should generally only reference system users/groups, i.e. users/groups that are guaranteed to be resolvable during early boot. If this field references users/groups that only become resolvable during later boot (i.e. after NIS, LDAP or a similar networked directory service become available), execution of the operations declared by the line will likely fail.

According to https://systemd.io/UIDS-GIDS/#notes-on-resolvability-of-user-and-group-names,

Regular users do not need to be resolvable during early boot, it is sufficient if they become resolvable during late boot. Specifically, regular users need to be resolvable at the point in time the nss-user-lookup.target unit is reached.

Since we have the option of specifiying the user with boot.initrd.systemd.tmpfiles.settings.<config-name>.<path>.<tmpfiles-type>.user, I would think that regular users and groups would be resolvable when systemd-tmpfiles runs in the initrd.
In NixOS, are regular users/groups not expected to be resolvable when 'systemd-tmpfiles` runs in the initrd stage of the boot process? Am I doing something wrong here? Any help would be appreciated.

The issue is that the user database is stored on your root filesystem and can depend on the order users were added/removed and users might be created outside of configuration.nix.

If you want there to be certain users within the initrd, you can add them to the initrd using boot.initrd.systemd.users.

Please do not use this option. Sometimes there are services that require being run as a non-root user, like systemd-networkd or sshd, and these are the only reason that this option exists. The option requires the UIDs to be static, and non-root users in stage 1 should generally be avoided in the first place.

There’s really very little reason to have tmpfiles rules like this in the first place. Generally, if it needs a stage 2 UID, it can be set up in stage 2 with regular stage 2 tmpfiles. The only real time where this isn’t the case is when the file needs to be created / owned before activation scripts run, since those run before stage 2’s systemd units start; but this is more of a condemnation of activation scripts and yet another reason you shouldn’t be using activation scripts either. Anything that’s done in activation scripts should generally be done in a stage 2 systemd unit instead, and then that way it can be ordered after other stage 2 units like tmpfiles.

4 Likes

Thank you, ElvishJerricco. I was a little confused about the stage 1 and stage 2 division, and this is the kind of advice I was hoping to get.

Here is the correct working configuration for those interested:

  systemd.tmpfiles.settings."persist" = {
    "/user/foo".d = {
      user = "user";
      group = "users";
    };
  };