It's pretty simple to use a repo to aid the building of nix packages. You need:
- Two machines, let's call them
Builder
andRepo
. - Each machine has nix installed. Let's say that
Repo
has a nix installation owned by the usernix-repo
, andBuilder
has one owned bynix-builder
. nix-builder
is able to SSH intoRepo
asnix-repo
.
Once you have this, to build a package on Builder
using Repo
:
Builder$ nix-build <thing_to_build> --option ssh-substituter-hosts nix-repo@Repo
Once a build is finished, it's likely that there will be one or more new objects that Builder
built, that weren't on Repo
. To upload these objects from Builder
to Repo
, we need to know what their paths are. nix-build
prints to stdout the paths that were built, which we can store in a text file (this second build will complete instantly, so don't worry; of course, if you're reading ahead you could just run this the first time):
Builder$ nix-build <thing_to_build> --option ssh-substituter-hosts nix-repo@Repo > store_paths.txt
Then we use nix-copy-closure
to upload them back to the repo:
Builder$ cat store_paths.txt | xargs nix-copy-closure --to nix-repo@Repo
(Note that it might be desirable to add extra options to nix-copy-closure
; see the manual for details).
At this point the paths will exist on Repo
, and be available to future builds and other build machines as well.
So let's show a quick example. Let's assume Builder
has only nix installed and nothing else. Let's assume it has a nix expression for foo
living in foo.nix
, and that foo
has not been built on the Builder
or the Repo
.
Builder$ nix-build foo.nix --option ssh-substituter-hosts nix-repo@Repo --dry-run
The --dry-run
option will tell nix not to actually do the build, but to show what actions would be performed. It should say something along the lines of
these derivations will be built:
/nix/store/<some long hash>-foo.drv
It's saying that the derivations will be built, and not fetched. This means they will be built from scratch. So let's do the actual build, and put it in store_paths.txt
:
Builder$ nix-build foo.nix --option ssh-substituter-hosts nix-repo@Repo > store_paths.txt
We can cat store_paths.txt
and verify that it contains the path in the nix store. Now we upload that to the repo:
Builder$ cat store_paths.txt | xargs nix-copy-closure --to nix-repo@Repo
At this point, we can SSH into the repo and verify that the paths are found there:
Repo$ ls /nix/store | grep foo
/nix/store/<some long hash>-foo
Going back to the builder machine, let's delete the thing that we built. We have to delete the result
symlink that was created by the nix-build
or it won't let us:
Builder$ rm result
Builder$ nix-store --delete /nix/store/<some long hash>-foo
Now let's try building foo
again:
Builder$ nix-build foo.nix --option ssh-substituter-hosts nix-repo@Repo --dry-run
This time, it will be slightly different:
these paths will be fetched:
/nix/store/<some long hash>-foo
Now it's pulling down from the repo, so it doesn't need to build! Yay. We can confirm this by doing a build:
Builder$ nix-build foo.nix --option ssh-substituter-hosts nix-repo@Repo
And see that it's fetching the path instead of building:
fetching path '/nix/store/<some long hash>-foo'
And there we go, the repo is working.