nixos: a declarative linux

NixOS is a Linux distribution where the whole system is described in one config file. You change the file and rebuild, and the system matches it. If something breaks, you boot the previous version.

the idea

configuration.nix
       |
       v
[ nixos-rebuild switch ]
       |   builds packages, config files and services
       v
/nix/store/...      new paths, next to the old ones
       |
       v
generation 42   <-- active, added to the boot menu
generation 41
generation 40

Nothing is changed in place. Every build lands in the Nix store under its own hashed path, and the system simply switches which generation is active.

a small config

{ config, pkgs, ... }:
{
  networking.hostName = "box";

  services.openssh.enable = true;
  services.openssh.settings.PasswordAuthentication = false;

  users.users.me = {
    isNormalUser = true;
    extraGroups = [ "wheel" ];
    openssh.authorizedKeys.keys = [ "ssh-ed25519 AAAA..." ];
  };

  environment.systemPackages = with pkgs; [ git vim age ];

  system.stateVersion = "25.05";
}

That is a complete server with key-only SSH, a user and a few tools. Copy the file to another machine and you get the same system.

the nix store

/nix/store/
  |-- b6gvzjyb2pg0...-openssl-3.4.1/
  |-- 9qx7f3m1lka2...-openssl-3.0.15/
  |-- 2hm8ck0wq5n7...-vim-9.1.0/
  '-- ...

The hash covers everything that went into a package: its source, dependencies and build options. Two versions of one library can live side by side, and nothing installs into /usr.

everyday commands

sudo nixos-rebuild switch              # apply the config
sudo nixos-rebuild switch --rollback   # back one generation
nix-shell -p openssl                   # try a tool, install nothing
sudo nix-collect-garbage -d            # remove old generations

why it suits security work

the trade-off

Nix is its own language and the learning curve is real. Software that expects a normal /usr layout sometimes needs extra packaging work. Once it clicks, going back is hard.