This guide takes you through your first steps with the Nix package manager.
While NixOS is a Linux distribution based on Nix, you can install Nix on other Linux distributions, MacOS and Windows via WSL using the install script from our website:
curl -L https://nixos.org/nix/install | sh
should we link to GPG verification for concerned users?
Check that the installation was successful by running
nix-channel --list
This shows you the package distribution channel used by Nix. By default, this is https://nixos.org/channels/nixpkgs-unstable
Nix can create reproducible environments given a declarative configuration called a Nix expression. Reproducible means you can share the configuration with others and guarantee that they are using the same software as you.
To get started, create a file called shell.nix
in a new folder with the following
contents:
with import <nixpkgs> {};
mkShell {
buildInputs = [
which
htop
];
}
To enter this environment, use nix-shell
inside your folder
nix-shell
The command will start downloading the missing packages from the cache. This may take a few moments. When it is done, you are dropped into a new shell. This shell contains makes the packages you specified in the config available.
Run htop
to confirm it is present. You can quit by pressing q
.
Next run which htop
to check where the htop
command is on-disk. You should
see something similar to this:
/nix/store/y3w2i8kfdbfj9rx287ad52rahjpgv423-htop-2.2.0/bin/htop
This is the path to the binary in the Nix store. Nix installs all packages into the store using a combination of its hash, name and version.
You can search for available packages using nix search
, for example:
nix search python3
nix search nodejs
nix search ghc
nix search cargo
Python is used as an example here. But we could let the reader choose between different tech stacks and application domains.
As an exercise, let us build a Python web application using the Flask web framework.
Create a new file default.nix
. This is file is conventionally used for
specifying packages
{ pkgs ? import <nixpkgs> {} }:
pkgs.python3Packages.buildPythonApplication {
pname = "myapp";
src = ./.;
version = "0.1";
propagatedBuildInputs = [ pkgs.python3Packages.flask ];
}
You will also need a Flask app as main.py
:
#! /usr/bin/env python
from flask import Flask
app = Flask(__name__)
@app.route("/")
def hello():
return "Hello, Nix!"
if __name__ == "__main__":
app.run(host='0.0.0.0')
and a setup.py
script:
from distutils.core import setup
setup(name='myapp', version='0.1', scripts=['main.py'])
Now build the package with:
nix-build
This will create a symbolic link result
to our package's path in the Nix
store, which looks like /nix/store/6i4l781jwk5vbia8as32637207kgkllj-myapp-0.1
.
Look around to see what is inside ;)
You may notice we can run the application from the package like
./result/bin/main.py
. We can still use the default.nix
as a shell
environment to get the same result:
nix-shell default.nix
python3 main.py
In this context, Nix takes on the role that you would otherwise use pip or virtualenv for. Nix installs required dependencies and separates the environment from others on your system.
You can check this Nix configuration into version control and share it with others to make sure you are all running the same software. Especially with many dependencies this is a great way to prevent configuration drift between different team members & contributors.
For this section we assume you have Docker installed.
Now that you have package a Flask app, you may want to deploy it. One way to do this is to package it as a Docker container that can be hosted on a container hosting platform.
Nix comes with tools for building Docker containers. To turn our Python app into a Docker container, we only have to specify what command to run. Nix will take care of building a minimal image required to make that happen.
Create a file docker.nix
:
{ pkgs ? import <nixpkgs> {} }:
let
app = pkgs.callPackage ./default.nix {
inherit pkgs;
};
in pkgs.dockerTools.buildImage {
name = "myapp";
config = {
Cmd = [ "${app}/bin/main.py" ];
};
}
We are calling the expression under ./default.nix
and pass it our own pkgs
.
Under Cmd
we use the variable app
to retrieve the path to the executable
Python app.
Now we can build the Docker image and load it into Docker:
nix-build docker.nix
docker load --input result
This should give you an image name like
myapp:hg1wd1wbj9sl9kvl9y5f3hs4pmix4vsa
. Use this to run the Docker image. Also
make sure to forward port 5000, so you can access the app in the browser:
docker run -it -p 5000:5000 myapp:hg1wd1wbj9sl9kvl9y5f3hs4pmix4vsa
Now you can see the site on http://localhost:5000
.
- We could leave out
shell.nix
and just work ondefault.nix
for everything. There are some different ways of setting this up, this guide should focus on the most straightforward approach. - How much Nix code can we show without starting to teach the language?
- How do we accomodate multiple tech stacks? We should showcase tech stacks that we can handle well, but their could be multiple to choose from.
- Can we rely on people having Docker installed?