This buildpack installs software using the Nix package manager. The software is assumed to have been built by Hydra, leading to speedy and predictable deployments that do no compilation of their own.
The resulting app must be run within PRoot, incurring a performance penalty.
Create a Procfile something like this (run_proot.sh will be created by the build scripts):
web: run_proot.sh myapp
Then create your application on Heroku, setting the location of your Hydra server:
heroku create -b https://github.com/ocharles/heroku-buildpack-nix-hydra.git
heroku config:set NIX_EXTRA_BINARY_CACHE=http://hydra.acme.net:3000
If you are using nix-serve
or another way to provide a binary cache, simply
provide the binary cache URL in place of the Hydra url.
Next, you need to configure your Hydra instance. The full details are outside
the scope of this readme, but you shouldn’t need more than the following
(in release.nix
) to get started:
{
my-project = import <my-project/shell.nix>;
}
Add a new input to this jobset called my-project
.
Your project now needs a fully self-contained Nix expression to build it.
Assuming you already have a normal default.nix
build function, you can work
with a shell.nix
expression such as:
{
nixpkgs ?
let
inherit (import <nixpkgs> {}) fetchFromGitHub;
in
import
( fetchFromGitHub
{
owner = "NixOS";
repo = "nixpkgs";
rev = "3f96280da0ebc578599130c55c283036b51a9e91";
sha256 = "1sv8rznqna02z642hfjykdr9xjn228932jkc03y8a4rqplqqg6l1";
}
) { config.allowUnfree = true; }
}:
let
inherit (nixpkgs) pkgs;
in pkgs.callPackage ./. {};
Notice that nixpkgs
is explicitly provided inside this expression, to
maximize reproducibility.
Push your application to the Git remote that Hydra is configured to build from. Once the build has completed, you can perform a deployment by pushing to your Heroku remote:
git push heroku master
For this to work, it’s essential that you get identical Nix store paths during Heroku deployment. Which is easier said than done. The main gotcha seems to be that whenever you refer to a path in a Nix file, a copy operation happens within the Nix store, even if that file is already in the Nix store.
Why is this a problem? The first thing Hydra does is pull the Git repository
down, and inserts it straight into the Nix store. This means that
<my-project>
in the original example is going to expand to something like
/nix/store/deadbeef33333-git-export
. However, in shell.nix
for
my-project
we refer to ./.
, which causes a copy operation, which means
a path like /nix/store/new-sha-here-deadbeef33333-git-export
.
This buildpack tries to take care of that by essentially copying what Hydra
does - the $BUILD_DIR
is immediately inserted into the Nix store, and all
subsequent evaluation is derived from that. However, if you keep finding that
Heroku builds rather than downloads from Hydra, drop me a message and let’s
see if we can figure out what’s going wrong.
This work builds on top of Cora Johnson-Roberson’s =heroku-buildpack-nix-proot= build pack. Many thanks for doing the initial exploration into this. Also, I send my thanks to those who made Cora’s original work possible.