Nix, NixOS
I'm a couple years deep into running NixOS as the Linux distribution of choice for my desktop computer and homelab, and as of last month also my laptop. I'd say I'm at a point now where I have a decent understanding of managing multiple NixOS systems, how to troubleshoot when Nix errors out, and how to figure out what Nix things I need to do whatever thing I'm trying to do.
So on this page you'll find all about how I use Nix and NixOS, I hope it's useful to you.
A good base
I always use the base configuration that nixos-generate-config provides during the initial setup and installation of NixOS. It provides a minimal configuration with comments scattered throughout, that after I've gone through it and adjusted as necessary, leaves me with a running system that I can now iterate on.
It's not uncommon for me to get to 50+ generations built before landing on a configuration I'm ready to start using properly.
For example, the laptop I'm writing this on right now is at generation 53 and I've only installed NixOS on it about a month ago.
$ sudo nix-env --profile /nix/var/nix/profiles/system --list-generations
52 2026-06-11 16:07:19
53 2026-06-24 14:33:39 (current)
Useful documentation → Nix Cookbook
The ones to definitely have set from the start are networking.hostName = "cool-system"; for your machine's name, console.keyMap = "your-keyboard-layout"; if you're not using a QWERTY-based keyboard layout and services.openssh.enable = true; when you're setting up a server (and don't forget to add your public key to users.users.<name>.openssh.authorizedKeys.keys of your user).
Version control
As soon as I have a system running NixOS I'll set up git to start tracking changes. I like to make my $USER the owner of /etc/nixos, turn it into a git repository, commit what exists so far and then push to a remote.
sudo chown -R $USER:users /etc/nixos
cd /etc/nixos
git init
git add .
git commit -m 'Initial NixOS configuration commit.'
git remote add <remote>
git push -u <remote> <branch>
Then any time changes are made I'll stage them to be committed, do a nixos-rebuild and if all is well, commit and push.
cd /etc/nixos
# Make changes ...
git add .
sudo nixos-rebuild switch # or: sudo nixos-rebuild boot
git commit -m '<describe what changed>'
git push
There are many other ways to version control a NixOS configuration and deploy it, but I like this approach because it's dead simple.
Organizing
Eventually doing everything in configuration.nix will become cumbersome, which is when I'll start to take the larger sections and split them out into their respective files. Usually the one I'll start with is services as that tends to be the majority of my configurations.
/etc/nixos/
├── configuration.nix
└── services/
├── default.nix
├── openssh.nix
└── ...
Then in my configuration.nix I'll add the services directory to the imports and in the services/default.nix I'll add each individual service file as an import as well.
# in configuration.nix:
imports = [
./services
];
# services/default.nix:
{ ... }:
{
imports = [
./openssh.nix
# additional services ...
];
}
# example services/openssh.nix:
{ config, lib, pkgs, ... }:
{
services.openssh = {
enable = true;
settings = {
PasswordAuthentication = "no";
PermitRootLogin = "no";
};
};
}
Repeat for the other services and configuration.
I don't separate everything out like this, some configuration I'll only make a separate .nix file for or it can stay in configuration.nix, but if a part of the configuration gets too big it'll be evicted to another place.
Automatic garbage collection and store optimisation
As more and more generations are built, eventually those old generations stack up and use a lot of storage space. These can manually be deleted with nix-collect-garbage and nix-store --optimise or automatically by enabling nix.gc and nix.optimise.
The configuration I'm using runs at 2 in the morning on Sunday, with time spaced out between garbage collection and store optimisation.
# in nix.nix:
{ lib, ... }:
{
nix = {
# Run "nix-collect-garbage" automatically
gc = {
automatic = true;
dates = [ "Sun *-*-* 02:15:00" ]; # Every Sunday at 02:15
options = "--delete-older-than 7d";
persistent = true;
};
# Run "nix-store --optimise" automatically
optimise = {
automatic = true;
dates = [ "Sun *-*-* 02:45:00" ]; # Every Sunday at 02:45
persistent = true;
};
};
# Only for laptops, disable the AC power requirement.
# Otherwise nix-optimise may never actually end up triggering.
systemd.services.nix-optimise = {
unitConfig.ConditionACPower = lib.mkForce false;
};
}
I'll have this set up on all of my NixOS systems just so the generations don't balloon in storage. Otherwise eventually there's no storage space left to do another nixos-rebuild, ask me how I know...
I find 7 days is plenty of time to keep generations around. Typically when any issues arise that require rolling back a generation, they happen immediately after updating. So if everything is running smoothly after updating then any future issues can likely be fixed by changing some configuration and rebuilding again, rather than needing to roll back a generation.
The time to pay the most attention during updates is when updating NixOS versions, for example from NixOS 25.11 to 26.05. In my experience regular updates in between are typically fine and almost never require rolling back a generation.
More to come
When I get around to it. :)