Files
nixos-config/README.md
T
daniil-berg 5e1b9332a4 docs(readme): show a realistic private-inventory layout and overrides
The consumer section had only a bare-bones flake snippet. Add an example
private layout (`system/` + `home/` split, `hosts/<host>/`), a fuller
`flake.nix` matching it, and an "Overriding the baseline" section covering
the three mechanisms (neutral default → real value, `local.*` options,
extra modules). Note that structurally only `flake.nix` is required.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-24 16:42:53 +02:00

6.5 KiB

NixOS + home-manager flake

A flake-based NixOS and home-manager configuration: a set of reusable modules plus a ready-to-run example host that assembles them. Use it either way:

  • as a starting template — clone, swap in your hardware, rebuild; or
  • as modules — import them into your own (private) flake.

Quick start (template)

nix flake init -t github:<owner>/<repo>        # or: git clone …
# generate hardware config for THIS machine (the one file you must replace):
sudo nixos-generate-config --show-hardware-config > hosts/example/hardware-configuration.nix
# set your hostName + username in hosts/example/default.nix, then:
sudo nixos-rebuild switch --flake .#example

nixosConfigurations.example is a real, buildable machine assembled from the modules in modules/. It doubles as living documentation: it shows exactly how the modules wire together, so there's nothing to reverse-engineer. The only genuinely machine-specific file is hardware-configuration.nix (NixOS generates it); everything else works from defaults you can tweak.

Import the modules into your own flake

Add this repo as an input and assemble your machines in a private flake. Below is a realistic example layout. Structurally only flake.nix is required — every other path is just one way to organize your config; rename, merge, inline, or drop them freely. The one bit of content a bootable machine can't skip is its hardware-configuration.nix (nixos-generate-config output); the hosts/<host>/ directory is only where this example keeps it.

your-inventory/
├── flake.nix                        # inputs (pub + follows) + nixosConfigurations.<host>
├── flake.lock
├── system/                          # system-level modules
│   ├── regional.nix                 # time zone / keymap / locale (see Overriding)
│   ├── packages.nix                 # extra system packages
│   └── tailscale.nix                # VPN / network topology
├── home/                            # home-manager modules, one file per concern
│   ├── identity.nix                 # git identity, name/email
│   ├── ssh.nix                      # SSH hosts (topology — stays private)
│   ├── syncthing.nix                # Syncthing peers
│   └── …                            # personal apps: browser, editor, chat, …
└── hosts/
    └── myhost/
        ├── default.nix              # hostname, user account, host-specific system bits
        └── hardware-configuration.nix   # `nixos-generate-config` output

The flake.nix that wires it together:

{
  inputs = {
    pub.url = "github:<owner>/<repo>";
    # Reuse the public flake's pins so versions never drift between the two.
    nixpkgs.follows = "pub/nixpkgs";
    home-manager.follows = "pub/home-manager";
  };

  outputs = inputs@{ self, nixpkgs, pub, home-manager, ... }: {
    nixosConfigurations.myhost = nixpkgs.lib.nixosSystem {
      system = "x86_64-linux";
      modules = [
        pub.nixosModules.default          # generic system baseline
        ./system/regional.nix             # your regional values (see below)
        ./hosts/myhost                    # hardware + hostname
        home-manager.nixosModules.home-manager
        {
          home-manager.useGlobalPkgs = true;
          home-manager.useUserPackages = true;
          # Inject your own inputs into your home modules; the public ones ignore them.
          home-manager.extraSpecialArgs = { inherit inputs; };
          home-manager.users.me.imports = [
            pub.homeModules.default         # generic home baseline
            ./home/identity.nix             # your git identity, name/email, …
            ./home/ssh.nix                  # your SSH hosts (topology stays private)
            # … more personal home modules
          ];
        }
      ];
    };
  };
}

Your machine list, hostnames, and any private specifics stay in your flake — you pull only the generic modules from here.

Overriding the baseline

The public modules are a baseline you extend without forking. Three ways to shape them from your private flake, in rough order of how often you reach for them:

  1. Neutral default → your real value. Region-agnostic settings ship as mkDefault placeholders (console.keyMap = "us", time.timeZone = "UTC", en_US locale). Set the real ones in a private module — no mkForce needed:

    # system/regional.nix
    { time.timeZone = "Europe/Berlin"; console.keyMap = "de"; }
    
  2. local.* options. A module that exposes a knob puts it under the local.* namespace with a default. Override in one line, e.g. a fuller TeX Live:

    local.texlive.package = pkgs.texlive.withPackages (ps: [ ps.scheme-full ]);
    
  3. Extra modules. Anything with no public counterpart is just another module in the import lists above — personal apps, SSH/Syncthing topology, secrets wiring.

Architecture: three layers

Kept apart on purpose. When adding config, decide which layer it belongs to:

  1. Modules — generic, public (this repo). Reusable building blocks under modules/, exposed as nixosModules.* / homeModules.*. Anything machine- or user-specific is a module option with a sane default — never hard-code personal values into a module.
  2. Inventory — topology, private (your flake). The actual machines: nixosConfigurations.<host>, per-host hosts/<host>/ (hardware, hostname), and topology-revealing config (SSH hosts, Syncthing peers, internal IPs). This is what you deploy from; it imports the modules above. Publish the modules, keep the inventory in a private repo. The example host here is a placeholder-filled stand-in for it.
  3. Secrets — neither repo. Private keys, tokens, passwords never enter any repo. Provide them as runtime files (0600, out-of-band) or via a secrets manager that decrypts to a runtime path; nix references only the path.

Layout

flake.nix            # inputs + outputs (nixosModules, homeModules, example host, devShells)
modules/             # the reusable library
hosts/example/       # the template host (placeholder hardware-configuration.nix)

Shared *.nix / *.lock live at the top level; per-host files under hosts/<host>/.

Chasing a shutdown/boot hang? modules/shutdown-debug.nix exposes local.shutdownDebug.* (verbose systemd logging to kmsg, a tty9 debug shell, a shortened stop-timeout) — see its header comment for how to read the evidence.