Nix Quick Install
Install Nix using the
.pkgfrom Determinate Systems.Create a
flake.nixfile in the project's root directory (see below).Activate the development environment with
nix-develop.Update dependencies with
nix flake updateas needed.
Direnv & Nix-Direnv Quick Install
# Install Direnv
# Install nix-direnv
# Configure direnv to use nix-direnv
# Modify shell config:
# For Fish, add this to ~/.config/fish/config.fish
|
# For Zsh, add this to ~/.zshrc
# Reload the shell config
What's Nix?
Nix is a declarative package manager. You declare your project's
dependencies in a flake.nix file, and Nix builds that environment
with nix develop. Unlike the chaos of "works on my machine"
development, Nix guarantees everyone gets the same environment. No
more version conflicts, no more missing dependencies, no more "did you
remember to install X?"
Nix pairs nicely with nix-direnv. When you switch directories, your environment switches with you. It's honestly kind of magical.
The name Nix is overloaded: it's an OS, a programming language, and a package manager. Over the years, there have been multiple ways to set things up.
This method installs the Nix package manager and uses a flake.nix
file in each project's root directory. It also uses nix-direnv to
automatically run nix develop.
Setting Up Nix
Download the .pkg installer from Determinate Systems. This is the
recommended way to install Nix on macOS because it survives system
upgrades. The installer handles all the setup for you - no manual
configuration needed.
Setting Up Direnv
Direnv automatically loads and unloads your environment when you
cd into and out of project directories. Combined with
nix-direnv, it caches your Nix environments so they load instantly
instead of rebuilding every time.
First, install direnv with Homebrew:
Then install nix-direnv and configure it:
Hook direnv into your shell. For Fish:
For Zsh, add to ~/.zshrc:
Creating A Project
First, navigate to your project directory, or create a new one:
Creating A Flake
Flakes are the modern way to configure Nix projects. They're self-contained files that explicitly declare all dependencies and lock them to specific versions, making your environment completely reproducible across machines and time.
Create a flake.nix file in your project's root directory using your
favorite text editor. Here's what the Rust development environment
for this blog looks like:
A flake.nix file has three main parts: a description, inputs, and
outputs:
{
description = "Rust development environment";
inputs = {
nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
rust-overlay.url = "github:oxalica/rust-overlay";
flake-utils.url = "github:numtide/flake-utils";
};
outputs = { self, nixpkgs, rust-overlay, flake-utils }:
flake-utils.lib.eachDefaultSystem (system:
let
overlays = [ (import rust-overlay) ];
pkgs = import nixpkgs {
inherit system overlays;
};
in
{
devShells.default = pkgs.mkShell {
buildInputs = with pkgs; [
# Rust toolchain with cross-compilation target
(rust-bin.stable.latest.default.override {
targets = [ "x86_64-unknown-linux-gnu" ];
})
# Rust development tools
cargo-nextest
cargo-watch
just
# Cross-compilation tools
zig
cargo-zigbuild
] ++ lib.optionals stdenv.isDarwin [
# macOS SDK (provides all system frameworks)
apple-sdk
];
# Environment variables
RUST_BACKTRACE = "1";
};
}
);
}
Inputs
Inputs are other flakes your environment depends on:
nixpkgs- the main package repository, like a giant catalog of softwarerust-overlay - provides up-to-date Rust toolchains
flake-utils - helper functions to reduce boilerplate
Outputs
Outputs define what your flake provides. This one creates a
development shell with devShells.default. The buildInputs list
declares exactly what tools you need:
Rust toolchain configured for cross-compilation to Linux
Development tools like cargo-nextest, cargo-watch, and Just
Cross-compilation tools (Zig and cargo-zigbuild)
macOS-specific dependencies through
lib.optionals stdenv.isDarwin
The RUST_BACKTRACE environment variable gets set automatically when
you enter the environment.
Activating Your Flake
Now that you have a flake.nix file, tell direnv to use it. In your
project's root directory:
The direnv allow command is a security feature - it prevents random
directories from automatically running code on your machine. You have
to explicitly trust each .envrc file.
After the first activation, Nix creates a flake.lock file that pins
exact versions of all dependencies. Commit both flake.nix and
flake.lock to version control so everyone gets identical
environments.
Updating Dependencies
The flake.lock file pins your dependencies to specific versions.
When you want to upgrade to the latest versions, use nix flake
update:
This updates all inputs (nixpkgs, rust-overlay, flake-utils) to their
latest versions and regenerates flake.lock.
To update just one input:
After updating, nix-direnv automatically rebuilds your environment the
next time you cd into the directory. Check what changed with git
diff flake.lock. If something breaks, revert with git restore
flake.lock.
Does It Actually Work?
When you cd into a directory with a flake.nix and .envrc,
nix-direnv builds this environment and adds all those tools to your
PATH. When you leave, it removes them. Same project, same
dependencies, every time.
Let's test it. Start from your home directory:
If you see the environment loading and unloading as you navigate, you're good to go. The second time you enter the directory should be nearly instant thanks to nix-direnv caching.