aboutsummaryrefslogtreecommitdiffstats

nix-config

A single Nix flake for provisioning both NixOS hosts and non-NixOS systems (any Linux now; macOS via nix-darwin later) from one shared set of modules.

Layout

  • flake.nix — inputs (nixpkgs, home-manager) and outputs (nixosConfigurations, homeConfigurations, formatter, devShells)
  • lib/ — mkNixos and mkHome helper functions that build nixosConfigurations / homeConfigurations entries
  • modules/nixos/common.nix — shared NixOS-only system settings applied to every NixOS host: flakes enabled, tom user (bash as the login shell), locale/timezone defaults, gc policy, sshd (key-only auth) with a firewall allowing port 22, and a GNOME desktop (GDM login, NetworkManager, PipeWire audio, CUPS+Avahi printing) — see below
  • modules/home/ — the shared, cross-platform home-manager profile (shell, editor, CLI tools, dev tools, dotfiles). The same modules are consumed whether activated via a NixOS host's home-manager module or standalone home-manager switch
  • users/tom.nix — identity/profile file consumed by both mkNixos and mkHome
  • hosts/template/ — a copy-source-only NixOS host skeleton; never wired into flake.nix outputs directly (excluded from discovery by name — see below). Copy it via scripts/bootstrap-host.sh (or nix run .#bootstrap-host) when provisioning a real machine
  • scripts/bootstrap-host.sh — scaffolds a new hosts/<hostname>/ from the template; exposed as apps.<system>.bootstrap-host
  • modules/home/dotfiles.nix — opt-in personal dotfiles automation (see below)
  • modules/home/bash-profile.nix — always-on ~/.bash_profile bootstrap (see below)
  • modules/home/editor.nix — Neovim provisioning; deliberately never writes to ~/.config/nvim (see below)

Any directory under hosts/ other than hosts/template that contains a system file automatically becomes a real nixosConfigurations.<name> output — adding a host means dropping in a directory, not editing flake.nix.

Installing NixOS on a new machine

This flake does not partition disks for you — do that first, with the standard NixOS installer or manually.

  1. Partition and format disks; mount the target filesystems under /mnt (including /mnt/boot for the ESP if UEFI).
  2. Get this repo onto the installer: nix-shell -p git && git clone <your-remote-url> ~/nix-config (requires the repo already pushed to a remote — see Follow-ups below).
  3. From ~/nix-config, scaffold the host: nix run .#bootstrap-host -- <real-hostname> --root /mnt --state-version <ver> --root /mnt tells the script to read (or generate, via nixos-generate-config --root /mnt if needed) hardware-configuration.nix from the freshly mounted install target — it never reads the live installer's own /etc/nixos, since --root has no default and must always be given explicitly. --state-version should match the installer's NixOS release (e.g. 24.05) and should never be changed after the fact.
  4. Review hosts/<real-hostname>/configuration.nix (boot loader choice, etc.) — the script prints this reminder along with the exact next commands.
  5. cd ~/nix-config && git add hosts/<real-hostname> (flakes only see git-tracked files).
  6. If needed: export NIX_CONFIG="experimental-features = nix-command flakes".
  7. nixos-install --root /mnt --flake ~/nix-config#<real-hostname>.
  8. Reboot and remove the install media.
  9. Afterwards, clone the flake to a stable path (e.g. /etc/nix-config) so future updates are just sudo nixos-rebuild switch --flake /etc/nix-config#<real-hostname>.

Desktop environment

Every NixOS host built from this flake boots into a GNOME desktop by default (GDM as the login manager), applied unconditionally in modules/nixos/common.nix rather than as a per-host opt-in. Necessary companions are enabled alongside it: networking.networkmanager (GNOME's own network settings UI), services.pipewire + security.rtkit (audio, including PulseAudio-compatible clients), and services.printing (CUPS) + services.avahi (nssmdns4, openFirewall) for printing and network/AirPrint printer discovery — openFirewall is required so mDNS discovery traffic isn't blocked by the port-22-only firewall this repo already enables. This is a system-level toggle only; per-user GNOME customization (dconf, Shell extensions) is out of scope.

Installing on a non-NixOS system (any Linux, or macOS once supported)

Non-NixOS hosts are unaffected by bootstrap-host — there's nothing per-host to scaffold. homeConfigurations has one generic entry per platform (tom@linux for x86_64-linux, tom@darwin for aarch64-darwin), shared by every standalone machine of that platform.

  1. Install Nix. Recommended: the Determinate Systems installer (flakes enabled by default). The official installer also works, but you'll need to add experimental-features = nix-command flakes to nix.conf yourself.
  2. git clone <your-remote-url> ~/nix-config.
  3. First run: nix run home-manager/master -- switch --flake ~/nix-config#tom@linux (or #tom@darwin on macOS). After that, home-manager switch --flake ~/nix-config#tom@linux (or #tom@darwin) works directly.

This only touches your home directory — no sudo required.

Personal dotfiles (opt-in)

modules/home/dotfiles.nix can automatically clone a separate personal dotfiles repo to ~/dotfiles and symlink its top-level package directories into $HOME, GNU-Stow style (each package directory's contents mirror their target path — e.g. a package's .gitconfig links to ~/.gitconfig). It's opt-in and controlled by a single option:

home.dotfiles.repoUrl = "https://example.com/your/dotfiles.git";

users/tom.nix sets this to https://git.tomvanderlee.com/dotfiles — the feature is active as soon as this flake is activated with home-manager switch.

Behavior notes: an existing ~/dotfiles checkout is never re-cloned or modified; every non-hidden top-level directory is treated as a stow package; one package's conflict with an existing file only skips that package (a warning is printed) rather than blocking the rest. Because ~/dotfiles/git/.gitconfig and ~/dotfiles/bash/.bashrc are expected to be the sole sources of git and bash configuration once stowed, this repo's own programs.git/programs.bash home-manager management has been removed — git itself is still installed via modules/home/cli-tools.nix, and bash ships with both macOS and NixOS regardless of whether repoUrl is set.

Editor (Neovim)

modules/home/editor.nix installs Neovim as the default editor ($EDITOR/$VISUAL, plus a vim alias), and provisions a C compiler (pkgs.stdenv.cc) and the tree-sitter CLI so natively-built plugins can compile. It deliberately sets programs.neovim.sideloadInitLua = true, which stops home-manager from writing anything to ~/.config/nvim/init.lua — that path is owned exclusively by the personal dotfiles repo's neovim Stow package (lazy.nvim + plugins). Without sideloadInitLua, home-manager's own generated init.lua permanently conflicts with that Stow package and it gets silently skipped (per the dotfiles automation's skip-on-conflict behavior above), so plugins never load. Don't add programs.neovim.extraConfig/plugins/anything else that writes into ~/.config/nvim here — it would reintroduce the same conflict.

Login shell bootstrap

NixOS and most other Linux distros don't ship a ~/.bash_profile that sources ~/.bashrc, so login shells never pick up your bash config. modules/home/bash-profile.nix creates one for you — but only if ~/.bash_profile doesn't already exist. If it's already there (any file or symlink), it's left completely untouched; home-manager never edits or removes it. This is always on, on every platform (Linux and macOS), and isn't gated by home.dotfiles.repoUrl — it works whether or not you use the personal dotfiles automation above.

Developer tooling

modules/home/dev-tools.nix provisions the developer's most-used CLI tools: gh, tmux, htop, yq-go, tree-sitter, docker, docker-compose, kubectl, awscli2. The container/cloud entries (docker, docker-compose, kubectl, awscli2) are client/CLI tools only — the actual container runtime (OrbStack/Docker Desktop on macOS) and any cloud account configuration stay outside Nix's control; these just give you a consistent CLI version to talk to whatever runtime/account is already set up.

terraform was considered but deliberately left out: recent versions are unfree-licensed (HashiCorp's BSL), which would require either nixpkgs.config.allowUnfree = true (flake-wide, not just for terraform) or substituting opentofu. Neither was wanted for now.

Shell and prompt: bash is the primary managed shell (matching where the real shell config already lives — ~/dotfiles/bash/.bashrc). modules/home/shell.nix only provisions the liquidprompt package; its configuration and activation are entirely owned by ~/dotfiles/liquidprompt/, same as git and bash. There is no Nix-managed starship or direnv — both were removed; per-project environments are left entirely to your existing pyenv/nvm/rustup setup rather than a Nix-managed alternative.

Development

  • nix fmt — format all .nix files with nixfmt-rfc-style.
  • nix develop — a shell with statix and deadnix for linting.
  • nix flake check — validates homeConfigurations."tom@linux", apps.bootstrap-host, and the other outputs defined here (there are no real nixosConfigurations until a host is scaffolded — see Follow-ups).

Follow-ups (not covered by this change)

  • Push this repo to a remote — required before nixos-install can git clone it during a real install.
  • Add a real NixOS host once hardware has been partitioned (see workflow above).
  • Add nix-darwin once a real Mac host is being configured.
  • Add secrets management (e.g. sops-nix) — none is set up yet.