The VMs now declare their root disk as a DataVolume with `source: upload`, so
upload straight into `<vm>-disk` (CDI only accepts an upload into a DataVolume)
instead of a shared golden PVC. Takes the disk size as an argument.
* feat(k3s-test-1): add three worker nodes and taint the control plane
- hosts/k8s/k3s-test-1-worker-{1,2,3} (k3s agents) + flake outputs
- vars/networking: .117/.118/.119 (the .111-.113 range is still held by the
never-deployed k3s-prod-1 workers)
- k3s-test-1-master-{1,2,3}: nodeTaints=node-role.kubernetes.io/control-plane:NoSchedule
- Justfile: upload the worker images too
* chore(k3s-test-1): move the workers to .111-.113
The k3s-prod-1 entries that held those addresses are gone (nix-config#284).
It was never bootstrapped (no flux deploy key, VMs commented out since
2024-10) and only carried a stale duplicate of the test cluster's config.
- hosts/k8s/k3s-prod-1-{master,worker}-{1,2,3} + flake outputs + the home module
- the k3s-prod-1-token agenix entry (the .age file lives in the private secrets repo)
- the k3s-prod-1-* IPs, the offlineHosts entries, the Justfile recipes and the docs
* feat(kubevirt-youko): export a shared golden-image store over NFS
* feat(vm-images): upload golden images into the cluster
Replace the rsync-to-orangepi5 step with a CDI upload into the golden-image
PVCs (vms/golden-images in k8s-gitops), so VM images no longer depend on the
single-board file server.
services.earlyoom.freeMemThresholdPercent does not exist in this nixpkgs; the module exposes freeMemThreshold/freeSwapThreshold. Just enable earlyoom with its defaults (trigger at 10% free mem/swap).
* refactor(kubevirt): drop the static 1G hugetlb pool, use THP on demand
The hosts pre-reserved 48/48/15 GiB of 1G hugepages, but the VMs only used 32/16/11 GiB, so ~52 GiB was stranded (hugetlb is exclusive and cannot be overcommitted or shared with the host / non-hugepage VMs). It also blocked scheduling (a 16 GiB hugepage VM cannot fit on a host with fewer free pages, e.g. youko with 15/24 GiB) and made the full intended VM set (idols 3x8G + k3s-test 3x16G + k3s-prod 3x4G + 3x16G = 132 GiB vs 146 GiB physical) impossible to fit.
Use transparent huge pages on demand (madvise) instead, and let the VMs use ordinary memory (instancetypes updated in k8s-gitops). This frees the stranded memory, removes the scheduling rigidity, and makes overcommit/swap usable again.
* feat(kubevirt): add earlyoom; stop overriding swappiness (zram already enabled)
zram is already provided by modules/nixos/base/zram.nix (enabled by default: 50% RAM, zstd, prio 100 + swappiness/watermark tunings). But genKubeVirtHostModule hard-coded vm.swappiness = 0, which overrode the modules mkDefault 180 and disabled swapping entirely - so the zram device was never used (Swap used: 0B).
- Remove the hard-coded vm.swappiness (let the zram modules tuning apply); the disk swapfile from disko stays as the last resort.
- Add services.earlyoom to kill the greediest process before the host thrashes / OOMs.
vmagent (and other senders) can deliver the same series+timestamp twice (retries/HA), which vmsingle rejected with "duplicate sample for timestamp; overrides not allowed". Set -dedup.minScrapeInterval=30s (>= the senders scrapeInterval) so duplicates collapse instead of being rejected.
- genKubeVirtHostModule: replace the Open vSwitch bridge (ovsbr1) with a Linux
bridge br0 for the VMs secondary network; the bridge CNI plugin attaches the VM
taps to br0 and the host keeps its address on br0. Drop the now-unused
multus-cni host package.
- kubevirt hosts: stop forcing disableFlannel = false, so k3s disables flannel and
Cilium provides the pod network.
shoukei is a laptop using DHCP and never consumes hostsAddr.shoukei.
Its 192.168.5.108 collided with k3s-prod-1-master-1 and produced a wrong
ssh Host shoukei entry.
The Settings UI stored `theme.mode` in ~/.local/state/noctalia/settings.toml, so it was not versioned. Pin it in the config baseline; the [location] sunrise/sunset schedule still drives light/dark.
At background_opacity 0.2 the light-mode surface disappears into dark wallpapers, so the dark light-mode text sits on a dark bar. Raise it to 0.61 to keep contrast while staying slightly translucent.
`programs.noctalia.settings` deploys the TOML as a store symlink, so every edit needs a `home-manager switch`. Deploy the baseline from the config layer with `mkOutOfStoreSymlink` instead: Noctalia hot-reloads config-layer files and never rewrites them, so edits apply live while Settings UI changes stay in ~/.local/state/noctalia/settings.toml (which loads last) and out of the repo.
Update the docs and host-specific comments to match the two-layer model.
The sandbox remaps XDG_DATA_HOME/XDG_CONFIG_HOME, so paths Firefox exposes to
or shares with the host ended up isolated. Bind the MPRIS artwork dir and the
real trash rw, and mimeapps.list, user-dirs.dirs and the shared NSS cert store
ro.
recently-used.xbel stays isolated: GTK writes it atomically, which a
single-file rw bind would break.
Add cpu, ram, disk and network up/down sysmon widgets to the bar start lane,
matching the old v4 SystemMonitor. Set the clock to ISO date, full English
weekday and 24h time (e.g. "2026-09-19 Saturday 16:06").
English stays the default (LANG=en_US.UTF-8). Drop the Chinese overrides for
LC_TIME, LC_NUMERIC, LC_ADDRESS, LC_NAME, LC_TELEPHONE and LC_IDENTIFICATION:
LC_NUMERIC matches en_US, the others are effectively unused, and LC_TIME forced
Chinese date and weekday names.
Keep zh_CN only where the en_US default is wrong for China: metric units, A4
paper and CNY.
Refresh the repository overview after the move to Noctalia v5:
- fix the component table: Noctalia as the desktop shell, native screenshots, systemd-networkd/NetworkManager, Zed & VS Code as GUI editors, Zellij as a multiplexer
- correct the deploy instructions (niri hosts use 'just niri'; 'just local' matches the bare hostname) and the Nvidia link
- update the NixOS badge to 26.11 and add a short validation section
- root README: VS Code config moved from home/linux/gui/base/editors.nix to vscode.nix
- READMEs now list both GUI editors (Zed, the primary one, plus VS Code)
- base README: xdg is a directory now, nvidia.nix no longer exists, editors.nix -> vscode.nix
- idols-ai README: disko-fs-media.nix -> disko-fs-data.nix (/persistent/data)
- secrets README: password-store moved from home/base/desktop to home/base/tui
- drop unused link definitions (AstroNvim, wl-clipboard, Catppuccin)
- fix a pre-existing typo flagged by the typos hook (keeped -> kept)
Noctalia v5 is the native C++/Wayland rewrite; the Quickshell-based v4 line (pkgs.noctalia-shell) is EOL and its JSON config is not migrated upstream.
- use the upstream home-manager programs.noctalia module (present at our pinned HM revision) instead of the hand-rolled package + symlink module: it installs pkgs.noctalia, validates config.toml at build time via checkConfig, and supports customPalettes; drop the v4 Qt/Qt6ct session setup
- replace the v4 settings.json/plugins.json with a minimal shared config.toml (font, Catppuccin shell palette, templates off, screenshot annotation, accessibility/bar scale, night light, wallpaper, location/weather); host-specific bits live in per-host files (shoukei's eDP-1 backlight in noctalia/host-shoukei.toml), which merge after config.toml. Application theming stays with catppuccin/nix
- treat config.toml as the single source of truth: do NOT persist ~/.local/state/noctalia and set setup_wizard_enabled = false, so UI tweaks are promoted back into the repo instead of accumulating in app-owned state
- port niri keybinds to 'noctalia msg ...', update spawn-at-startup and the backdrop layer namespace, fix the broken 'octalia-shell' media-previous bind, and replace the grim/slurp/satty screenshot stack with Noctalia's native region/fullscreen/annotate commands (drop the unused packages)
- drop the now-redundant swaybg and swaylock (Noctalia owns the wallpaper and lock screen, the latter authenticating against the PAM 'login' service) and its PAM entry; keep wlogout as an emergency fallback
- update hypridle's lock commands and the matching eval test
- refresh the noctalia docs
networkd enables systemd-resolved by default, and resolved's own mDNS responder conflicts with avahi (duplicate answers, avahi logs 'Detected another mDNS stack running on this host'). Keep avahi for DNS-SD (cups-browsed depends on it) and turn resolved's mDNS off.
home.sessionVariables writes export NAME="value" without escaping embedded double quotes, so the JSON {"share":"disabled"} reached opencode as the shell word {share:disabled} and failed config parsing. Emit the export with sessionVariablesExtra instead.
The 2026-09 nixpkgs update changed DRM probe order, renaming the Intel connectors DP-1 -> DP-4 and HDMI-A-2 -> HDMI-A-3. The connector-name based output and open-on-output rules stopped matching, which dropped monitor scale/mode/position and sent every named workspace to the first output. Match by make/model/serial instead, which is stable across connector renumbering.
Tencent replaced the file behind the versionless CDN URLs, so the pinned hashes stopped matching. Refresh the x86_64 and aarch64 hashes with nix store prefetch-file.
trash-empty lacked Persistent=true, so a host that was off or suspended at the scheduled time never ran the purge and trash retention silently stopped.
Guix was enabled on every desktop host, but shoukei had no /gnu mount (store and state lived on the tmpfs root) and it was not used. Drop the module, the modules.guix option, the @guix subvolume and the /var/guix preservation entry.
The restic job needs three agenix secrets that do not exist yet (restic-password, ssh-key-for-restic-backup and an rclone config for the smb-downloads remote). Comment it out until they are provisioned so it does not fail silently every night.
Load both *.yml and *.yaml alert rule files: the Loki rules were named .yaml while only *.yml was loaded, so they silently never ran. Fix the Grafana domain typo (writefo.fun -> writefor.fun). Comment out the postgres-playground datasource, which used the Grafana admin password for a role that has no password and could never authenticate.
checks.<system>.eval-tests returned a boolean, so nix flake check failed with "not a derivation". Wrap the evalTests result in a runCommand so flake check actually evaluates it.
* feat(btrbk): make snapshots explicit and guarded
Replace the always-on, hardcoded module with modules.btrbk options (enable/volume/subvolume/snapshotDir/target/onCalendar), and assert the btrfs top-level subvolume is mounted so a missing mount fails evaluation instead of silently failing at runtime.
Store snapshots in the @snapshots subvolume (snapshot_dir=@snapshots) rather than next to the live subvolume, make the off-host target explicit and off by default, and document that local snapshots are not off-host backups.
Enable the feature explicitly on idols-ai, shoukei and the three kubevirt hosts; qemu-guest now disables it through the option. Add eval tests for shoukei and ai.
* feat(monitoring): alert on btrbk snapshot failure
Add HostBtrbkSnapshotFailed on node_systemd_unit_state for btrbk-btrbk.service, so a broken snapshot schedule alerts instead of only surfacing in the journal.
RTK compresses only shell output and independent evaluations show little to no end-to-end cost reduction. Drop the package and its install snippets; runtime artifacts were uninstalled separately.
Use one wording per gating family: exact target and scope, recovery path or safety guard, and explicit authorization. Remove the home-directory instance from workspace access and the repeated approval gate from the breaking-change bullet. Sync the high-impact eval scenario wording.