My Nix Journey - How to Use Nix to Set Up Dev Environment

In my previous post, I briefly covered using nix develop command to set up dev enviromnent. In today’s post, I would like to show you how you can use my templates and then use nix develope or nix-direnv to set up the environment for a project.

Let’s get started.

flake templates

I’ve created a GitHub repo to help you start.

1
2
3
4
5
6
7
8
➜ nix flake show github:liyangau/flake-templates
github:liyangau/flake-templates/c6e2bb22212027d16e4bc9eac40c55e65c4f581f
├───defaultTemplate: template: Use packages available on nixpkgs
└───templates
├───local: template: Use local package default.nix
├───nur: template: Use packages available on NUR
├───python: template: Create python 3.12 environment with libraries
└───shell: template: Use packages available on nixpkgs

Currently there are 4 templates and I have detail instructions on how to use these template in the repo. Here, I’d like to go a little deeper on how to use the local package template.

Local packages

When I first start using Nix, I was astonished by the extensive range of packages in nixpkgs. However, it wasn’t long before I encountered the issue of certain packages not being included in both nixpkgs and NUR.

What should we do if the package isn’t found in either Nixpkgs or NUR? You might see some suggesting writing a derivation, but let’s be honest, who wants to spend time learning/writing something completely unfamiliar when all they need is a CLI tool? Especially these CLI tools can be easily installed either by homebrew or curl most of the time. Is it possible to generate this derivation automatically? My workflow involves using nix-init to create the package file and then the nix build command for make sure the pacakge file works.

NIX_PATH

If NixOS isn’t your operating system, chances are you don’t have nixpkgs in your NIX_PATH. Consequently, certain commands might fail. In such scenarios, you can manually set the environment variable before executing the desired commands. elow environment variable use nixpkgs unstable channel to execute your commands.

1
NIX_PATH="nixpkgs=flake:nixpkgs"

You can use it as NIX_PATH="nixpkgs=flake:nixpkgs" nix-init. B

nix-init

If you’ve inspected some Nix package files, you are likely notice there are some hash values you need to enter. nix-init simplifies the process of generating Nix packages file from URLs and prefetching hashes automatically.

Currently, it supports the following builders:

  • stdenv.mkDerivation
  • buildRustPackage
  • buildPythonApplication and buildPythonPackage
  • buildGoModule

I hope that in the future, it will also support buildNpmPackage or buildYarnPackage.

To use nix-init, follow these steps:

  1. Install it either by putting the package on your configuration.nix or use nix shell as shown below:
1
nix shell nixpkgs#nix-init
  1. Run nix-init ad follow the prompt to generate this default.nix file. Here’s an example of how I used nix-init to generate a package file for deck. TThe only input required was the repository URL. The tool automatically selects the best builder, uses the latest version, and outputs to the current directory by default.
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    /tmp/test 
    ➜ nix-init
    Enter url
    ❯ https://github.com/Kong/deck
    Enter tag or revision (defaults to v1.37.0)
    ❯ v1.37.0
    Enter version
    ❯ 1.37.0
    Enter pname
    ❯ deck
    How should this package be built?
    ❯ buildGoModule
    Enter output path (leave as empty for the current directory)
    ❯ .
    s
    /tmp/test
    ls default.nix
    default.nix

nix build

nix build can be used to build a derivation, in this case, we use it to test the genearted default.nix.

Let’s execute the following command:

1
2
3
nix build \
--impure --expr \
'let pkgs = import <nixpkgs> { }; in pkgs.callPackage ./default.nix {}'

If the package got built successfully, you should observe a symlink to the nix store path, as shown below:

1
2
ls -la result
lrwxrwxrwx 1 fomm users 55 May 14 01:10 result -> /nix/store/xmwh9cr6k6a2bhhhn39gy7hbsrsrsmmg-deck-1.37.0

The result folder structure is as below and we can use the CLI via ./result/bin/<CLI_NAME>.

1
2
3
4
5
6
7
➜ tree result
result
└── bin
├── deck
└── docs
➜ ./result/bin/deck version
decK v1.37.0 (v1.37.0)

Use template

Now that we have the package file default.nix, we can utilize my flake template to incorporate and utilize this package for our project. First, execute the following command to generate flake.nix in our current directory:

1
nix flake init --template github:liyangau/flake-templates#local

Then we can use nix develop to spawn a shell with the package loaded:

1
nix develope -c $SHELL

This action generates a flake.lock file in the directory and provides us with a shell containing the loaded package. If you need a different version of the CLI when invoking nix-init, as demonstrated below where a new default.nix for v1.36.1 version was created, the environment will reflect this version. This capability is particularly useful if you wish to pin your CLI tool to a specific version for the project:

1
2
3
4
5
➜ nix develop -c $SHELL
warning: creating lock file '/tmp/test/flake.lock'
/tmp/test via  (nix-shell-env)
➜ deck version
decK v1.36.1 (v1.36.1)

nix-direnv

If you prefer the package to be automatically loaded when you cd to your project folder, you can use nix-direnv. From above step or use my flake template, we already have a flake.nix in my project folder. Here’s how to proceed once you’ve installed nix-direnv:

  • Create a .envrc file in current folder and add use flake to it. You can accomplish this with the following command as well:
    1
    echo 'use flake' > .envrc
  • Enable auto-loading for the current folder:
    1
    direnv allow

That’s all I want to share with you today and I hope it helps you to use nix better.

See you on the next one.