Creating a static nix channel
TL;DR: full example at https://github.com/lpenz/nixpkgs-lpenz/
Nix is a packet manager for Unix-like systems that can be effectively used as an overlay distribution.
These are my notes on how to create a static nix channel with a binary cache that can be used to distribute compiled packages - and how to do it using travis-ci and github pages. This assumes that we already have the derivations that we want to distribute.
You should also check out Cachix, as an alternative to what is described here.
Generating key pair
The first step in creating a repository is generating a key pair:
nix-store --generate-binary-cache-key lpenz.org cache-priv-key.pem cache-pub-key.pem
That creates the single-line files cache-priv-key.pem
and
cache-pub-key.pem
. The first has the private key, which we will
use to do the packaging process in travis-ci (store it in a safe
place). The second file has the public key that we will use to
authenticate the packages in the clients.
Setting up the repository in github and travis-ci
This article is not going into the details of how to create a repository and populate it with nix derivations. Here are some references on that:
- https://nixos.org/nixos/nix-pills/generic-builders.html
- https://nixos.org/nixpkgs/manual/#chap-stdenv
To set up github pages, go to the Settings page of the github repsitory with the derivations, GitHub Pages and set Source to the gh-pages branch.
To set up travis-ci, create a NIX_CACHE_PRIV_KEY environment variable in
the settings
(details),
with the contents of cache-priv-key.pem
. Keep in mind the best
practices at
Recommendations on how to avoid leaking secrets to build logs
to avoid problems.
Creating the channel
A channel is simply a nixexprs.tar.xz
with all nix derivation
files, and a binary-cache-url
file that, as the name implies,
points to the binary cache URL.
Let's assume that we are creating the channel in the _output
directory, and that we will put the cache files in
_output/cache
. To create the nixexprs.tar.xz
file:
tar -cJf _output/nixexprs.tar.xz ./*.nix \ --transform "s,^,${PWD##*/}/," \ --owner=0 --group=0 --mtime="1970-01-01 00:00:00 UTC"
Creating the binary-cache-url
file is simpler:
echo '<URL of cache>' > _output/binary-cache-url
Nix also tries to retrieve the channel URL by itself - create a
index.html
file to handle that:
touch _output/index.html
Creating the binary cache
To create the binary cache, start by building the packages with:
nix-build
Then sign the results:
export NIX_SECRET_KEY_FILE="$PWD/nix-cache-priv-key.pem" echo "$NIX_CACHE_PRIV_KEY" > "$NIX_SECRET_KEY_FILE" nix sign-paths
And finally, copy the results from the store to the cache directory:
nix copy --to "file:///$PWD/_output/cache"
.travis.yml deploy section
Detailed instructions on how to set this up are at GitHub Pages Deployment. We essentially have to set up a GITHUB_TOKEN.
The deploy section of .travis.yml
ends up looking like the following:
deploy: provider: pages skip-cleanup: true github-token: "$GITHUB_TOKEN" local_dir: _output target-branch: gh-pages on: branch: master
Using the channel
To use the channel, add the channel URL to nix:
nix-channel --add <URL of channel> nix-channel --update
That allows us to build packages from the channel, but it doesn't
make nix use the binary cache. To enable the cache, we have to set up
the channel as a substituter. To do that, put the following in
~/.config/nix/nix.conf
, creating the file it if it doesn't exist:
substituters = https://cache.nixos.org <URL of cache> trusted-public-keys = cache.nixos.org-1:6NCHdD59X431o0gWypbMrAURkbJ16ZPMQFGspcDShjY= <cache-pub-key.pem contents>
Note that we have to also add the default, as that line is an absolute configuration.
Conclusions
Nix is very versatile, and one way to use it is an overlay distribution, by installing nixpkgs over an existing non-NixOS linux installation. On that setup, a private nix channel can be very useful to develop and deploy software consistently without being pegged to a particular gloal distribution or environment - and the binary cache allows us to do that without even having to recompile from source. That's how I've been using it, and I'm quite happy with the results.