move hardware config in-repo per-host and mount the data drives

importing /etc/nixos/hardware-configuration.nix by absolute path from the
shared core module had two problems: it made the flake impure (every eval
needed --impure, since the file lives outside the store) and both hosts
resolved to whichever machine's file was on disk, so evaluating t480 on shadow
silently baked in shadow's disk uuids.

shadow now carries its own generated scan at modules/hosts/shadow/_hardware.nix.
the leading underscore keeps import-tree from picking it up as a flake-parts
module - its filter drops any path containing "/_" - so it is imported
explicitly from the host's default.nix.

it is generated with --no-filesystems and all of shadow's filesystems are
declared by hand in disks.nix instead. that split is what makes regeneration
safe: nixos-generate-config emits fileSystems for whatever is mounted at the
time, which would otherwise conflict with the entries below every time the data
drives happened to be mounted. `nix run .#sync-hardware` refreshes the file.

DUMP/GAMES/BAK are mounted by label at /mnt/{dump,games,bak} with
noauto + x-systemd.automount, so the two 2.7T spinners stay parked until
something touches the path and spin back down after 10 minutes idle. nofail
keeps a missing disk from dropping the boot into emergency mode.

/ and /boot are unchanged - still the same uuids, verified identical to the
previous evaluation. t480 keeps the absolute import for now with a TODO; its
scan can only be generated on the machine itself.
This commit is contained in:
2026-08-05 22:06:01 +01:00
parent ad459e2b4b
commit 4535616799
6 changed files with 187 additions and 5 deletions
+56
View File
@@ -0,0 +1,56 @@
{...}: {
# Every filesystem on shadow lives here, hand-maintained.
#
# hardware.nix is generated with `--no-filesystems` precisely so it never
# emits fileSystems/swapDevices entries of its own. That keeps regeneration
# (`nix run .#sync-hardware`) safe: re-running it can never clobber these
# definitions, nor produce duplicates for whichever data drives happened to
# be mounted at the time it ran.
flake.nixosModules.shadowDisks = {...}: let
# Secondary data drive, addressed by filesystem label.
#
# noauto + x-systemd.automount makes systemd generate an .automount unit and
# mount on first access to the path rather than at boot — the two 2.7T
# spinners stay parked until something actually touches them. idle-timeout
# unmounts them again once quiet so they can spin back down.
#
# nofail matters even with automount: it stops a missing or dying disk from
# becoming a failed dependency that drops the boot into emergency mode.
#
# On-disk ownership is already ahmza mode 755 on all three, so no uid mapping
# is needed — ext4 stores ownership in the filesystem and ignores uid=/gid=.
dataDrive = label: {
device = "/dev/disk/by-label/${label}";
fsType = "ext4";
options = [
"nofail"
"noauto"
"x-systemd.automount"
"x-systemd.idle-timeout=600"
"x-systemd.device-timeout=10"
];
};
in {
fileSystems = {
# Boot essentials, by UUID. UUIDs rather than labels here on purpose:
# these two must resolve unambiguously, and a label is only as unique as
# the last disk someone plugged in.
"/" = {
device = "/dev/disk/by-uuid/deff63fc-1738-4cfc-a79c-06e58c78471a";
fsType = "ext4";
};
"/boot" = {
device = "/dev/disk/by-uuid/5873-AD05";
fsType = "vfat";
options = ["fmask=0077" "dmask=0077"];
};
"/mnt/dump" = dataDrive "DUMP";
"/mnt/games" = dataDrive "GAMES";
"/mnt/bak" = dataDrive "BAK";
};
swapDevices = [];
};
}