Environment Management with Home Manager
Environment Management with Home Manager
nix develop is the project. Home Manager is the person. The boring default is: personal git/prompt/editor in Home Manager; compilers only in the project’s devShell — never Go 1.x in home.packages “for every repo”.
Part 7 teaches Home Manager as a NixOS user module. This chapter is the boundary with part 3’s shells, including on a foreign Linux laptop (Nix 2.35, nixpkgs 26.05).
Mental model
~/.nix-profile Home Manager: jq, bat, git config, nvim
direnv / nix develop
PATH prepends go, gopls, postgresql client for this repo only
When you cd out, the compiler vanishes. Your git aliases stay. That split is how two repos can pin two Go versions without fighting.
home.stateVersion = "26.05"; on a new user born on 26.05. If the user was created on 24.11, leave "24.11". Birth, not channel.
On NixOS 26.05, Home Manager follows the system nixpkgs. On Ubuntu + Nix 2.35, pin nixos-26.05 in the user flake too.
Worked examples
Case 1: Personal tools only
Save as home.nix:
# home.nix
{ pkgs, ... }:
{
home.username = "deskuser";
home.homeDirectory = "/home/deskuser";
home.stateVersion = "26.05";
home.packages = with pkgs; [
htop
bat
fzf
jq
ripgrep
];
programs.git = {
enable = true;
userName = "Desk Engineer";
userEmail = "engineer@desk.corp";
};
}home-manager switch --flake .#deskuser
which jqjq is a store path. It is not gcc. which go after this switch must still fail (or be distro go you should not use).
Case 2: Project compiler stays in the flake
Save as flake.nix in the service repo:
# flake.nix
{
description = "Desk API";
inputs.nixpkgs.url = "github:NixOS/nixpkgs/nixos-26.05";
outputs = { self, nixpkgs }:
let
system = "x86_64-linux";
pkgs = nixpkgs.legacyPackages.${system};
in
{
devShells.${system}.default = pkgs.mkShell {
packages = [ pkgs.go pkgs.gopls pkgs.postgresql ];
};
};
}# outside the shell
which go || echo 'no go'
nix develop --command which goHome Manager must not also install pkgs.go. If it does, which go outside the project still works — and CI (which only has the flake) does not match your laptop.
Case 3: direnv composes, it does not replace HM
Save as .envrc:
# .envrc
use flakedirenv allow
echo "$PATH" | tr ':' '\n' | headuse flake prepends the devShell. Starship from Home Manager still runs. You do not put programs.git in the project flake.
Case 4: Same nixpkgs as the OS when you can
On NixOS, in the user flake:
# flake.nix
{
inputs.nixpkgs.url = "github:NixOS/nixpkgs/nixos-26.05";
inputs.home-manager.url = "github:nix-community/home-manager/release-26.05";
inputs.home-manager.inputs.nixpkgs.follows = "nixpkgs";
}nix flake metadataOne nixpkgs. HM release-24.11 + nixpkgs 26.05 is eval pain. On Ubuntu + Nix 2.35, the same pin keeps jq the same closure as CI.
Case 5: Rollback is per layer
home-manager generations
home-manager switch --rollbackThis does not roll back the project’s go. nix develop is not a Home Manager generation. Two rollbacks, two tools, two meanings. A bad home.packages does not warrant nixos-rebuild switch --rollback.
The trap
The trap is home.packages = [ pkgs.go pkgs.nodejs pkgs.rustc ]; as a “developer workstation.” The next repo needs another version. You overlay. CI diverges. Put toolchains in the repo flake.
The other trap is bumping home.stateVersion to "26.05" on a user born on 24.11 because “we upgraded the channel.”
The boring rule
- Home Manager: identity, editor, prompt, generic CLIs.
- devShell: language toolchains and DB clients the app needs.
stateVersionis birth, not “current channel.”- Same nixpkgs pin as the desk OS when the machine is NixOS 26.05. HM
release-26.05+follows. - Do not install compilers globally “so PATH always has go.”
Try this
- Add
pkgs.hellotohome.packages, switch,hello, remove it, switch. which goin and out ofnix developon a repo that listspkgs.go. Out must fail (or be distro go you should not use).echo $PATHinside direnv and label HM vs devShell entries.- Set
home.stateVersionwrongly to a future value on a lab user, read the warning, restore the birth value.