Check out FlakeHub — the best place to discover and publish Nix flakes, from Determinate Systems.

Sign up for the Determinate Systems mailing list

* indicates required

4. Build a package using Nix

Quick start / Build a package using Nix
Guide 4 of 8
In this guide

Build a Nix package defined in Nixpkgs

Run the package from the local directory

Initialize a flake template in your preferred programming language

Build a Nix package from the flake.nix in the template

While Nix can do many things, package management is the thing that it’s perhaps best known for. In this tutorial, we’ll use our installed Nix CLI to build and run some Nix packages included in Nixpkgs. Later in the guide we’ll build and run a Nix package defined in a local flake.

Build a package from Nixpkgs

Let’s start by building bat, a syntax-highlighted version of cat written in Rust that has a Nix package defined in Nixpkgs, in an empty directory (make sure to run this in a directory where you have write access):

Terminal window
mkdir build-nix-package && cd build-nix-package
nix build "https://flakehub.com/f/NixOS/nixpkgs/*#bat"

Here, nixpkgs is a flake reference to the NixOS/nixpkgs repository on GitHub, while #bat indicates that we’re building the bat output from the Nixpkgs flake.

When the build is done, run ls . and you should see something called result in the current directory. result is actually a symlink to the built package in the Nix store, which you can verify:

Terminal window
readlink result

You should see a path like this (it’s likely to be a bit different on your machine):

What’s happened here is that the Nix CLI has:

  • Downloaded the Nix code in Nixpkgs
  • Found a package definition with the name bat (code here)
  • Used the build instructions for bat to build the package
  • Stored the result in the Nix store using Nix’s hash-based path system

You can now run bat:

Terminal window
./result/bin/bat --help

:rocket: Success! You’ve built and run a package using Nix.

Build a package for tools written in $LANGUAGE

One of the great things about Nix is that package builds are extremely flexible, which enables you to create packages for things written in just about any programming language. In this section, we’ll explore that by building and running packages for tools written in a variety of languages. Select one below to see some examples:

Select your language

Let’s build and run CMake:

Build and then run CMake
nix build "nixpkgs#cmake"
./result/bin/cmake --help

Let’s build and run Pandoc:

Build and then run Pandoc
nix build "nixpkgs#pandoc"
./result/bin/pandoc --version

Let’s build and run npm:

Build and then run npm
nix build "nixpkgs#nodePackages.npm"
./result/bin/npm --help

If you run ls result/bin you’ll notice that the package also includes npx.

Let’s build and run pip:

Build and then run pip
nix build "nixpkgs#python3Packages.pip"
./result/bin/pip --help

Let’s build and run kubectl:

Build and then run kubectl
nix build "nixpkgs#kubectl"
./result/bin/kubectl --help

Let’s build and run ripgrep:

Build and then run ripgrep
nix build "nixpkgs#ripgrep"
./result/bin/rg --help

Let’s build and run scalafmt:

Build and then run scalafmt
nix build "nixpkgs#scalafmt"
./result/bin/scalafmt --version

Beyond Nixpkgs

While Nixpkgs is by far the largest Nix package repository in the known universe, any Nix flake can include package outputs. Let’s build a package from a different repo, this time the package for Home Manager, a popular Nix tool for configuring home environments:

Terminal window
nix build "https://flakehub.com/f/nix-community/home-manager/*"

Here, https://flakehub.com/f/nix-community/home-manager/* is a flake reference to the nix-community/home-manager repo on FlakeHub. To run Home Manager:

Terminal window
./result/bin/home-manager --help

Upstreaming your packages to Nixpkgs is always an option, but it’s good to bear in mind that with Nix you can distribute packages via any public Git repository with a flake.nix.

Build a package in a local flake

Earlier in this guide, we built a Nix package defined in Nixpkgs to get a sense of some of the mechanics of that process. In this guide, we’ll dig a bit deeper and build a Nix package defined in a local Nix flake.

As above, select a preferred language:

Select your language

To get started in your project, create an empty directory and initialize a flake template:

Whichever language you’ve selected, you can build the Nix package defined in the local flake by running:

Terminal window
nix build

This command determines that the local flake has a package output that defines how the package is built. In this particular flake there’s a default package, which enables us to run nix build without specifying an output, but if the package were output as packages.mypkg, for example, we’d need to run nix build .#mypkg to build it.

Here’s the package definition that builds our C++ package:

flake.nix
{
packages = forAllSystems ({ pkgs }: {
default = pkgs.stdenv.mkDerivation {
name = "zero-to-nix-cpp";
src = self;
buildInputs = with pkgs; [ boost gcc poco ];
buildPhase = ''
c++ -std=c++17 -o zero-to-nix-cpp ${./main.cpp} -lPocoFoundation -lboost_system
'';
installPhase = ''
mkdir -p $out/bin
cp zero-to-nix-cpp $out/bin/
'';
};
});
}

For the full flake, see flake.nix on GitHub or run cat flake.nix. What you see here is a derivation that defines how to build the package, more specifically the mkDerivation function provided by Nix’s standard environment.

The package that results when you run nix build is a CLI tool that outputs a message. To run that tool:

Run the compiled binary
./result/bin/zero-to-nix-cpp

You should see this output:

Expected output
Hello from Nix + C++!

Here’s the package definition that builds our Haskell package:

flake.nix
{
packages.default = pkgs.haskellPackages.mkDerivation {
pname = "zero-to-nix-haskell";
version = "0.1.0";
src = self;
license = pkgs.lib.licenses.cc-by-sa-40;
executableHaskellDepends = with pkgs.haskellPackages; [
base
];
};
}

For the full flake, see flake.nix on GitHub or run cat flake.nix. What you see here is a derivation that defines how to build the package, more specifically the haskellPackages.developPackage function provided by Nixpkgs.

The package that results when you run nix build is a CLI tool that outputs a message. To run that tool:

Run the compiled binary
./result/bin/zero-to-nix-haskell

You should see this output:

Expected output
Hello from inside a Haskell program built with Nix!

Here’s the package definition that builds our JavaScript package:

flake.nix
{
packages.default = pkgs.buildNpmPackage {
name = "zero-to-nix-javascript";
# The packages required by the build process
buildInputs = [
pkgs.nodejs_18
];
# The code sources for the package
src = self;
npmDepsHash = "sha256-8Bj7nPZBAU+SnVTeKeArcjYnfZV4z/4I7fX+l0V+v04=";
# How the output of the build phase
installPhase = ''
mkdir $out
npm run build
cp dist/index.html $out
'';
}
}

For the full flake, see flake.nix on GitHub or run cat flake.nix. What you see here is a derivation that defines how to build the package, more specifically the buildNpmPackage function, which is a wrapper around Nix’s built-in derivation function.

The package that results when you run nix build is a website built using the [Vite] framework. To view that website, open the HTML file at result/index.html.

Here’s the package definition that enables us to build our Python package:

flake.nix
{
packages.default = python.pkgs.buildPythonApplication {
name = "zero-to-nix-python";
src = self;
buildInputs = with python.pkgs; [ pip ];
};
}

For the full flake, see flake.nix on GitHub or run cat flake.nix. What you see here is a derivation that defines how to build the package, more specifically the buildPythonApplication function, which is a wrapper around Nix’s built-in derivation function.

The resulting package is an executable that prints to the terminal. To run the package:

Terminal window
./result/bin/zero-to-nix-python

You should see this terminal output:

Hello from inside a Python program built with Nix!

Here’s the package definition that enables us to build this Go package:

flake.nix
{
packages.default = pkgs.buildGoModule {
name = "zero-to-nix-go";
src = ./.;
vendorSha256 = "sha256-fwJTg/HqDAI12mF1u/BlnG52yaAlaIMzsILDDZuETrI=";
};
}

For the full flake, see flake.nix on GitHub or run cat flake.nix. What you see here is a derivation that defines how to build the package, more specifically the buildGoModule function, which is a wrapper around Nix’s built-in derivation function.

The package that results when you run nix build is a web server built using the Gin framework. To run the package:

Run the compiled binary
./result/bin/zero-to-nix-go

In another window, run curl http://localhost:8080 to receive a message from the server.

Here’s the package definition that enables us to build this Rust package:

flake.nix
{
packages.default = pkgs.rustPlatform.buildRustPackage {
name = "zero-to-nix-rust";
src = ./.;
cargoLock = {
lockFile = ./Cargo.lock;
};
}

For the full flake, see flake.nix on GitHub or run cat flake.nix. What you see here is a derivation that defines how to build the package, more specifically the buildRustPackage function, which is a wrapper around Nix’s built-in derivation function.

To run the resulting package, which is an executable that prints to the terminal:

Terminal window
./result/bin/zero-to-nix-rust

You should see this terminal output:

Hello from Nix + Rust!

We’re not gonna lie to you: packaging Scala is pretty tricky. Fortunately, there’s a third-party project called sbt-derivation that provides some nice helpers, including a handy function called mkSbtDerivation, which is a wrapper around Nix’s built-in derivation function.

For the full flake behind this package, see flake.nix on GitHub or run cat flake.nix. To run the resulting package, which is an executable that prints to the terminal:

Terminal window
./result/bin/zero-to-nix-scala

You should see this terminal output:

Hello from Nix + Scala!

We won’t delve too much deeper into derivations and creating your own packages here, but we hope that this guide shows you how Nix code gets turned into real build output.