# 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) ```sh nix flake init -t github:/ # 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//` directory is only where this example keeps it. ``` your-inventory/ ├── flake.nix # inputs (pub + follows) + nixosConfigurations. ├── 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: ```nix { inputs = { pub.url = "github:/"; # 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: ```nix # 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: ```nix 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.`, per-host `hosts//` (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//`. 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.