diff --git a/nix/sources.json b/nix/sources.json
new file mode 100644
index 0000000..bdd1fd7
--- /dev/null
+++ b/nix/sources.json
@@ -0,0 +1,26 @@
+{
+    "niv": {
+        "branch": "master",
+        "description": "Easy dependency management for Nix projects",
+        "homepage": "https://github.com/nmattia/niv",
+        "owner": "nmattia",
+        "repo": "niv",
+        "rev": "abd0de3269fd712955d27b70e32921841c7b8bb7",
+        "sha256": "0b38n1ad00s1qqyw3ml3pypf8i1pw4aqw0bpa02qq9iv7sp3x0gz",
+        "type": "tarball",
+        "url": "https://github.com/nmattia/niv/archive/abd0de3269fd712955d27b70e32921841c7b8bb7.tar.gz",
+        "url_template": "https://github.com/<owner>/<repo>/archive/<rev>.tar.gz"
+    },
+    "nixpkgs": {
+        "branch": "nixos-unstable",
+        "description": "A read-only mirror of NixOS/nixpkgs tracking the released channels. Send issues and PRs to",
+        "homepage": "https://github.com/NixOS/nixpkgs",
+        "owner": "NixOS",
+        "repo": "nixpkgs-channels",
+        "rev": "4557b9f1f50aa813ae673fe6fcd30ca872968947",
+        "sha256": "0cam48cn042axcik9vqxsqjc2hwyb2grjbjxacsn4w0y1zk6k6l2",
+        "type": "tarball",
+        "url": "https://github.com/NixOS/nixpkgs-channels/archive/4557b9f1f50aa813ae673fe6fcd30ca872968947.tar.gz",
+        "url_template": "https://github.com/<owner>/<repo>/archive/<rev>.tar.gz"
+    }
+}
diff --git a/nix/sources.nix b/nix/sources.nix
new file mode 100644
index 0000000..718ea6f
--- /dev/null
+++ b/nix/sources.nix
@@ -0,0 +1,136 @@
+# This file has been generated by Niv.
+
+let
+
+  #
+  # The fetchers. fetch_<type> fetches specs of type <type>.
+  #
+
+  fetch_file = pkgs: spec:
+    if spec.builtin or true then
+      builtins_fetchurl { inherit (spec) url sha256; }
+    else
+      pkgs.fetchurl { inherit (spec) url sha256; };
+
+  fetch_tarball = pkgs: spec:
+    if spec.builtin or true then
+      builtins_fetchTarball { inherit (spec) url sha256; }
+    else
+      pkgs.fetchzip { inherit (spec) url sha256; };
+
+  fetch_git = spec:
+      builtins.fetchGit { url = spec.repo; inherit (spec) rev ref; };
+
+  fetch_builtin-tarball = spec:
+    builtins.trace
+      ''
+        WARNING:
+          The niv type "builtin-tarball" will soon be deprecated. You should
+          instead use `builtin = true`.
+
+          $ niv modify <package> -a type=tarball -a builtin=true
+      ''
+      builtins_fetchTarball { inherit (spec) url sha256; };
+
+  fetch_builtin-url = spec:
+    builtins.trace
+      ''
+        WARNING:
+          The niv type "builtin-url" will soon be deprecated. You should
+          instead use `builtin = true`.
+
+          $ niv modify <package> -a type=file -a builtin=true
+      ''
+      (builtins_fetchurl { inherit (spec) url sha256; });
+
+  #
+  # Various helpers
+  #
+
+  # The set of packages used when specs are fetched using non-builtins.
+  mkPkgs = sources:
+    if hasNixpkgsPath
+    then
+      if hasThisAsNixpkgsPath
+      then import (builtins_fetchTarball { inherit (mkNixpkgs sources) url sha256; }) {}
+      else import <nixpkgs> {}
+    else
+      import (builtins_fetchTarball { inherit (mkNixpkgs sources) url sha256; }) {};
+
+  mkNixpkgs = sources:
+    if builtins.hasAttr "nixpkgs" sources
+    then sources.nixpkgs
+    else abort
+      ''
+        Please specify either <nixpkgs> (through -I or NIX_PATH=nixpkgs=...) or
+        add a package called "nixpkgs" to your sources.json.
+      '';
+
+  hasNixpkgsPath = (builtins.tryEval <nixpkgs>).success;
+  hasThisAsNixpkgsPath =
+    (builtins.tryEval <nixpkgs>).success && <nixpkgs> == ./.;
+
+  # The actual fetching function.
+  fetch = pkgs: name: spec:
+
+    if ! builtins.hasAttr "type" spec then
+      abort "ERROR: niv spec ${name} does not have a 'type' attribute"
+    else if spec.type == "file" then fetch_file pkgs spec
+    else if spec.type == "tarball" then fetch_tarball pkgs spec
+    else if spec.type == "git" then fetch_git spec
+    else if spec.type == "builtin-tarball" then fetch_builtin-tarball spec
+    else if spec.type == "builtin-url" then fetch_builtin-url spec
+    else
+      abort "ERROR: niv spec ${name} has unknown type ${builtins.toJSON spec.type}";
+
+  # Ports of functions for older nix versions
+
+  # a Nix version of mapAttrs if the built-in doesn't exist
+  mapAttrs = builtins.mapAttrs or (
+    f: set: with builtins;
+    listToAttrs (map (attr: { name = attr; value = f attr set.${attr}; }) (attrNames set))
+  );
+
+  # fetchTarball version that is compatible between all the versions of Nix
+  builtins_fetchTarball = { url, sha256 }@attrs:
+    let
+      inherit (builtins) lessThan nixVersion fetchTarball;
+    in
+      if lessThan nixVersion "1.12" then
+        fetchTarball { inherit url; }
+      else
+        fetchTarball attrs;
+
+  # fetchurl version that is compatible between all the versions of Nix
+  builtins_fetchurl = { url, sha256 }@attrs:
+    let
+      inherit (builtins) lessThan nixVersion fetchurl;
+    in
+      if lessThan nixVersion "1.12" then
+        fetchurl { inherit url; }
+      else
+        fetchurl attrs;
+
+  # Create the final "sources" from the config
+  mkSources = config:
+    mapAttrs (
+      name: spec:
+        if builtins.hasAttr "outPath" spec
+        then abort
+          "The values in sources.json should not have an 'outPath' attribute"
+        else
+          spec // { outPath = fetch config.pkgs name spec; }
+    ) config.sources;
+
+  # The "config" used by the fetchers
+  mkConfig =
+    { sourcesFile ? ./sources.json
+    }: rec {
+      # The sources, i.e. the attribute set of spec name to spec
+      sources = builtins.fromJSON (builtins.readFile sourcesFile);
+      # The "pkgs" (evaluated nixpkgs) to use for e.g. non-builtin fetchers
+      pkgs = mkPkgs sources;
+    };
+in
+mkSources (mkConfig {}) //
+  { __functor = _: settings: mkSources (mkConfig settings); }
diff --git a/nixpkgs-update b/nixpkgs-update
deleted file mode 100755
index f479b1c..0000000
--- a/nixpkgs-update
+++ /dev/null
@@ -1,30 +0,0 @@
-#!/usr/bin/env nix-shell
-#!nix-shell -i bash -p bash curl jq
-#
-# Updates the pinned nixpkgs version
-set -euo pipefail
-
-source=nixpkgs.nix
-owner=NixOS
-repo=nixpkgs-channels
-branch=nixos-unstable
-
-echo "finding new revision for $owner/$repo@$branch..."
-
-rev=$(curl -sfL https://api.github.com/repos/$owner/$repo/git/refs/heads/$branch | jq -r .object.sha)
-
-echo "rev=$rev"
-
-url=https://github.com/$owner/$repo/archive/$rev.tar.gz
-
-echo "finding unpacked sha256 for $url..."
-
-sha256=$(nix-prefetch-url --unpack "$url")
-
-echo "generating new $source..."
-cat <<NEW_SOURCE | tee "$source"
-builtins.fetchTarball {
-  url = "$url";
-  sha256 = "$sha256";
-}
-NEW_SOURCE
diff --git a/nixpkgs.nix b/nixpkgs.nix
deleted file mode 100644
index a542566..0000000
--- a/nixpkgs.nix
+++ /dev/null
@@ -1,4 +0,0 @@
-builtins.fetchTarball {
-  url = "https://github.com/NixOS/nixpkgs-channels/archive/4557b9f1f50aa813ae673fe6fcd30ca872968947.tar.gz";
-  sha256 = "0cam48cn042axcik9vqxsqjc2hwyb2grjbjxacsn4w0y1zk6k6l2";
-}
diff --git a/shell.nix b/shell.nix
index b92d1dc..6fa1a96 100644
--- a/shell.nix
+++ b/shell.nix
@@ -1,14 +1,14 @@
 let
-  nixpkgs = import ./nixpkgs.nix;
+  sources = import ./nix/sources.nix;
 
-  pkgs = import nixpkgs {
+  pkgs = import sources.nixpkgs {
     config = {};
     overlays = [];
   };
 
 in pkgs.mkShell {
 
-  NIX_PATH="nixpkgs=${nixpkgs}";
+  NIX_PATH="nixpkgs=${toString pkgs.path}";
 
   buildInputs = [
     pkgs.git-crypt