NixOS Development Best Practices
This skill outlines how to use NixOS, Nix Flakes, and direnv to create robust, reproducible development environments.
1. Core Concepts
- •Nix Flakes: The modern standard for defining Nix projects. They ensure reproducibility by pinning dependencies in a
flake.lockfile. - •DevShells: Isolated development environments defined in
flake.nix. They provide all necessary compilers, tools, and libraries without polluting your global system. - •Direnv: A shell extension that automatically loads/unloads environment variables when you enter/exit directories.
- •Nix-Direnv: A direnv extension that makes loading Nix shells faster and simpler.
2. Flake Structure (flake.nix)
Every project should have a flake.nix in its root. A standard Rust project flake looks like this:
nix
{
description = "My Rust Project";
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
(rust-bin.stable.latest.default.override {
extensions = [ "rust-src" "rust-analyzer" ];
})
# Common Tools
pkg-config
openssl
# GUI/System Dependencies (Example for Cosmic/Iced)
wayland
libxkbcommon
vulkan-loader
];
# Environment Variables
shellHook = ''
export LD_LIBRARY_PATH=${pkgs.lib.makeLibraryPath [
pkgs.wayland
pkgs.libxkbcommon
pkgs.vulkan-loader
]}:$LD_LIBRARY_PATH
echo "🚀 Development environment loaded!"
'';
};
}
);
}
3. Automatic Environment Loading (direnv)
To automatically activate the flake when you cd into the directory:
- •
Install Direnv & Nix-Direnv: Add this to your system configuration (
configuration.nixorhome-manager):nixprograms.direnv = { enable = true; nix-direnv.enable = true; }; - •
Create
.envrc: In your project root, create a file named.envrcwith this single line:bashuse flake
- •
Allow: Run
direnv allowin the terminal.
4. Best Practices
- •Pin Everything: Commit
flake.lock. This guarantees that every developer uses the EXACT same version of Rust, external libraries, and system tools. - •Keep Scripts Local: Don't rely on global tools. If your project needs
justorcargo-nextest, add them tobuildInputsinflake.nix. - •System Dependencies: Unlike other OSs, you cannot just "install libraries" globally. You must define them in
buildInputs. If a binary fails with "library not found", you likely need to add it toLD_LIBRARY_PATHin theshellHook. - •Rust Specifics:
- •Use
fenixorrust-overlayfor managing Rust toolchains in Nix. - •Set
RUST_SRC_PATHenvironment variable ifrust-analyzercomplains about missing source code.
- •Use
5. Troubleshooting
- •"Command not found": Check if the package is in
buildInputs. Runnix flake updateto refresh inputs. - •IDE Issues: Launch your IDE (VS Code, etc.) from the terminal inside the project folder (
code .). This ensures it inherits thedirenvenvironment variables. - •Slow Loading: Ensure
nix-direnvis enabled. It caches the shell calculation.