Skip to content

A quickstart and reference for Nix to help you get up and running fast.

License

Notifications You must be signed in to change notification settings

chingc/tutorial-nix

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

3 Commits
 
 
 
 

Repository files navigation

README: Nix

Hello, this is a record of my journey learning Nix.

I'm writing this mainly as a quickstart and reference for my future self, but I hope others find it helpful.

This is not comprehensive. It will just go over the basics and some of the more useful features.

Note: This doc will be using the newer nix cli interface and flakes.

What is it?

Nix is a purely functional package manager. It helps to avoid the installation and versioning pain points of software development.

That doesn't sound like much. However...

Suppose you need golang, node, and python. You can create a shell environment that has those tools with a single command.

$ go; node; python
zsh: command not found: go
zsh: command not found: node
zsh: command not found: python

$ nix shell nixpkgs#go nixpkgs#nodejs nixpkgs#python3

$ go version
go version go1.23.3 darwin/arm64

$ node --version
v20.18.0

$ python --version
Python 3.12.7

$ exit

$ go; node; python
zsh: command not found: go
zsh: command not found: node
zsh: command not found: python

We were able to get access to golang, node, and python instantly. Actually, the initial run takes a few moments, but subsequent runs are fast!

We skipped the setup docs, we didn't edit our shell profile, and we didn't have to worry about PATH or other environment variables.

Best of all, we didn't pollute the user environment. Everything is gone once you exit or Ctrl-D the shell.

Other benefits?

Nix builds packages in isolation from each other. This ensures that they are reproducible and don't have undeclared dependencies, so if a package works on one machine, it will also work on another.

Nix makes it trivial to share development and build environments for your projects, regardless of what programming languages and tools you're using.

Install

The easiest way to install Nix (Linux, macOS, WSL2) is to use The Determinate Nix Installer.

It installs Nix with flake support and the unified CLI feature already enabled. It also stores a receipt for the install process to allow for a clean uninstall.

Note: Directions to upgrade or uninstall can be found in their GitHub repo.

Run Programs Directly

We've already seen how a shell environment can be created with nix shell.

With nix run we can skip creating the shell and run programs directly.

$ nix run nixpkgs#cowsay Hello, Nix!

$ nix run nixpkgs#lolcat -- --help

$ nix run nixpkgs#fortune | nix run nixpkgs#lolcat

Reproducible Scripts

A trivial script with non-trivial dependencies.

#! /bin/bash

curl https://github.com/NixOS/nixpkgs/releases.atom | xml2json | jq .

This script fetches XML content from a URL, converts it to JSON, and formats it for better readability.

It requires curl, xml2json, jq, and bash. If any of these dependencies are not present on the system running the script, it will fail partially or altogether.

With Nix, we can declare all dependencies explicitly, and produce a script that will always run on any machine that has Nix.

#! /usr/bin/env nix
#! nix shell nixpkgs#bash nixpkgs#curl nixpkgs#jq nixpkgs#python312Packages.xmljson
#! nix --ignore-environment --command bash

curl https://github.com/NixOS/nixpkgs/releases.atom | xml2json | jq .
  • The --ignore-environment option prevents the script from implicitly using programs that may already exist on the system.
  • The --command option specifies the interpreter that will be invoked by nix shell after it has obtained the dependencies and initialized the environment.

Notice how nix shell was only specified once when using multiple nix shell shebangs.

As long as the system has Nix, scripts can be written in any language without worrying about dependencies.

#! /usr/bin/env nix
#! nix shell nixpkgs/nixos-24.11#python3
#! nix --ignore-environment --command python

print("n", "n^2")
for n in range(1, 10):
    print(n, n * n)

In this example we've specified the nixos-24.11 stable channel. This helps reproducibility. You can even use a git commit hash of the Nixpkgs repository for further granularity.

Declarative Shell Environments

We can create a file that defines an environment. This can be shared with anyone to recreate the same environment on a different machine.

Create a flake.nix file:

{
    description = "A fun shell environment";

    inputs = {
        nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
        flake-utils.url = "github:numtide/flake-utils";
    };

    outputs = { self, nixpkgs, flake-utils }:
        flake-utils.lib.eachDefaultSystem (system:
            let
                pkgs = import nixpkgs { inherit system; config = {}; overlays = []; };
            in {
                devShells.default = pkgs.mkShellNoCC {

                    # Packages to include
                    packages = with pkgs; [
                        cowsay
                        lolcat
                        python3Full
                    ];

                    # Define environment variables
                    env = {
                        GREETING = "Hello, Nix!";
                    };

                    # Run a shell script on environment startup
                    shellHook = ''
                        echo $GREETING | cowsay | lolcat
                    '';

                };
            }
        );
}

Enter the environment by running nix develop in the same directory as flake.nix.

Note: If the directory you're working in is a git repository you may get an error indicating flake.nix doesn't exist. Stage the file with git add to get nix to recognize it. I don't know. It's weird like that.

If you make changes to flake.nix just exit or Ctrl-D to exit the environment and restart it with nix develop.

More on Versioning

Without pinning or locking your tools and dependencies to specific versions you'll eventually hit the point of development where a bug is affecting others but somehow it "works on my machine". It's often very unpleasant and very difficult to debug.

We've seen from an earlier example how to pin to a specific Nix channel. The channels can be found at status.nixos.org.

Tip: Which channel branch should I use?

Search for Packages

Go to https://search.nixos.org/packages or use the command line.

$ nix search nixpkgs neovim

Wrapped vs Unwrapped

The key difference between wrapped and unwrapped packages in NixOS is that wrapped packages are configured to work seamlessly within the NixOS environment, while unwrapped packages are the raw, unmodified versions.

In most cases, users should install the wrapped version of a package, as it is preconfigured to work correctly on NixOS. The unwrapped version is primarily used when further customization or overriding of the package is required, as it serves as the base for creating a new wrapped derivation with additional modifications.

Thoughts

tbd

References

Below are some of the main sources I've used.

Additional resources:

About

A quickstart and reference for Nix to help you get up and running fast.

Topics

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published