Skip to content

Speed Up Development with Nix Devshells

Updated: at 02:10 PM

Speed Up Development with Nix Devshells

image

It shouldn’t take a whole day to setup a repo you haven’t worked on before. It shouln’t be that hard to pick up a project you stopped working on 6 months ago because your priorities changed. Enter development shells from nix.

If you haven’t heard about nix before, the tldr is that Nix is a configuration system that solves the problem “It works on my machine”. It is notoriously difficult to get into, but it is very rewarding once you do.

In this post, we will take a look at how we can setup a reproducible development shell with minimal setup so that we can start coding faster!

Table of contents

Open Table of contents

Prerequisites

Run the following command to install nix:

sh <(curl -L https://nixos.org/nix/install)

Flakes are feature of nix that is considered experimental but has been around for many years. Therefore we need to enable this feature.

mkdir -p ~/.config/nix && echo "experimental-features = nix-command flakes " >> ~/.config/nix/nix.conf

What’s a flake?

A nix flake is convenient packaging interface for some nix code typically distributed through a remote repository like github. A flake is basically a software package.

Ready, Set, Go

To create a nix devshell run the following command

nix flake new -t "github:numtide/devshell" project/

This will generate some boilerplate code for getting started with a nix shell

❯ ls -lha
Permissions Size User Date Modified Name
.rw-r--r--   294 kog  27 sep 12:06  .envrc
.rw-r--r--     9 kog  27 sep 12:06  .gitignore
.rw-r--r--    68 kog  27 sep 12:06  devshell.toml
.rw-r--r--  2,8k kog  27 sep 12:06  flake.lock
.rw-r--r--   673 kog  27 sep 12:06  flake.nix
.rw-r--r--   606 kog  27 sep 12:06  shell.nix

Now, simply run

nix develop

And you get this output

 nix develop
warning: updating lock file '/home/kog/repos/coding/demos/project/flake.lock':
 Added input 'flake-compat':
    'github:edolstra/flake-compat/0f9255e01c2351cc7d116c072cb317785dd33b33' (2023-10-04)
🔨 Welcome to devshell

[general commands]

  hello - A program that produces a familiar, friendly greeting
  menu  - prints this menu

[devshell]$

And we are done!

What just happened?

When we ran nix flake new -t github:numtide/devshell we created a nix flake template.

Speed is key

We now have a good template to start from. Let’s make a hello world fast python application!

First, lets add the nixpkgs package repository to the flake

{
  description = "virtual environments";
  inputs.nixpkgs.url = "github:nixos/nixpkgs/nixpkgs-unstable"; # <--- Add this line
  inputs.devshell.url = "github:numtide/devshell";
  inputs.flake-utils.url = "github:numtide/flake-utils";

  inputs.flake-compat = {
    url = "github:edolstra/flake-compat";
    flake = false;
  };

  outputs = {
    self,
    flake-utils,
    devshell,
    nixpkgs,
    ...
  }:
    flake-utils.lib.eachDefaultSystem (system: {
      devShells.default = let
        pkgs = import nixpkgs {
          inherit system;

          overlays = [devshell.overlays.default];
        };
      in
        pkgs.devshell.mkShell {imports = [(pkgs.devshell.importTOML ./devshell.toml)];};
    });
}

Then run nix flake update to commit dependency on nixpkgs. Now we edit devshell.toml to look like this

# https://numtide.github.io/devshell
[[commands]]
help = "Install dependencies"
name = "install"
command = "poetry install"

[[commands]]
help = " ⚡⚡⚡Super Fast Devshells with python!"
name = "faster"
command = "install && poetry run uvicorn fast:app"


[[commands]]

package = "poetry"


[devshell]

packages = [
"poetry"
]

Then add pyproject.toml

[tool.poetry]
name = "fast-devshell"
version = "0.1.0"
description = ""
authors = ["Alexander Reinthal <email@reinthal.me>"]
readme = "README.md"

[tool.poetry.dependencies]
python = "^3.10"
fastapi = "^0.115.0"
uvicorn = "^0.30.6"


[build-system]
requires = ["poetry-core"]
build-backend = "poetry.core.masonry.api"

then add the python application:

from fastapi import FastAPI

app = FastAPI()

@app.get("/")
async def read_root():
    return {"message": "So fast!⚡⚡⚡"}

then, re-enter your shell nix develop if necessary. Finally, run our python app:

faster

project2 on main is 📦 v0.1.0 via 🐍 via ❄️  impure (devshell-env) took 8s
 faster
Creating virtualenv fast-devshell-GWUqcQw5-py3.12 in /home/kog/.cache/pypoetry/virtualenvs
Installing dependencies from lock file

Package operations: 12 installs, 0 updates, 0 removals

  - Installing idna (3.10)
  - Installing sniffio (1.3.1)
  - Installing typing-extensions (4.12.2)
  - Installing annotated-types (0.7.0)
  - Installing anyio (4.6.0)
  - Installing pydantic-core (2.23.4)
  - Installing click (8.1.7)
  - Installing h11 (0.14.0)
  - Installing pydantic (2.9.2)
  - Installing starlette (0.38.6)
  - Installing fastapi (0.115.0)
  - Installing uvicorn (0.30.6)
INFO:     Started server process [306430]
INFO:     Waiting for application startup.
INFO:     Application startup complete.
INFO:     Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
INFO:     127.0.0.1:50032 - "GET / HTTP/1.1" 200 OK
INFO:     127.0.0.1:50032 - "GET /favicon.ico HTTP/1.1" 404 Not Found

Discussion: Why not just use poetry without nix?

Good question! Nix devshells are general and we can setup any environment we like this way, go, rust, c++, zig, ruby. As long as necessary dependencies exist in nixpkgs, which is the worlds largest package repository.

Setting up devshells can be done in many different ways, each programming language usually have their own package pmanager with it’s own quirks. Nix strength lies it being a general. For example, when a project is using multiple programming language, like for example, javascript for the frontend and java for the backend.

Summary and next steps

In this post we setup a fast development shell using nix flakes. We can add lots of functionality to the devshell.toml. We can add environment variables, git-hooks, even daemon services like a postgres database or redis for caching.

For more info on how this particular devshell works check out Numtide Devshell documentation.

I hope you learned something!

Please connect with me on socials if you wanna talk about code! :) See links below this article

References