Nixpkgs' policy as for Systemd prestart setup scripts vs Systemd tmpfiles

Some modules / services in Nixpkgs which go along with a Systemd service, set up certain directories (usually in /var/) for the services to be able to use when running. Usually this is done using PreStart or in Nix:

systemd.services.<name>.prestart = ''
  mkdir /var/lib/this and that
  chmod this to that
''

Sometimes, these scripts force settings / permissions a user wouldn’t necessary be comfortable with. An example of a discussion demonstrating this kind of debate is in https://github.com/NixOS/nixpkgs/pull/76552 .

An idea come up during it which reminded me of the classic and more settled way of doing such a setup: Using systemd tmpfiles which are somewhat supported in Nixpkgs’ options. I’m not yet experienced enough with it but git grep suggests many services use it but that the prestart approach is more common.

Perhaps in order to encourage services maintainers to use tmpfiles, we could improve it by making it more expressive. Perhaps it’s usage may go like this:

systemd.tmpfiles.rules = {
	"/var/lib/servicehome" = {
		type = "d";
		mode = "0775";
		user = "service-daemon";
		group = "admins";
		age = "";
		argument = "/dev/null";
	};
};

This way a user using a service which set up such a tmpfiles directory may override only the relevant part with e.g:

systemd.tmpfiles.rules."/var/lib/servicehome".mode = "0770";

BTW while I read tmpfiles.d(5) I noticed the vast support for all kinds of configurations which a mere script may do less reliably and in a harder to configure way IMO.

I would like to hear your opinions and maybe I’ll raise an RFC in Nixos/rfcs.

1 Like

Dozens upon dozens of services have been converted to use tmpfiles instead of preStart. Ideally you will use StateDirectory and friends, but when that is not possible tmpfiles is (my) preferred method, and I think generally accepted as a good solution.

I like your idea. @arianvp is actually working on something like this already. Maybe he’ll comment.

1 Like

So there is a move towards using a better approach other then preStart scripts. Nice :slight_smile: Yet it could be better if it was done more formally.

I noticed many services use such a configuration option but it seems they just handle the implementation of it themselves using preStart

StateDirectory automatically provisions directories for you, so if there is a service which is using StateDirectory and using mkdir to create the same folder this is likely a mistake. Do you have an example of this?

Yes, see https://github.com/NixOS/nixpkgs/blob/52a4fd27adf02987467b4afb8b2c3675cb13b51a/nixos/modules/services/web-servers/apache-httpd/default.nix#L687 .

See https://github.com/NixOS/nixpkgs/blob/14e842ec021ea18618f893fea8e7ce332f858a4a/nixos/modules/services/web-servers/apache-httpd/default.nix#L713 and https://github.com/NixOS/nixpkgs/blob/14e842ec021ea18618f893fea8e7ce332f858a4a/nixos/modules/services/web-servers/apache-httpd/default.nix#L686 for fixed version :slight_smile:

1 Like

I recently upgraded to 20.03 and found that my gitea setup that works under 19.09 starts to fail. The ExecStartPre of gitea service needs to find the hooks by

HOOKS=$(find ${cfg.repositoryRoot} -mindepth 4 -maxdepth 6 -type f -wholename "*git/hooks/*")

But now it fails with permission denied because my repositoryRoot is under /home/<user>. After some digging I think this commit is what I was looking for: https://github.com/NixOS/nixpkgs/commit/7145cf224c3eb45985d9bfc483c9e13c16fdaa91#diff-ca28bfecf4338f2def6526c29a1ddc36

I still do not know what is the preferred way to do this but I think that is somewhat related to this topic. Should I use tmpfiles.rules to create a symbolic link of the repositoryRoot, or should I just move it out of the user’s home directory?

Thanks in advance for the advice!

I think you may encounter conflicts or perhaps an issue similar to https://github.com/NixOS/nixpkgs/issues/86600

Your use case demonstrates exactly what’s a bit annoying in the current implementation of tmpfiles: you can’t (easily) override only 1 line of a tmpfiles rule, because it’s a list and not an attribute set (for which you could have used a lib.mkForce).

Running gitea with repositories in some (other) users home directory sounds scary. Moving the repository into the default gitea repository location, and having it owned by that user sounds like a much more compatible alternative…

you can’t (easily) override only 1 line of a tmpfiles rule, because it’s a list and not an attribute set (for which you could have used a lib.mkForce ).

Right. I may be wrong but I guess it is still possible to override the original list by taking the original list, filtering out what needs to be removed and appending what is needed, with lib.mkForce or lib.mkOverride … A bit less elegant though.

Thanks for the advice! I did it that way and everything works out fine.
Out of curiosity - I am still not sure the difference between putting the repositories “in the home directory” vs "under /var/lib/gitea", can you help explain? Thanks!

More details: I created a user dedicate for gitea, so the home directory does not have anything else. Main motivation is that my /home has a big SSD mounted …

Can you show us your gitea NixOS configuration, as well as configuration of the “gitea-dedicated user”? That might help me understand your situation better…

Definitely. The gitea configuration was here:

https://git.breakds.org/breakds/nixvital/src/commit/1813281ce61bdf15a9e3a82b4e002a71b37cec29/modules/services/gitea.nix#L63

And the user configuration for git here:

https://git.breakds.org/breakds/nixvital/src/commit/1813281ce61bdf15a9e3a82b4e002a71b37cec29/modules/users/default.nix#L55

Btw, I moved the repo under /var/lib/gitea since then and everything works out perfectly.