The <name?>
parameter in environment.etc
represents the file name/path relative to /etc
. For example, I used to use the following to create a helper script for ClamAV:
environment.etc."clamav/helper.sh" = {
mode = "0555";
text = ''
# Script here...
'';
};
Similarly, I still do use the following to set up networks:
environment.etc."NetworkManager/system-connections/my-network.nmconnection" = {
mode = "0600";
source = ./files/my-network.nmconnection;
};
(I find it more useful to keep files for which don’t need to contain package paths, etc. separate from my NixOS configuration.) By default, environment.etc
creates symlinks to read-only files in the Nix store, you’ll need to use the group
, user
, and mode
attributes if something (like Network Manager) is too picky to deal with this.
But!
I’ve found that environment.etc
is generally not the right tool for this job. You can generally get a lot more mileage out of pkgs.writeTextFile
. For example, these days I create the same ClamAV helper script using the following pattern:
let
clamavUserScanner = pkgs.writeTextFile {
name = "clamav-user-scanner";
executable = true;
destination = "/bin/clamav-user-scanner.sh";
text = ''
# Script here...
'';
in {
# ...
systemd.user.services.clamav-scan-weekly = {
description = "Perform a full scan of the user's home directory for viruses";
serviceConfig = {
Type = "oneshot";
ExecStart = "${clamavUserScanner}/bin/clamav-user-scanner.sh \"%h\"";
};
};
# ...
}
(The reason I use pkgs.writeTextFile
here rather than systemd.user.services.<name?>.script
is that I re-use this file in a couple of different contexts.)
If you need to add a particular user script, then you can create your own package file in an overlay using a similar pattern. This requires a bit more setup; I start by putting something like the following in my configuration.nix
:
{ config, pkgs, ... }:
{
# Add local overlays
#
nixpkgs.overlays = [ (import ./overlays) ];
# ...
}
(I haven’t been able to get nix.nixPath
to work for me, which looks like how you’re supposed to specify an overlay, though honestly I’m still a bit of a noob myself.) Then I have a file /etc/nixos/overlays/default.nix
that looks a bit like this:
self: super:
let
callPackage = super.callPackage;
in {
# ...
my-script = callPackage ./pkgs/my-script { };
# ...
}
And finally a file in /etc/nixos/overlays/pkgs/my-script/default.nix
that looks like this:
{ stdenv, lib, pkgs, ... }:
let
myScript = pkgs.writeTextFile {
name = "my-script";
executable = true;
destination = "/bin/my-script.sh";
text = ''
# Script here...
'';
};
in stdenv.mkDerivation rec {
pname = "my-script";
version = "0.0.1";
# Copy the script defined in the `let` statement above into the final
# derivation.
#
buildInputs = [ myScript ];
builder = pkgs.writeTextFile {
name = "builder.sh";
text = ''
. $stdenv/setup
mkdir -p $out/bin
ln -sf ${myScript}/bin/my-script.sh $out/bin/my-script.sh
'';
};
}
With this setup, I can then add pkgs.my-script
to environment.systemPackages
, just like a normal Nix package.
One nice thing about these last two approaches is that you can reference packages in your scripts without needing to put them into the global environment. For example, if you want to use MozJPEG, you can reference its cjpeg
binary as ${pkgs.mozjpeg}/bin/cjpeg
, and Nix will pull in the package for your script without you having to do anything else. It’s really quite magical.
(As @jonringer mentions, for scripts just for your user, home-manager
is probably a better solution. You can use the same patterns there, however. To create directories with home-manager
, the easiest way I’ve found is to use a hidden, empty file. For example:
xdg.dataFile."foo/.keep".text = "";
This will create the directory ~/.local/share/foo
. Check out home-manager
's documentation for details, but the key attributes to search for are xdg.dataFile
to create things in ~/.local/share
, xdg.configFile
for ~/.config
, and home.file
for a more generic approach. All three of these have semantics similar to pkgs.writeTextFile
.)
As far as creating directories, check out systemd.tmpfiles.rules
(despite its name, it’s about way more than just temporary files). man tmpfiles.d
should get you started.