Just a random collection of things to reference about nix.
Minimal nix derivation
with import <nixpkgs> {};
stdenv.mkDerivation {"env";
name =
buildInputs = [
bashInteractive
]; }
This is useful for writing a quick and dirty, non-language specific
default.nix
as a starting point to potentially packaging something.
If you need some tooling for a project, drop the tools in buildInputs
, then
when you nix-shell
, you’ll have them available. Like:
with import <nixpkgs> {};
stdenv.mkDerivation {"env";
name =
buildInputs = [
awscli python36 nodejs-8_x
]; }
mkShell
If you only want a shell environment, you can use mkShell
in a shell.nix
,
which is slightly shorter:
with import <nixpkgs> {};
mkShell {
buildInputs = [
awscli python36 nodejs-8_x
]; }
And also supports the inputsFrom
parameter, if you want to pull build inputs
from other packages
import <nixpkgs> {} }:
{ pkgs ?
pkgs.mkShell {# this will make all the build inputs from hello and gnutar
# available to the shell environment
with pkgs; [ hello gnutar ];
inputsFrom =
buildInputs = [ pkgs.gnumake ]; }
Pin nixpkgs
You’ll likely want to do this for any non-trivial project, as it insulates the project from any changes (or breakages) from the global nixpkgs and ensures the same version of nixpkgs for the project is used by every one.
niv
You should probably just use niv to do this.
https://github.com/nmattia/niv
See the bootstrapping section to start.
nix >= 2.0
If you don’t want to use niv and you only need to support nix > 2.0, you can use
builtins.fetchTarball
The builtin function supports the a sha256
param on
the function, so it doesn’t have to constantly refetch the tarball to check for
updates.
. If you need to support < 2.0 nix versions (or a mix), see the next
section.
"92f0d31b949bcf018262252408b8a69376a6cb68"
{ rev ? "08ir579xsh7414i9y7jaislr2d56q716ayv2jlrrbpsx38gly453"
, sha256 ?
}:
let
builtins.fetchTarball {
nixpkgsPath = "https://github.com/NixOS/nixpkgs/archive/${rev}.tar.gz";
url = inherit sha256;
};
import nixpkgsPath {
pkgs =
config = {# do whatever you want
# packageOverrides = oldPkgs: let newPkgs = oldPkgs.pkgs; in {...}
};
};in
# do stuff with pkgs
You could also make nixpkgs a parameter and import the source inline, which is more compact.
"92f0d31b949bcf018262252408b8a69376a6cb68"
{ rev ? "08ir579xsh7414i9y7jaislr2d56q716ayv2jlrrbpsx38gly453"
, sha256 ? import (builtins.fetchTarball {
, nixpkgs ? "https://github.com/NixOS/nixpkgs/archive/${rev}.tar.gz";
url = inherit sha256;
}) { config = {} }
}:
# do stuff with nixpkgs
nix < 2.0
For pre-2.0 nix support, you could use this fetch-nixpkgs
helper or use fetchFromGitHub
.
With fetchFromGitHub
:
import <nixpkgs> {}
{ hostPkgs ? "92f0d31b949bcf018262252408b8a69376a6cb68"
, rev ? "08ir579xsh7414i9y7jaislr2d56q716ayv2jlrrbpsx38gly453"
, sha256 ? import (hostPkgs.fetchFromGitHub {
, nixpkgs ? "NixOS"; repo = "nixpkgs"; inherit rev sha256;
owner =
}) { config = {} }
}:
# do stuff with nixpkgs
In environment
You can also set NIX_PATH
to a location to use for <nixpkgs>
, which if set
in some file sourced by the project (say a .env
or .envrc
), pins the version.
Either a specific commit:
export NIX_PATH=nixpkgs=https://github.com/NixOS/nixpkgs-channels/archive/628ba24e77b2c6f82f4c2d9e8a31d93fa06f2743.tar.gz:.
Or a channel:
export NIX_PATH=nixpkgs=https://github.com/NixOS/nixpkgs-channels/archive/nixos-19.03-small.tar.gz:.
nix-shell as an interpreter
The nix manual has a nice section on this.
Older, but longer tutorial on this.
Package script dependencies:
#! /usr/bin/env nix-shell
#! nix-shell -i bash -p opusTools -p graphicsmagick -p file -p parallel -p flac
# do script things as normal, guaranteed the specified tools are available
Run Haskell as a script:
#! /usr/bin/env nix-shell
#! nix-shell -i runghc -p "haskellPackages.ghcWithPackages (ps: with ps; [])"
main :: IO ()
= putStrLn "Hello World!" main
Pairs well with something like turtle, e.g.,
#! /usr/bin/env nix-shell
#! nix-shell -i runghc -p "haskellPackages.ghcWithPackages (ps: with ps; [turtle])"
{-# LANGUAGE OverloadedStrings #-}
import Turtle
data Command = IncreaseVolume Int | DecreaseVolume Int deriving (Show)
parser :: Parser Command
parser= fmap IncreaseVolume
"up" "Turn the volume up"
(subcommand "amount" "How much to increase the volume") )
(argInt <|> fmap DecreaseVolume
"down" "Turn the volume down"
(subcommand "amount" "How much to decrease the volume") )
(argInt
= do
main <- options "Volume adjuster" parser
x case x of
IncreaseVolume n -> printf ("Increasing the volume by "%d%"\n") n
DecreaseVolume n -> printf ("Decreasing the volume by "%d%"\n") n
Write your bash scripts…in Haskell!
Example taken from the end of the last section of the Turtle tutorial.
nix + direnv
https://github.com/direnv/direnv/wiki/Nix
Just have a default.nix
or shell.nix
setup for you project. And an .envrc
containing:
use nix
After a direnv allow
in the directory, every time you move into the directory
you’ll have a nix-shell
environment setup for you automatically, in your own
familiar shell.
Combine this with an editor plugin for direnv
and as you move around in your
editor, your project dependencies will be setup for you automatically. Very
convenient.
The upstream nix function in direnv is pretty simple. You’ll probably want to use a modified one to speed up direnv, particularly if you have it integrated with you editor. See the section on a persistent and cached shell for nix with direnv.
If you’re feeling adventurous, look into https://github.com/target/lorri
instead of using the nix-shell
direnv stuff directly.
Language-specific things
Many languages have to tooling to automate packaging with nix, e.g.,
cabal2nix
, pypi2nix
, etc. For a more complete list see the wiki
page.
This support is uneven across languages, largely dependent on how many nix users there are who give their time to improve and maintain that languages infrastructure. Some languages are also more or less hostile to packaging in the nix way, some have more non-determinism or poor dependency tracking/specification, which complicate making easy tooling for it.
Chapter 9 of the nixpkgs manual has more details on a numbers of different languages, development tips, troubleshooting, etc.
Haskell
The Haskell infrastructure in nix is extensive, the section in the manual is a useful read.
Basic dev environment
Probably the quickest way to get going developing a Haskell package is with haskellPackages.developPackage:
Have a cabal file for your project, then your default.nix
can just be:
import <nixpkgs> {} }:
{ pkgs ?
pkgs.haskellPackages.developPackage {
root = ./.; }
This automatically calls cabal2nix
, makes it easy to specify overrides, and
will drop you into a development shell when nix-shell
is used (no need for a
separate shell.nix
).
If you want to fix a compiler version, say the default is too new for you:
import <nixpkgs> {}
{ pkgs ? "ghc842"
, ghcVersion ?
}:
"${ghcVersion}".developPackage {
pkgs.haskell.packages.
root = ./.; }
There’s a little more on this in the manual, particularly for when you need to override a dependencies packaged version.
A more complete (and complicated) example:
import <nixpkgs> {}
{ pkgs ? "ghc842"
, ghcVersion ?
}:
"${ghcVersion}".developPackage {
pkgs.haskell.packages.
root = ./.;
# `source-overrides` are overrides on which source code to fetch for a
# package, here we can specify *what* to build for a dependency
source-overrides = {# if you need to use a different version from Hackage than what is in
# the current package set, can just specify the version and it will be
# fetched automatically (gets turned into a call to the `callHackage`
# function)
"1.5.0.0";
HUnit =
# if you need a Haskell package that is not on Hackage, say either an
# unpublished version or a fork, just fetch the repo
servant-swagger = pkgs.fetchFromGitHub {"haskell-servant";
owner = "servant-swagger";
repo = "9c0ceb9f5cd00a47e922ba5de4be5ad591d99a0a";
rev = "1qkpj40sgy0nidd9av4rvd8754lh6zhakdqa05jgkzizf37wfz10";
sha256 =
};
# if you have a local checkout of a package, you can specify the path to it,
# this might commonly be a submoduled package, but you can specify paths
# outside the current project, though this of course only works for you personal
# local setup, but sometimes that's all you need
my-local-package = ../my-local-package;
};
# `overrides` are overrides to the package derivations themselves, so it's
# here we can specify changes to *how* dependencies are built, generally
# using helper functions from the nix haskell library, see all the options:
# https://github.com/NixOS/nixpkgs/blob/f3282c8d1e0ce6ba5d9f6aeddcfad51d879c7a4a/pkgs/development/haskell-modules/lib.nix
#
# "overriding" a set of things is a common pattern in nix, for clarity here
# I've used a more verbose style, but:
#
# `newHaskellPkgs` is conventionally called `self` and is the overridden set
# after it's been resolved, which you sometimes need
#
# `oldHaskellPkgs` is conventionally called `super`, it's basically the set of
# things you are overriding
overrides = newHaskellPkgs: oldHaskellPkgs: {# sometimes you need to use a package that has a broken test suite for
# whatever reason, here in overrides you can use the helper `dontCheck` to
# skip the test suite when building the package
hasktags = pkgs.haskell.lib.dontCheck oldHaskellPkgs.hasktags;
# sometimes packages have overly restrictive version bounds, the
# `doJailbreak` helper can tell the build to ignore them so it can be built
# against the versions you have in your package set
async = pkgs.haskell.lib.doJailbreak oldHaskellPkgs.async;
# sometimes you need to patch a package to build or work right with you
# package set
some-package = pkgs.haskell.lib.appendPatch oldHaskellPkgs.some-package ./fix-some-package.patch;
};
# this is a spot you can modify the derivation for the package itself,
# usually will be applying one haskell lib function like in `overrides`
modifier = pkgs.haskell.lib.buildStrictly; }
If you want to add some development tools to the nix-shell
environment, one
way you could do it is with a shell.nix
containing:
import <nixpkgs> {} }:
{ pkgs ? let
import ./default.nix { inherit pkgs; };
package = in
package.overrideAttrs (oldAttrs: {
buildInputs = oldAttrs.buildInputs ++ [
pkgs.haskellPackages.cabal-install
pkgs.haskellPackages.ghcid# ...
]; })
Though keeping the right compiler version/package set in sync between the dev
tools and the package environment from developPackage
could be challenging
since the package set from developPackage
isn’t exposed. I’m not sure as I
don’t use this setup.
For this and other reasons I generally recommend the next development setup, but
developPackage
is quick when you need it.
Improved setup
While haskellPackages.developPackage
is pretty easy, for projects with more
than one package, or if you just want more flexibility, or explicitness, you can
break apart what developPackage
is doing.
In your default.nix
, extend the Haskell package set to include the project packages:
import <nixpkgs> {} }:
{ pkgs ?
let
haskellPackages = pkgs.haskellPackages.extend (pkgs.haskell.lib.packageSourceOverrides {# your Haskell packages for the project
foo = ./foo;
bar = ./bar;# for a simple project with only one package at the top-level, this might be
# baz = ./.;
});in {
inherit pkgs haskellPackages;
# same as if we'd written:
# pkgs = pkgs;
# haskellPackages = haskellPackages;
# make the project package a top level attributes for easier building, e.g.,
# nix-build -A foo
#
# instead of
#
# nix-build -A haskellPackages.foo
foo = haskellPackages.foo;
bar = haskellPackages.bar; }
Then utilize the haskellPackages.shellFor
function to build a more
configurable development environment in your shell.nix
:
let
import ./. {};
project = in
project.haskellPackages.shellFor {# dependencies for the listed packages will all be available in the shell
packages = p: [p.foo p.bar];
# build a local hoogle database for all the dependencies of the given
# packages, the hoogle executable in the shell environment will be set to
# use it automatically
# withHoogle = true;
# list any other things you want available in the shell
buildInputs = [
project.haskellPackages.cabal-install# project.haskellPackages.ghcid
# ...whatever other Haskell dev tools you need
# project.pkgs.niv
# project.pkgs.gnumake
# project.pkgs.terraform
# ...whatever other general dev tools you need
]; }
Adapted from this comment. This is a simplified example, just trying to get the basic idea across.
You can get fancier, automatically pickup the project packages from the
cabal.project
, and such, see this
gist for an
example.
See the documentation on haskellPackages.shellFor and haskell.lib.packageSourceOverrides for more on what is actually going on.
A more complete (and complicated) example equivalent to the developPackage
one:
import <nixpkgs> {} }:
{ pkgs ?
let
haskellPackages = pkgs.haskellPackages.extend (pkgs.lib.composeExtensions# these are "source overrides", that is, overrides on which source code to
# fetch for a package, here we can specify *what* to build
(pkgs.haskell.lib.packageSourceOverrides ({# your Haskell packages for the project
foo = ./foo;
bar = ./bar;
# if you need to use a different version from Hackage than what is in
# the current package set, can just specify the version and it will be
# fetched automatically (gets turned into a call to the `callHackage`
# function)
"1.5.0.0";
HUnit =
# if you need a Haskell package that is not on Hackage, say either an
# unpublished version or a fork, just fetch the repo
servant-swagger = pkgs.fetchFromGitHub {"haskell-servant";
owner = "servant-swagger";
repo = "9c0ceb9f5cd00a47e922ba5de4be5ad591d99a0a";
rev = "1qkpj40sgy0nidd9av4rvd8754lh6zhakdqa05jgkzizf37wfz10";
sha256 =
};
# if you have a local checkout of a package, you can specify the path to it,
# this might commonly be a submoduled package, but you can specify paths
# outside the current project, though this of course only works for you personal
# local setup, but sometimes that's all you need
my-local-package = ../my-local-package;
}))
# these are overrides to the package derivations themselves, so it's here we
# can specify changes to *how* things are built, generally using helper
# functions from the nix haskell library, see all the options:
# https://github.com/NixOS/nixpkgs/blob/f3282c8d1e0ce6ba5d9f6aeddcfad51d879c7a4a/pkgs/development/haskell-modules/lib.nix
#
# "overriding" a set of things is a common pattern in nix, for clarity here
# I've used a more verbose style, but:
#
# `newHaskellPkgs` is conventionally called `self` and is the overridden set
# after it's been resolved, which you sometimes need
#
# `oldHaskellPkgs` is conventionally called `super`, it's basically the set of
# things you are overriding
(newHaskellPkgs: oldHaskellPkgs: {# you can tweak the build flags for you packages here if needed
foo = pkgs.haskell.lib.buildStrictly oldHaskellPkgs.foo
bar = pkgs.haskell.lib.buildStrictly oldHaskellPkgs.bar
# sometimes you need to use a package that has a broken test suite for
# whatever reason, here in overrides you can use the helper `dontCheck` to
# skip the test suite when building the package
hasktags = pkgs.haskell.lib.dontCheck oldHaskellPkgs.hasktags;
# sometimes packages have overly restrictive version bounds, the
# `doJailbreak` helper can tell the build to ignore them so it can be built
# against the versions you have in your package set
async = pkgs.haskell.lib.doJailbreak oldHaskellPkgs.async;
# sometimes you need to patch a package to build or work right with you
# package set
some-package = pkgs.haskell.lib.appendPatch oldHaskellPkgs.some-package ./fix-some-package.patch;
})
);
in {
inherit pkgs haskellPackages;
# same as if we'd written:
# pkgs = pkgs;
# haskellPackages = haskellPackages;
# make the project package a top level attributes for easier building, e.g.,
# nix-build -A foo
#
# instead of
#
# nix-build -A haskellPackages.foo
foo = haskellPackages.foo;
bar = haskellPackages.bar;
# say foo is an executable, statically linking it to it's Haskell libs can
# reduce the closure size and simplify some things, e.g., non-nix deploys,
# though fully static Haskell executables take some more work, see:
# https://github.com/NixOS/nixpkgs/issues/43795
foo-static = pkgs.haskell.lib.justStaticExecutables haskellPackages.foo; }
haskell.nix
haskell.nix is an alternate
Haskell infrastructure for nixpkgs. Its own motivation
section details why
it exists, but generally, the goal is to have nix doing more of the work
natively (instead of relying on things like cabal2nix
), which increases
flexibility, control, and simplicity.
I’ve not used it personally, but it’s neat and seems a worthwhile endeavor to experiment with what else is possible.
Other nix-haskell stuff
Learning Nix
https://learnxinyminutes.com/docs/nix/
Nix: Under the hood
Good talk covering the basics of how nix works, simply.
Test your NixOS config
nix repl '<nixpkgs/nixos>'
You can poke at your config without having to nixos-rebuild
, with TAB
complete.
The config
attribute holds your system config. pkgs
allows you to look at
the packages in that version of the system.
NixOS options
There are a lot of configuration options supported by NixOS. The options search page on the website is useful to find a) if the thing you want to configure has been packaged as a service in NixOS and b) what knobs you can turn for it.
One nice thing about the search tool is that it links directly to the source file for the service, so you can inspect what is actually happening with the option if you want to.
Note that currently, the website search tool only shows options for the latest
stable release, if you are living on the edge, you’ll have to search nixpkgs
yourself, either grep a local copy of the repo, search GitHub, or use
nixos-option
.
nixos-option
evaluates your NixOS config and will display all the attributes
in a given attribute set available for it. For example:
nixos-option services.postgresql
Will show all the attributes you can set to configure the postgres service. It unfortunately does not display the description when listing the attributes (though hopefully is at least broadly clear in the name itself), but if you specify a specific one, like:
nixos-option services.postgresql.dataDir
Then it will display more complete documentation for it, including the current value in your configuration, the default, a description of what the option is for, and a file path for module it’s declared in.
The website search is nicer, but nixos-option
works offline and can be helpful
as it does display the currently set value for your configuration. The repl is
usually better to explore your evaluated configuration, given it has TAB
complete, but sometimes nixos-option
is more useful.
Finding Nix packages
nix search <name>
or https://nixos.org/nixos/packages.html
Cheatsheet
The NixOS cheatsheet has a bunch of stuff that is useful to look over, including a table comparing how you would do something in Ubuntu and the equivalent command on NixOS.
It also has a bunch of short little snippets about how to do various things, cross-compile a package, and other similar tasks.
Get source hash
So anytime something is fetched in a nix expression, we want the hash of the
result (the sha256
attribute) for reproduciblity and security; to know it
hasn’t been changed from what we expect (either from a malicious attack or
simple error) and to make checking if we even need to go fetch anything in the
first place (i.e., we’ve already fetched this version so the source so can just
use the local copy).
There are some nix-prefetch-*
tools to help with getting that hash, the most
commonly used one is probably nix-prefetch-url
.
Just give it a URL (usually to a tarball), it will fetch it, add it to the nix store and print the hash for your expression.
For the hash to use in a fetchTarball
call, you can just the tool directly.
For the hash to use in a fetchFromGitHub
call, you will want to add the
--unpack
flag, as this will check against the hash of the content of the
archive, not the hash of the tarball itself.
For example:
nix-prefetch-url --unpack https://github.com/NixOS/nix/archive/1f795f9f44607cc5bec70d1300150bfefcef2aae.tar.gz
There are other tools, nix-prefetch-git
and nix-prefetch-github
, equivalent
to above would be:
'<nixpkgs>' nix-prefetch-github -c nix-prefetch-github --rev 1f795f9f44607cc5bec70d1300150bfefcef2aae NixOS nix nix run -f
You can also just take another hashOr use lib.fakeSha256
or generate a
random hash to use, e.g., uuidgen | sha256sum
.
, change one character, then
try to build the thing, nix will error telling you the hash it expected, which
you can then use and build again. This is hacky, and best to be avoided.
See the relevant sections in the manual.
LaTeX
Arbitrary, one-off environments:
nix-shell -E 'with import <nixpkgs> {}; mkShell { buildInputs = [(texlive.combine { inherit (pkgs.texlive) scheme-small standalone latexmk cm-super; })]; }'