name: NixOS Development description: Best practices for using NixOS, Flakes, and direnv for reproducible development environments.
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:
{
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):programs.direnv = { enable = true; nix-direnv.enable = true; };Create
.envrc: In your project root, create a file named.envrcwith this single line:use flakeAllow: 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.