- Nix 97%
- Shell 3%
nixconfig #
Personal NixOS configuration using Flakes, flake-parts, and import-tree.
Structure #
nixconfig/
├── flake.nix # Entry point — wires flake-parts + import-tree
├── modules/
│ ├── aggregators/ # Role bundles + aspect aggregators (base, workstation, server, shell, apps, desktop, services)
│ ├── features/ # Feature modules (base, desktop, dev, profiles, rescue, server, shell)
│ │ ├── base/ # core (nixos + HM), local, cachyosKernel, sops
│ │ ├── desktop/ # compositors, apps, bar, services, noctalia, tools
│ │ ├── dev/ # editors, tools, languages
│ │ ├── profiles/ # gaming, laptop, performance, virtualisation, mkVM
│ │ ├── rescue/ # minimal rescue shell
│ │ ├── server/ # media/, security/, share/, vpn/ + monitoring, nginx, sshd, serverCore
│ │ └── shell/ # zsh, starship, ssh, shell tools
│ ├── hosts/ # Per-host registry entries (+ hardware/disko/impermanence)
│ │ ├── nimeses/ # Desktop laptop (workstation)
│ │ ├── prometheus/ # Desktop PC / NVIDIA (workstation)
│ │ ├── hephaistos/ # Server — Tailscale-only (vaultwarden, croc)
│ │ ├── athena/ # Server — public (nimeses.com, full stack)
│ │ ├── hermes/ # Minimal desktop / kiosk (base)
│ │ └── icarus/ # finix (experimental, non-NixOS)
│ ├── lib/ # Registry + aspects, shared compositor options
│ └── meta/ # Flake-parts wiring, generators, deploy-rs nodes, nixpkgs, users, overlays
└── modules/MODULES.md # Full module reference
All .nix files under modules/ are picked up automatically by import-tree — no manual imports needed when adding new modules.
First-time Installation #
1. Clone the repo #
git clone https://github.com/N1meses/nixconfig
2. Generate hardware configuration #
nixos-generate-config --root /mnt --show-hardware-config > modules/hosts/<hostname>/hardware<Hostname>.nix
3. Create host file #
Create modules/hosts/<hostname>/<hostname>.nix — use an existing host as reference (e.g. nimeses.nix). A host is a single registry.hosts.<hostname> entry; host-specific inline config lives on the entry itself as nixosModule/homeModule (there is no separate configurations.* block anymore — that was collapsed into the registry):
{config, ...}: {
registry.hosts.<hostname> = {
username = "<username>";
system = "x86_64-linux";
stateVersion = "<current-nixos-version>";
# domain = "example.com"; # primary FQDN (public or tailnet) if it serves anything
# hostId = "deadbeef"; # required for ZFS
# extraGroups = ["plugdev"];
# One list of module names, each routed to whichever layer defines it:
# flake.modules.nixos.<name> -> system, .homeManager.<name> -> home, both -> both.
# Start from a role bundle (base / workstation / server), then add extras.
# Validated against an enum of known names — a typo fails at eval with the full list.
aspects = with config.flake.lib.aspects; [
workstation # or: server / base
hardware<Hostname>
# add feature module names here
];
# Host-specific inline config (boot, hardware tweaks, extra packages, sops).
# networking.hostName is set by the generator from the registry key.
nixosModule = {pkgs, ...}: {
users.users.<username>.initialPassword = "changeme";
};
homeModule = {pkgs, ...}: {
# e.g. home.packages, features.compositors.*, ...
};
};
}
4. Install #
nixos-install --flake .#<hostname> --option experimental-features "nix-command flakes"
or when rebuilding the first time after a graphical install
sudo nixos-rebuild switch --flake .#<hostname> --option "extra-experimental-features" "nix-command flakes"
After the first build, experimental features are enabled permanently via the
coremodule — subsequent rebuilds don't need the flag.
Adding a New Host #
- Create
modules/hosts/<hostname>/directory - Add
hardware<Hostname>.nix(generated bynixos-generate-config) - Add
<hostname>.nixwithregistry.hosts.<hostname>— itsaspectslist plus any host-specificnixosModule/homeModuleinline config git addthe new files (import-tree only sees tracked files)- Rebuild:
nh os switch --flake .#<hostname>
Common Commands #
Rebuild #
nh os switch # rebuild + switch current host (NixOS + HM)
nh os switch --flake .#<hostname> # specific host
Remote deploy (deploy-rs) #
deploy.nodes is generated from the registry (modules/meta/deploy.nix) for every
NixOS host, so remote pushes are rollback-safe:
deploy .#<host> # build locally, copy closure, activate on remote
deploy .#<host> --skip-checks # skip the (slow) flake-check pre-flight
Magic rollback is on: if the new config breaks connectivity, the target auto-reverts
to the previous generation on its own. Nodes set sshOpts = ["-o" "ControlPath=none"]
so the confirmation step uses a real fresh login rather than a multiplexed master
socket (otherwise a broken sshd/firewall could falsely confirm).
Maintenance #
nix flake update # update all inputs
nix flake update <input> # update specific input
nix flake check # check for evaluation errors
nh clean all # remove old generations
Inspection #
nix flake show # list all flake outputs
nix repl . # open repl with flake loaded
Hosts #
| Host | Role | Bundle | Domain |
|---|---|---|---|
nimeses |
Desktop laptop | workstation | — |
prometheus |
Desktop PC (NVIDIA) | workstation | — |
hephaistos |
Server — Tailscale-only | server | hephaistos.tail4109e2.ts.net |
athena |
Server — full public stack | server | nimeses.com |
hermes |
Minimal desktop / kiosk | base | — |
icarus |
finix (experimental, non-NixOS) | — | — |
The five NixOS hosts are also deploy-rs targets (deploy .#<host>); icarus is a
finix build, not a NixOS deploy node.
Module System #
Modules are registered as flake.modules.nixos.<name> or flake.modules.homeManager.<name>. A host selects them through a single aspects list in its registry.hosts.<host> entry:
aspects = with config.flake.lib.aspects; [ workstation niri git ];
Role bundles & aggregators. Names in aspects can also be aggregators —
keys of flake.aspectInclude.<name> (in modules/aggregators/) that expand to a
list of other aspects via transitive closure. The role bundles base,
workstation, and server are the top-level ones (e.g. workstation pulls in
base, desktop, niri, …), so most hosts list a bundle plus a few extras. An
aggregator key needs no backing module — it contributes only its members.
The generators (modules/meta/generation.nix for NixOS, modules/meta/homeModules.nix
for home) route each resolved name to whichever layer defines it:
| Name defined in… | Applied to |
|---|---|
flake.modules.nixos.<name> only |
the system |
flake.modules.homeManager.<name> only |
home |
| both | both |
This means a compositor (nixos session + HM config) can't be half-wired, and a standalone homeConfigurations.<host> build automatically ignores nixos-only names (keeping the home side portable). aspects is enum-typed against the set of known module names, so a typo fails at eval and the error lists every valid name. The with config.flake.lib.aspects; scope lets names be written bare instead of quoted.
Listed = enabled — naming a module in aspects is sufficient to enable it. No extra .enable = true needed except for:
- Compositor selection (
features.compositors.niri.enable) - Server domain (
features.server.domain) - Per-monitor config (
features.compositors.monitors.<name>)
See modules/MODULES.md for the full module reference.