Nix Cookbook
Managing storage
Reclaim space on Nix install?
Remove old generations
When you make changes to your system, Nix creates a new system Generation. All of the changes to the system since the previous generation are stored there. Old generations can add up and will not be removed automatically by default. You can see your generations with:
$ nix-env --list-generations
To remove all older than
Garbage collection
As you work with your system (installs, uninstalls, upgrades), files in the Nix store are not automatically removed, even when no longer needed. Nix instead has a garbage collector which must be run periodically (you could set up, e.g., a cron to do this).
$ nix-collect-garbage
This is safe so long as everything you need is listed in an existing generation or garbage collector root (gcroot).
If you are sure you only need your current generation, this will delete all old generations and then do garbage collection:
$ nix-collect-garbage -d
On NixOS, you can enable a service to automatically do daily garbage collection:
/etc/nixos/configuration.nix
nix.gc.automatic = true;
Deduplication
You may wind up with duplicate files in your Nix store. Data deduplication is a resource intense process, so is not done automatically. Often you can save about 25-35% of your store space by optimizing the store though. This will perform a deduplication process on your Nix store (hard link duplicates together):
$ nix optimise-store
You can set the nix.conf
option below to set this to happen periodically:
/etc/nix/nix.conf
auto-optimise-store = true
For NixOS, the equivalent option is:
/etc/nixos/configuration.nix
nix.autoOptimiseStore = true;
Nix manual references
Environment Tasks
Creating Shell Scripts
Arbitrary system shell scripts can be created with pkgs.writeShellScriptBin. It creates a derivation which you add to environment.systemPackages.
{ pkgs, ... }:
let
helloWorld = pkgs.writeShellScriptBin "helloWorld" ''
echo Hello World
'';
in {
environment.systemPackages = [ helloWorld ];
}
Creating Periodic Services
Using the systemd support periodic services can be defined. In this case a service named simple-timer
writes out the current time to /tmp/simple-timer.log
every minute.
{ pkgs, ... }:
{
systemd = {
timers.simple-timer = {
wantedBy = [ "timers.target" ];
partOf = [ "simple-timer.service" ];
timerConfig.OnCalendar = "minutely";
};
services.simple-timer = {
serviceConfig.Type = "oneshot";
script = ''
echo "Time: $(date)." >> /tmp/simple-timer.log
'';
};
};
}
Wrapping packages
If you need to wrap a binary of a package (or a non-binary), there are a few ways of doing it. The simplest of which is just creating a new binary that calls the old one:
pkgs.writeShellScriptBin "hello" ''
# Call hello with a traditional greeting
exec ${pkgs.hello}/bin/hello -t
''
The disadvantage of this way is that it doesn't propagate man pages and other paths from the old derivation. There are multiple ways of solving that:
let
wrapped = pkgs.writeShellScriptBin "hello" ''
exec ${pkgs.hello}/bin/hello -t
'';
in
pkgs.symlinkJoin {
name = "hello";
paths = [
wrapped
pkgs.hello
];
}
Similarly the following works too:
pkgs.symlinkJoin {
name = "hello";
paths = [ pkgs.hello ];
buildInputs = [ pkgs.makeWrapper ];
postBuild = ''
wrapProgram $out/bin/hello \
--add-flags "-t"
'';
}
If you prefer not to have every file symlinked and have a cleaner result, the following is also possible:
pkgs.runCommand "hello" {
buildInputs = [ pkgs.makeWrapper ];
} ''
mkdir $out
# Link every top-level folder from pkgs.hello to our new target
ln -s ${pkgs.hello}/* $out
# Except the bin folder
rm $out/bin
mkdir $out/bin
# We create the bin folder ourselves and link every binary in it
ln -s ${pkgs.hello}/bin/* $out/bin
# Except the hello binary
rm $out/bin/hello
# Because we create this ourself, by creating a wrapper
makeWrapper ${pkgs.hello}/bin/hello $out/bin/hello \
--add-flags "-t"
''
And lastly, there is the possibility of wrapping things right inside the derivation you want to wrap, this is however discouraged and impractical in most cases, as it requires recompilation of it:
pkgs.hello.overrideAttrs (oldAttrs: {
buildInputs = oldAttrs.buildInputs or [] ++ [ pkgs.makeWrapper ];
postInstall = oldAttrs.postInstall or "" + ''
wrapProgram $out/bin/hello \
--add-flags "-t"
'';
})
Debugging
Common Errors
Bad configuration option: gssapikexalgorithms
Found when using an SSH binary from Nix on typically RPM-based distros like CentOS, Fedora, Scientific Linux, Redhat, etc. The quick fix: Just comment out the configuration option in the ssh config file, you probably don't need it.
Desktop Environment does not find .desktop files
IF your DE does not look in $HOME/.nix-profile/share
for .desktop files.
You need to add that path to the XDG_DATA_DIRS
, the position reflects precedence so files in earlier directories shadow files in later directories. This can be accomplished in various ways depending on your login manager, see Arch wiki: Xprofile for more information.
For example using ~/.xprofile
as follows:
$ export XDG_DATA_DIRS=$HOME/.nix-profile/share:/usr/local/share:/usr/share
Notice that you have to include the default locations on your system, otherwise they will be overwritten. Find out the proper paths using echo $XDG_DATA_DIRS
. (Note: export XDG_DATA_DIRS=$HOME/.nix-profile/share:$XDG_DATA_DIRS
did not work, XDG_DATA_DIRS ended up containing only $HOME/.nix-profile/share:
which isn't even a valid path.)
NOTE: The above fix will make your programs installed by nix visible in your application menu, but you still will not be able to run them, because they are symlinked outside your XDG_DATA_DIRS paths, and are not executable (one or the other criteria must be met to run the program from a menu). This impacts KDE users, and potentially others. I noticed that on native NixOS with KDE, NixOS adds all these paths for each application to one's XDG_DATA_DIRS variable.
error: The option has conflicting definitions
If while doing a
nixos-rebuild switch
you see an error like:
building Nix...
building the system configuration...
error: The option `systemd.services.postfix.serviceConfig.PIDFile' has conflicting definitions, in `/nix/var/nix/profiles/per-user/root/channels/nixos/nixos/modules/
services/mail/postfix.nix' and `/etc/nixos/configuration.nix'.
(use '--show-trace' to show detailed location information)
This means exactly what it says, but how to fix it? Assuming one of the nix files is your configuration file, then you want your version to stick, and not the version from some maintainer somewhere, you can use
mkOverride
, which is a nix property defined in lib. so in the example above, the option in conflict is 'systemd.services.postfix.serviceConfig.PIDFile', so to override it you would do something like:
systemd.services.postfix.serviceConfig.PIDFile = lib.mkOverride 0 "mynewvalue";
which will override the other value, and force yours to have priority.