Skip to content

Instantly share code, notes, and snippets.

@grossbart
Last active April 23, 2024 06:59
Show Gist options
  • Star 7 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save grossbart/9d0d4b2a32095e82aec74c202e4454c2 to your computer and use it in GitHub Desktop.
Save grossbart/9d0d4b2a32095e82aec74c202e4454c2 to your computer and use it in GitHub Desktop.
Nix on macOS

Nix

Nix is a powerful package manager that makes package management reliable and reproducible. It provides atomic upgrades and rollbacks, side-by-side installation of multiple versions of a package, multi-user package management and easy setup of build environments.

Installation

Installing Nix is easy and straightforward; follow the installation instructions for macOS.

Uninstalling Nix

Nix is also very painless to remove because all its dependencies live in /nix and if you remove that folder, your system will be free of Nix again. This means there is no reason not to try it.

Usage

If you have Nix you don’t really need Homebrew or similar tools anymore. Not all packages are available in Nix, but so far I haven't encountered any that I've been missing in my daily workflow.

Commands

Start a Nix environment

👉 This is the most used command. Projects that have a Nix configuration can be run very easily if you have Nix installed. Just make sure that whenever you open a new terminal window and navigate to the project’s root folder, you run

nix-shell

After this you can run all the project’s commands, e.g. make or yarn add … and be sure that all the tools that this project needs are available.

Search for a package

This looks overly complex, which is why we document it here. Use this to get hold of the package name that you can then use to install or reference it.

nix-env -qaP .*iconv.*

Try out a package

If you just want to try out a package or need to run a command only once, start a shell with the package available:

nix-shell -p yarn

Install a global package

This installs yarn globally, making it available in all terminal sessions:

nix-env -i yarn

Upgrade your Nix installation

You should update your Nix from time to time using the following commands. For projects that have a shell.nix file to describe their environment, this is not necessary, this is only for your global packages (and the Nix binary).

nix-channel --update
nix-env -u --always
# Then, if everything works fine
nix-collect-garbage -d

For Developers

Nix Expressions

We rarely want to have binaries installed globally, because that is only specific to a single machine and another team member might not have the required binaries available; this is where Nix expressions come in.

A simple shell.nix

Create a shell.nix file in your project’s root directory with the following contents:

let
  pkgs = import <nixpkgs> {};
  stdenv = pkgs.stdenv;
in rec {
  project = stdenv.mkDerivation rec {
    name = "my-node-application";
    buildInputs = [
      pkgs.nodejs
      pkgs.yarn
    ];
  };
}

Then, still from your project’s root directory, run

nix-shell

to get into a shell environment with all the specified dependencies available. You’re ready to work!

Recommended: pinning shell.nix

The above example is not entirely deterministic because different people might have different Nix channels on their machines, leading to slightly different dependencies being installed. To pin your packages to an exact version, you need to follow these steps:

Start a shell with a helper script in path

nix-shell -p nix-prefetch-scripts

Fetch the current Nix git repository’s revision and sha256 hash

nix-prefetch-git --no-deepClone --url https://github.com/NixOS/nixpkgs.git

Returns something like this:

{
  "url": "https://github.com/NixOS/nixpkgs.git",
  "rev": "910b5cda19d29b1bdab3c88bd0cc5bbf29520590",
  "date": "2018-01-06T16:57:58+08:00",
  "sha256": "1sf9chk6jsmyh3bq122fl7c75yr5dbl3c7brfw737aq1skvcgkyh",
  "fetchSubmodules": true
}

Create your shell.nix file and use the information from above

let
  unpinned = import <nixpkgs> {};
in
{ pkgs ? import (unpinned.fetchFromGitHub
  { owner = "NixOS";
    repo = "nixpkgs";
    rev = "910b5cda19d29b1bdab3c88bd0cc5bbf29520590";
    sha256 = "1sf9chk6jsmyh3bq122fl7c75yr5dbl3c7brfw737aq1skvcgkyh";
  }) {}
}:

pkgs.stdenv.mkDerivation rec {
  name = "example-project-with-gdal-ruby-node";
  buildInputs = [
    pkgs.gdal
    pkgs.ruby_2_1
    pkgs.bundler
    pkgs.nodejs
    pkgs.yarn
  ];
}

More info can be found on the Nix wiki about Pinning Nixpkgs.

Use a historic Nix release

It can be necessary to specify an older project’s dependencies as they were at the time the project was created and not as they are today. For this you can point to a specific revision of the Nix packages repo like so (note the --rev, i.e. revision, option)

nix-prefetch-git --no-deepClone --url https://github.com/NixOS/nixpkgs.git --rev 6ed8a76ac64c88df0df3f01b536498983ad5ad23

This is an example that points to the Nix release 0.14 from 2012.


Recipes

Better shell scripts with Nix

Nix can be specified as a shell interpreter so you can write shell scripts in your favourite language. Here’s an example:

#! /usr/bin/env nix-shell
#! nix-shell -i python -p python pythonPackages.prettytable

import prettytable

# Print a simple table.
t = prettytable.PrettyTable(["N", "N^2"])
for n in range(1, 10): t.add_row([n, n * n])
print t

Replace Docker with Nix

Nix can also replace Docker: https://lethalman.blogspot.ch/2016/04/cheap-docker-images-with-nix_15.html


About Nix

The benefits

  • You can have multiple versions of a package installed without problems
  • Upgrading or uninstalling packages can’t break other packages since there is no mutation hell
  • Nix helps you make sure that package dependency specifications are complete
  • Atomic upgrades and rollbacks – when an upgrade breaks things, you can just nix-env --rollback to get back to the previous state
  • Packages are built from Nix expressions, which is a simple functional language. This makes building deterministic and building a Nix expression twice should yield the same result
  • Nix can skip building from source and instead use a binary cache with pre-built binaries Toolchain

There are several tools in the Nix ecosystem:

  • Nixpkgs – The Nix Packages collection
  • NixOps – The NixOS Cloud Deployment Tool
  • NixOS – The Purely Functional Linux Distribution
  • nix-darwin – /etc/nixos/configuration.nix for macOS (experimental)

How it works

  • Nix treats packages like immutable values, built by functions that don’t have side-effects
  • Packages are stored in the Nix store, usually at /nix/store
  • Each package has a unique subdirectory, e.g. /nix/store/mlhw8gn5vc8p6cqf00bp53dqkbm35kd0-nodejs-7.2.1
  • To make these packages available to you, Nix references the packages in the store that belong together with symlinks
  • Global packages are symlinked into ~/.nix-profile, you add this to your path with source ~/.nix-profile/etc/profile.d/nix.sh
  • Project specific package sets can be specified with a shell.nix file and will be accessible if you run nix-shell
  • To remove Nix from your system, you just delete /nix and everything is gone 💥 Caveats
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment