NixOS & modules
This page walks the NixOS side of the repo top to bottom: the flake, the host entry point, and every module under modules/.
The flake (flake.nix)
Inputs
| Input | Purpose |
|---|---|
nixpkgs | nixos-unstable — the package set and NixOS modules |
disko | declarative disk partitioning / ZFS layout |
sops-nix | encrypted secrets, decrypted at activation |
home-manager | per-user environment, as a NixOS module |
nixos-anywhere | remote bare-metal installer (used via nix run) |
Every secondary input follows nixpkgs, so the whole tree resolves against a single nixpkgs revision (fewer duplicate dependencies, faster evaluation).
Outputs
| Output | What it is |
|---|---|
nixosConfigurations.avocado | the full system (x86_64-linux), built for deploy/install |
homeConfigurations."rithviknishad@avocado" | standalone Home Manager profile for nh home switch |
devShells.default | the tool shell (nix develop) for all four common systems |
formatter | nixfmt (nix fmt) |
The dev shell puts just, nixos-rebuild, sops, age, ssh-to-age, mkpasswd, nixfmt, git, cloudflared, kubectl, kubernetes-helm, helmfile, and nixos-anywhere on PATH, and defaults SOPS_AGE_KEY_FILE to ~/.config/sops/age/keys.txt. Enter it with nix develop or let direnv load it automatically via .envrc.
Host entry point (hosts/avocado/)
default.nix
The host file is deliberately thin: it sets the hostname, enables NetworkManager, pins system.stateVersion = "26.11", and imports every module. Adding a capability to the box means adding one line here plus a module file.
imports = [
./hardware.nix ./disko.nix
../../modules/base.nix ../../modules/ssh.nix ../../modules/nh.nix
../../modules/sops.nix ../../modules/zfs.nix ../../modules/kiosk.nix
../../modules/home-manager.nix ../../modules/tailscale.nix
../../modules/k3s.nix ../../modules/monitoring.nix
../../modules/cloudflared.nix ../../modules/esphome.nix
../../users/rithviknishad.nix
];
system.stateVersiongoverns stateful defaults chosen at install time. Do not bump it casually after install.
hardware.nix
Boot and kernel specifics for this exact machine:
- initrd modules for the SATA/USB controllers (
ahci,xhci_pci,sd_mod, …). kvm-intelfor virtualization.- systemd-boot on UEFI, ESP at
/boot, keep the last 10 generations. - ZFS support enabled with
networking.hostId = "b288d857"(ZFS requires a unique 8-hex host id for pool ownership). - Intel microcode updates + redistributable firmware.
ondemandCPU governor.
Filesystems are not declared here — disko owns them.
The modules
Each module is small, commented, and does one job. Here’s what every one adds.
base.nix — shared system baseline
- Enables flakes and the modern
nixCLI;trusted-users = root, @wheel. - Automatic GC weekly, deleting generations older than 30 days;
auto-optimise-storeon. - Timezone
Asia/Kolkata, localeen_US.UTF-8,allowUnfree = true. - A small system package set:
git vim curl wget htop tmux rsync. - Firewall on (individual ports are opened by the modules that need them).
ssh.nix — OpenSSH server
Key-only login (PasswordAuthentication = false), root allowed only with a key (prohibit-password), firewall opened automatically. This is what nixos-anywhere and every nixos-rebuild --target-host rely on.
nh.nix — the “nix helper” workflow
Symlinks /etc/nixos → the in-home clone at /home/rithviknishad/systems.nix and points nh at it, so on the box you can run:
nh os switch # build + activate
nh os boot # stage for next boot
nh os build # build only, no sudo
sops.nix — secrets wiring
Imports the sops-nix module, sets secrets/avocado.yaml as the default source, and reads the host age key from /var/lib/sops-nix/key.txt. Declares the two secrets needed early at activation: the user’s hashed-password (neededForUsers) and the user’s SSH private key. Full model on the Secrets page.
zfs.nix — pool maintenance
- Weekly scrub and periodic trim.
services.zfs.autoSnapshotis enabled with a retention ladder (frequent/hourly/daily/weekly/monthly).
Heads-up: auto-snapshot only acts on datasets tagged
com.sun:auto-snapshot=true. The pool root sets it tofalse(disko.nix) and no dataset overrides it, so no automatic snapshots are taken today. To start snapshotting/home, set that property on thehomedataset. Scrub and trim run regardless.
kiosk.nix — stats display (cage + btop)
- The built-in screen is a kiosk: cage (a single-application Wayland compositor) starts at boot on tty1 and runs btop fullscreen inside a
footterminal. No desktop, no login screen, no screensaver — nothing to lock or idle. (This replaced the earlier GNOME desktop module.) - The session runs as a dedicated
kioskuser with no password, no SSH keys, and no sudo; the console exposes only btop. Admin access stays SSH (key-only). VT switching is left enabled (-s) so a getty on tty2 remains reachable with physical access if the network dies. - Never sleeps: the
sleep,suspend,hibernate, andhybrid-sleepsystemd targets are masked — critical because idle suspend once dropped the box off the network. - Enables graphics (Intel UHD 620), installs DejaVu for foot, and adds
btop- foot’s terminfo system-wide.
home-manager.nix — Home Manager as a NixOS module
Imports the home-manager NixOS module, uses global pkgs / user packages, backs up clobbered dotfiles with a .hm-bak suffix, and mounts the home/rithviknishad profile for the user. See Home Manager.
tailscale.nix — private mesh networking
Declarative Tailscale: auto-authenticates from a sops-managed auth key, useRoutingFeatures = "both", trusts the tailscale0 interface in the firewall, and sets reverse-path filtering to loose. See Networking.
k3s.nix — single-node Kubernetes
k3s as a server with clusterInit = true (embedded etcd, so more servers/agents can join later for HA). Token from sops; API cert issued for the tailnet name via --tls-san. Bundled Traefik / local-path / ServiceLB left on. Opens 6443 (API), 2379/2380 (etcd), 10250 (kubelet), and UDP 8472 (flannel VXLAN). Installs kubectl, helm, and k9s on the box. Details on the Kubernetes page.
cloudflared.nix — Cloudflare Tunnel
Runs a named tunnel that maps public subdomains of rithviknishad.dev (hello, photos, grafana, status, esphome) to http://localhost:80 (Traefik), with a default http_status:404. Credentials come from a sops binary secret. See Networking.
docker.nix — local image builds
Docker as a build tool only — some k8s workloads need images upstream doesn’t publish in a usable form (the care backend bakes plugins in at image build time; care_fe compiles the API URL into the bundle). The care-images justfile recipes docker build them on the box and import the result straight into k3s’s containerd (k3s ctr images import) — no registry, same idea as the Nix-built bingo image. Weekly autoPrune keeps build-cache layers from eating the pool. Nothing runs under docker; workloads live in k3s.
esphome.nix — host-side ESPHome networking
The ESPHome dashboard runs in k3s with hostNetwork (ESPHome); this module opens inbound UDP 5353 so mDNS responses from ESP devices on the LAN reach it, and TCP 6052 on the cni0 bridge only so in-cluster clients (Traefik ingress, Gatus probe) can reach the host-bound dashboard port. 6052 is deliberately not opened on the LAN — the UI has no auth; it’s reached via Tailscale or Cloudflare Access.
monitoring.nix — host-side metrics glue
The in-cluster monitoring stack can’t read per-pool ZFS health or SMART from inside a pod, so this module runs two systemd timers that write Prometheus textfile metrics to /var/lib/node-exporter/textfile, which node-exporter mounts read-only:
| Timer | Cadence | Emits | Feeds |
|---|---|---|---|
zfs-textfile-metrics | every 1 min | node_zfs_zpool_state | ZFS pool-health alerts |
smart-textfile-metrics | every 5 min | smartmon_* (health, temp, power-on hours) | SMART disk alerts |
Both scripts write to a temp file, chmod 0644 (node-exporter runs unprivileged and needs world-readable files), then atomically mv into place so the collector never reads a partial file. The SMART script probes several smartctl -d access types to handle SATA drives behind the AHCI controller. This is the host half of the Monitoring stack.
The system user (users/rithviknishad.nix)
users.mutableUsers = false— accounts are fully declarative; nopasswd-ing on the box.- User
rithviknishad: groupswheel(sudo),networkmanager,dialout; login shell zsh; password hash comes from sops (hashedPasswordFile). - Authorized SSH keys for the user and for
root(root keys letnixos-anywhereconnect during install). Two keys are trusted: the owner’s and a collaborator’s (tellmeY18).