Personal flake following the dendritic pattern for my hosts.
  • Nix 96.8%
  • Just 1.9%
  • Shell 1.3%
Find a file
Sarah Lament b220280ded
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
turns out nothing was backing up (and a downsize) (#71)
- 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
2026-07-08 16:29:05 -05:00
.forgejo/workflows prepared flake for additional hosts (#67) 2026-07-06 01:45:12 -05:00
modules turns out nothing was backing up (and a downsize) (#71) 2026-07-08 16:29:05 -05:00
sops internal mail relays over wireguard now (#70) 2026-07-07 20:01:46 -05:00
static turns out nothing was backing up (and a downsize) (#71) 2026-07-08 16:29:05 -05:00
.envrc flake: pre-commit should work now 2026-05-22 02:00:51 -05:00
.gitignore flake: added justfiles 2026-05-27 00:07:10 -05:00
.sops.yaml sops rework (#33) 2026-06-14 17:36:14 -05:00
flake.lock prepared flake for additional hosts (#67) 2026-07-06 01:45:12 -05:00
flake.nix prepared flake for additional hosts (#67) 2026-07-06 01:45:12 -05:00
justfile sops rework (#33) 2026-06-14 17:36:14 -05:00
LICENSE Initial commit~! 2025-12-01 01:23:07 -06:00
README.md README touchup 2026-06-07 23:25:14 -05:00
statix.toml fable 5 audit (#66) 2026-07-04 19:42:50 -05:00

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, users is 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 use static/packages for 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.

License

MIT