- Nix 96.8%
- Just 1.9%
- Shell 1.3%
|
All checks were successful
CI / check (push) Has been skipped
CI / build (athena) (push) Has been skipped
CI / build (ishtar) (push) Has been skipped
CI / build (minerva) (push) Has been skipped
CI / dry-activate (athena) (push) Has been skipped
CI / dry-activate (ishtar) (push) Has been skipped
CI / dry-activate (minerva) (push) Has been skipped
CI / deploy (athena) (push) Successful in 16s
CI / deploy (ishtar) (push) Successful in 29s
CI / deploy (minerva) (push) Successful in 23s
- borg was silently dead on two hosts, fixed both
- `athena`: a `knotc zone-dump` `preHook` aborted every run - `knotc` isn't on the job `$PATH`, and `zone-dump` isn't even a real subcommand. dropped the hook, `/var/lib/knot` lmdb is crash-consistent so the flush bought nothing
- `minerva`: never ran once - hetzner `sub3`'s host key was never trusted, and `/root/.ssh/known_hosts` is tmpfs on an impermanent box so a manual accept wouldn't survive a reboot anyway
- declared the storage-box key in `programs.ssh.knownHosts`, derived from `constants.borg` - survives the wipe, unblocks minerva, hardens athena/ishtar for reinstalls
- both verified now (`vmail`/`dkim`/`knot` on athena, `vaultwarden`/`forgejo`/`gollum` on minerva); zero failure alerting anywhere -> follow-up
- prepped `athena` for an impermanent reinstall on a downsized nanode
- `diskoConfigurations/athena.nix`: gpt + 1M `bios_grub` + ext4 `/boot` + btrfs `@nix`/`@persist`/`@log` + tmpfs `/`. MBR would've dropped the `bios_grub` part but disko deprecated the legacy `table` type; ext4 `/boot` keeps GRUB off btrfs
- `athena.roles.impermanent = true`
- persist dirs w/ explicit ownership (a plain string dir lands `root:root` and locks the service out): `vmail` (`storage.owner`/`group`), `dkim` (`rspamd`), `/var/lib/knot` (`knot`, the KASP keys or DNSSEC goes bogus on reboot)
- `linodeBase`: dropped `grub.device` - disko's `bios_grub` already sets `grub.devices`, having both dup'd the disk in `mirroredBoots`
- cleanup:
- `/persist` `neededForBoot` -> `impermanence.nix` (identical for every impermanent host), out of athena + minerva host configs
- `acme` rides in via `mailserver`'s `imports` (its only consumer) instead of athena's `activeModules`
- `journald` `SystemMaxUse=500M` fleetwide - athena's had ballooned to `3.9G`
- left for later: the reinstall itself - linode `Direct Disk` -> delete/resize/new disk -> `nixos-anywhere .#athena` -> seed `/persist` (age key + `borg extract`) -> verify dnssec/mail/hub
Reviewed-on: #71
|
||
|---|---|---|
| .forgejo/workflows | ||
| modules | ||
| sops | ||
| static | ||
| .envrc | ||
| .gitignore | ||
| .sops.yaml | ||
| flake.lock | ||
| flake.nix | ||
| justfile | ||
| LICENSE | ||
| README.md | ||
| statix.toml | ||
nix-configurations
My personal NixOS configurations, using the Dendritic Pattern.
TL;DR
The dendritic pattern takes your flake and literally breaks it into parts. Each file is its own module, be it a nixosModules or homeModules module, or even an entire nixosConfiguration definition, typically via flake-parts and import-tree.
How do I do it?
The top-level flake.nix uses flake-part's helper function, then pass import-tree ./modules to it. Everything within the ./modules tree gets imported as a flake-parts module without the need for glue code (no default.nix that lists every file).
How are modules used?
Each module is defined through flake.{nixosModules,homeModules,etc}.moduleName, which can then be referenced through self.{nixosModules,homeModules,etc}.moduleName. These modules are then imported into each nixosConfigurations.hostName as such:
activeModules = with self.nixosModules; [
core disko lanzaboote nvidia
kde gaming develop stylix workstation
lament # yes, even my user itself is a module
];
# then, within the host or user definition
modules = activeModules ++ [
# other things, such as
{system.stateVersion = "26.05";}
];
Custom lib? In flake-parts?
flake.myLib is a custom flake-parts option using types.lazyAttrsOf types.raw so that each part of the lib can be written as an individual module and merge together just like nixosModules or homeModules.
Structure
modules/
nixosConfigurations/
athena.nix ## Linode VPS, service host
ishtar.nix ## Personal desktop
nixosModules/
hardware/
profiles/
services/
system/
homeModules/
apps/
shell/
diskoConfigurations/
${hostName}.nix
module.nix ## placement by colocation, diskoConfiguration handler
users/ ## see notes below
packages.nix ## single entrypoint for packages
static/ ## host-specific, or not modularized yet
{hostname}/
packages/ ## see note below
Modules are organized by output type then by semantic category.
Notes
- I use home-manager at the system level.
- No,
usersis not a valid flake output, but it made sense to keep it as a separate concern. - For
packages, without serious Googling I couldn't figure out how to in-line the actual package declaration, so I usestatic/packagesfor that. - "You only have the two hosts, so this level of organization is overkill." Yes, yes it is.
- "You only have the two hosts, so having device specific overrides is overkill." Yes. Yes it is.
So... Why?
Honestly? I liked the way the Dendritic Pattern looked on paper, and the idea of dropping a new module into the flake tree and importing it into my system instead of glue coding it (and args like self) in, just to forget where I put it, was enough.
Need to change how ZSH works? homeModules. Caddy? nixosModules. New host? New file in nixosConfigurations. As long as I know what I want to change, where it is becomes obvious.