Switching from Homebrew to Nix

The Nix package manager has a number of unique features that differentiate it from Homebrew. These enable it to provide a much more stable and smoother installation experience when managing different environments and dependencies.

The smoother experience is due to Nix using binaries when installing software. This eliminates the need to compile from source.
Increased stability is supported through a generation system. Each new package installation creates a new generation of your environment which can be easily rolled back should an error occur.

Nix also supports creating isolated development environments. These isolated development environments also have their own generation roll back system. This allows you to completely compartmentalize your system into many stable custom environments.

Nix Package Manager Features

The following are some of the differences compared to Homebrew and how they can make the user experience a lot more pleasant.

  • Nix channels contain previously compiled binary versions of a package from where downloads are sourced. This means that you do not need to download and compile a package, its dependencies, and pray, but instead you download and untar a working tarball from a channel. Use the following command to view your download channels:

    brian@rhodecode:~# nix-channel --list nixpkgs http://nixos.org/channels/nixpkgs-unstable

  • Nix --rollback and --switch-generation functions provide the additional stability. Each package installation creates a new generation of your Nix environment. This means that if a download breaks a build you simply rollback to the last previous working generation.

  • The nix-shell is used to provide isolated environments.

Making the Switch to Nix

To switch package handling from Homebrew to Nix, use the following steps:

  1. Find out which packages you currently manage with homebrew:

      $ brew list
      openssl python treadline sqlite
  2. Remove these packages from your system.
    brew remove package-name

  3. Remove Homebrew using the following steps:

    cd brew --prefix
    rm -rf Cellar brew 
    prune rm git ls-files
    rm -r Library/Homebrew Library/Aliases Library/Formula Library/Contributions
    rm -rf .git
    rm -rf ~/Library/Caches/Homebrew` 
  4. Install Nix on your operating system with the following command:

    $ bash <(curl https://nixos.org/nix/install)

    Note: the installer sources a Nix profile in the ~/.profile or ~./bash_profile file by adding the following line:

    if [ -e /Users/brian/.nix-profile/etc/profile.d/nix.sh ]; 
    then . /Users/brian/.nix-profile/etc/profile.d/nix.sh; fi 
    # added by Nix installer

    If during installation this is not created, you should add it manually to the appropriate file.

  5. Once Nix is downloaded. Reinstall the packages you require with the Nix install command:

    # download package-name
    nix-env --install package-name  
    # see all available packages
    nix-env --query --available

For more information, see Nix Installing Packages

Note: Package names in Homebrew and Nix may not be the exact same, but should be similar. You can view all package names using this command:

brian$ nix-env -qaP

Installing with Nix

Once you have reconfigured your package manager to Nix, you can see where the default packages are using the which command, and rollback nix-env --rollback or switch generation nix-env --switch-generation generation-number.

brian@rhodecode:~# which python/usr/bin/python 
brian@rhodecode:~# nix-env -i python
installing `python-2.7.8'
building path(s) /nix/store/iqm4bnb1assfdwka4xmmky96yrnhj91w-user-environment'
created 263 symlinks in user environment
brian@rhodecode:~# which python

When Installs Go Bad

This is where Nix comes into a league of its own. If an install wrecks your development environment, you can simply rollback to the previous working one.

Additionally, the Nix Garbage collection function allows you to quickly clean up your environment once you have carried out a number of changes:

nix-collect-garbage -d

See the following example for an explanation of these functions:

[nix-shell:~]$ nix-env -i python3 # Install Python 3
replacing old `python3-3.4.1'\n
installing `python3-3.4.1'\n
[nix-shell:~]$ which python3 # based on latest gen\n/root/.nix-profile/bin/python3\n
[nix-shell:~]$ nix-env --list-generations\n   
1   2014-07-27 10:18:50    
2   2014-07-27 10:31:27      
3   2014-07-27 14:06:42      
4   2014-07-27 14:09:27      
5   2014-07-27 14:16:44      
6   2014-07-27 14:16:53      
7   2014-07-28 08:13:07      
8   2014-07-28 08:20:00      
9   2014-07-28 08:21:42     
10   2014-07-28 08:22:31  (current)
[nix-shell:~]$ nix-env --switch-generation 4 
switching from generation 10 to 4
[nix-shell:~]$ which python3 # based on gen 4
[nix-shell:~]$ nix-env --switch-generation 10
switching from generation 4 to 10
[nix-shell:~]$ nix-collect-garbage -d 
removing old generations of profile /nix/var/nix/profiles/default
removing generation 1
removing generation 9
removing old generations of profile /nix/var/nix/profiles/per-user/root/channels\nfinding garbage collector roots...
deleting garbage...
875 store paths deleted, 552.07 MiB freed
[nix-shell:~]$ nix-env --list-generations  
10   2014-07-28 08:22:31   (current)
[nix-shell:~]$ exit

Uninstalling with Nix

To remove a package with Nix, use the -e flag. For example:

nix-env -e package-name

Nix will gracefully handle the uninstall and also remove any symlinks created to support the package:

brian@rhodecode:~# nix-env -e git
uninstalling `git-2.0.2'\nbuilding path(s) `/nix/store/vz37m6k23r7j5mnvmij92bnym6j16cbl-user-environment'
created 6 symlinks in user environment

Setting Up an Isolated Environment

The default.nix file is used to define an environment. Each system can have multiple default.nix files which create independant work areas. This enables multiple different package versions to be installed and run on the same machine without introducing dependency conflicts.

Additionally, this allows very easy environment sharing because you can share your default.nix file, and colleagues will have an exact copy of your setup.

Configuring default.nix

Configuring a default.nix file requires declaring your setup within the file. This is why it is called a declarative setup.

Every time you run nix-shell, Nix will ensure that you get a consistent set of binaries corresponding to your specification.

For specific projects you should install the packages you need in a project folder that also contains a default.nix file. This way project specific packages do not confllict with system packages.

To create a project specific work area use the following steps:

  1. Create a new folder mkdir new-project
  2. Navigate to that folder and create a default.nix file. Within this file you must specify the particular packages for that work environment. Use the following examples to create your own default.nix file. This example default.nix file specifies the required packages in the buildInputs section.

  3. In this example, the rhodecode-docs project requires:

    { system ? builtins.currentSystem
    pkgs = import <nixpkgs> { inherit system; };
    inherit (pkgs) fetchurl fetchgit;
    buildPythonPackage = pkgs.python27Packages.buildPythonPackage;
    python = pkgs.python27Packages.python;
    Jinja2 = buildPythonPackage rec {
      name = "Jinja2-2.7.3";
      src = fetchurl {
      url = "http://pypi.python.org/packages/source/J/Jinja2/${name}.tar.gz";
      md5 = "b9dffd2f3b43d673802fe857c8445b1a";
      propagatedBuildInputs = [ MarkupSafe ];  
    MarkupSafe = buildPythonPackage rec {
      name = "MarkupSafe-0.19";
      src = fetchurl {      
      url = "https://pypi.python.org/packages/source/M/MarkupSafe/${name}.tar.gz";
      md5 = "ccb3f746c807c5500850987006854a6d";
    Pygments = buildPythonPackage rec {
      name = "Pygments-1.6";
      src = fetchurl {      
      url = "https://pypi.python.org/packages/source/P/Pygments/${name}.tar.gz";      
      md5 = "a18feedf6ffd0b0cc8c8b0fbdb2027b1";
    Sphinx = buildPythonPackage (rec {    
      name = "Sphinx-1.2.2";   
      src = fetchurl {      
      url = "http://pypi.python.org/packages/source/S/Sphinx/${name}.tar.gz";      
      md5 = "3dc73ccaa8d0bfb2d62fb671b1f7e8a4";    
    propagatedBuildInputs = [ docutils Jinja2 Pygments ];
    docutils = buildPythonPackage rec {   
      name = "docutils-0.8.1";    
      src = fetchurl {      
      url = "https://pypi.python.org/packages/source/d/docutils/${name}.tar.gz";
      md5 = "2ecf8ba3ece1be1ed666150a80c838c8";
    in python.buildEnv.override {
    inherit python;  
    extraLibs = [ Sphinx ];

Using nix-shell Functionality

Once your environment is declared in the default.nix file you can use the nix-shell functionality to create your isolated development environments. Both of the following examples carry out the same function of creating a new Sphinx installation.

  • To issue a single command from the nix-shell you can issue it like this:

    brian$ nix-shell --command=sphinx-quickstart
  • To develop within the nix-shell, switch into it and execute the specific environment commands from within.

    brian@rhodecode:~/nix-project# vi default.nix
    brian@rhodecode:~/nix-project# nix-shell
    these paths will be fetched (2.59 MiB download, 14.76 MiB unpacked):
    Downloading `http://cache.nixos.org/nar/0xhnlcz9f1wak7dhhrnznbgbjxqimkk0wip7q9w38xnllw7dmhgn.nar.xzā€™ to ā€˜/nix/store/q486ypcjr6kgkw8c62574dqp97sgh9d0-python2.7-Sphinx-1.2ā€™...
    [nix-shell:~/nix-project]$ sphinx-quickstart
    Welcome to the Sphinx 1.2 quickstart utility
    [nix-shell:~/nix-project]$ exit
  • Note: You need to navigate to where you have the default.nix file stored.


Once you have carried out these steps you will be able to manage your environments much more easily.