Search code examples
nixnixosnixops

Propagate nixpkgs checkout to NixOps machines


I'm maintaining my personal changes on nixpkgs, which I use both for system rebuilds (via NixOps) and for development on my workstation (mostly via nix-shell). The changes are commits rebased ontop of the nixos-17.09 channel and are stored on a private git server. All of the machines in the deployment have read access to that git server.

When using nix-shell on a remote machine, I get old packages originating from the install time of the machine (in my case nixos-17.03).

Is there any way to have the exact same revision of nixpkgs which is used for deployment available at the remote machine?


Solution

  • The problem your having is due to the fact that because NixOps builds locally and then copies the closure to the remote machines, and as such does not run nix-channel --update on the remote machines, the Nix Packages Collection (nixpkgs) is not updated on the remote machines.

    Commands such as nix-env and nix-shell rely on the nixpkgs pointed to by $NIX_PATH (or whichever path you provide via the -I argument).

    Solution

    The solution is to get your version of nixpkgs into the remote machines and ensure $NIX_PATH points to it. By default, $NIX_PATH looks like this:

    nixpkgs=/nix/var/nix/profiles/per-user/root/channels/nixos/nixpkgs:nixos-config=/etc/nixos/configuration.nix:/nix/var/nix/profiles/per-user/root/channels
    

    So the idea is to make it look like something like this:

    nixpkgs=/nix/store/blah-blah-blah:nixos-config=/etc/nixos/configuration.nix:/nix/var/nix/profiles/per-user/root/channels
    

    where blah-blah-blah is your nixpkgs Git repo inserted into the Nix store. Here's an example of how to do that:

    let
        # This will point to whatever NIX_PATH states on the local machine,
        # unless overwritten with -I.
        hostNixpkgs = import <nixpkgs> {};
    
        # Some other nixpkgs from a GitHub repo
        romildo = hostNixpkgs.pkgs.fetchFromGitHub {
             owner = "romildo";
             repo = "nixpkgs";
             rev = "b167ba35987c2e69dc1bb91d5c245a04421ee7ed";
             sha256 = "02d8dfvginz45h2zhq745kynzygnsy760qh7yy152wpfhczag180";
        };
    in
    {
        network.description = "My NixOS VMs";
    
        vm0 = { config, lib, pkgs, ... }:
        {
          ...
    
          # This is really hacky, but it works.
          # I'd prefer to set environment.etc.NIX_PATH, but that's not allowed,
          # because that value is "read-only".
          environment.extraInit = "export NIX_PATH=nixpkgs=${romildo}:nixos-config=/etc/nixos/configuration.nix:/nix/var/nix/profiles/per-user/root/channels";
        };
    }
    

    How it works

    Nix creates /nix/store/BLAH-set-environment which exports a bunch of environment variables, among other things. This file is made available in all shell sessions. environment.extraInit makes it possible to append whatever shell-neutral (sh) code you want to this environment. Because this arbitrary code is inserted at the end of the script, it can be used to override the environment variables, such as NIX_PATH.

    The caveat is that you must log back in after deployment in order for the change to take affect. But the resulting NIX_PATH will look something like this: nixpkgs=/nix/store/mzxkszfv05np2f6rgdi2kwxd937f0sxa-source:nixos-config=/etc/nixos/configuration.nix:/nix/var/nix/profiles/per-user/root/channels. So in this example, nix-shell will look for nixpkgs at /nix/store/mzxkszfv05np2f6rgdi2kwxd937f0sxa-source.