Add a file to the nix store (permanently)

How to add a file to the nix store and have it survive garbage collection.

If you have proprietary software which you would like to install on your nixos you are going to run into an issue: there is likely no public url for you to download the files from. Given this you are probably going to want to download the file and put it in the nix store manually so your derivations can reference it. For some reason I found this to be rather difficult to figure out how to do. I also did it wrong and garbage collection just blew away my custom fonts. This article is written so I never have to remember how to do this again. If you want to skip the reading, here’s a worked example.

Add a file to the store

The fancy new nix 3 command line has the store subcommand which lets you manipulate the store. nix store --help reveals that there are the following subcommands:

· nix store add-file - add a regular file to the Nix store
· nix store add-path - add a path to the Nix store

Surely one of these is what we want, right? Maybe! For reasons I don’t really understand these two commands are missing some of the features the older command had… For that reason the best tool to use (in my experience) is nix-prefetch-url. The man pages say about everything you want, here’s what I find useful.

  • It is meant to be run with urls, local files will have a url structure like file:/absolute/path/to/file.zip
  • The name of the prefetched file has to match the name of the file in your derivation, not just the hash. This took me a hot minute to figure out. You can force the name of the imported file to be different than the name of the original file with the --name option.
  • If the file is an archive you probably want the --unpack flag, this will make it compatible with fetchzip and the like.
  • To get the path of the file in the store (which you will need to register a GC root) you need the --print-path flag.

Keep that file safe

Great! We got a file into the store, problem: this file has nothing pointing to it, the nix garbage collector is liable to delete it at a moments notice. To stop this we need to register a GC root. As far as I can tell there are no commands in place to do this for you. Let alone do it atomically1. Turns out a gc root is just a symlink in /nix/var/nix/gcroots pointing into the nix store. So this is as easy as:

ln -st /nix/var/nix/gcroots /nix/store/<path>

A worked example

In this example I am pushing a zip file of the lovely butterick fonts into the nix store so I can reference that zip file to install the fonts. First we push the file into the nix store.

$ nix-prefetch-url \
	--unpack \
	--print-path \
	file:/home/ethan/downloads/butterick

1j62qgsvyyza2yzam5rzalky7lgdyxj9gngrzyz0zpqkrnwpz5cx
/nix/store/sb9856q6l3yhzykh83m39rrlsmrhxklz-butterick.zip

Then we register the GC root

$ sudo ln -st \
	/nix/var/nix/gcroots \
	/nix/store/sb9856q6l3yhzykh83m39rrlsmrhxklz-butterick.zip

I like the SRI hash format used with most nix derivations for some reason… so we convert the hash from the prefetch step to SRI using nix-hash.

$ nix-hash \
	--to-sri \
	--type sha256 \
	1j62qgsvyyza2yzam5rzalky7lgdyxj9gngrzyz0zpqkrnwpz5cx

sha256-nZV/uc0T3w++//nZl2T37dHjJ1U/l6q+F+p7v/XDwsg=

Finally we can write a nix expression to reference this file.

fetchzip {
    url = " ";
    # name has to match the file name...
    name = "butterick.zip"; 
    hash = "sha256-nZV/uc0T3w++//nZl2T37dHjJ1U/l6q+F+p7v/XDwsg=";
}

  1. Unfortunately the garbage collector could, in theory, run after you add the file but before you register the GC root. This seems unlikely, if it happens you can re-run nix-prefetch-url with the broken GC root in place and everything should just work™.↩︎