Caddy only proxies HTTP; git-over-ssh to gitea needs a raw TCP forward
since gitea's built-in SSH server (jupiter:2222) isn't otherwise reachable
from the public internet. socat forwards the VPS's public :2222 over the
tailnet. Matches what's now live on the (still-Debian) VPS - ready to drop
in once neptun gets migrated to this NixOS config.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
- sabnzbd, prowlarr, sonarr, radarr, clonarr, seerr, cinephage, mediamanager
services, wired into jupiter with LAN Caddy vhosts.
- Gitea: migrated the old ZimaOS docker instance's data (sqlite db, 4 repos,
no LFS objects) into the NixOS module's default stateDir layout. HTTP via
Caddy; git SSH on its own built-in server at :2222 (not :222 - the unpriv
gitea user can't bind <1024).
- mediamanager-nix flake input for the mediamanager service.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
- services/pihole.nix: official pihole/pihole:2026.07.2 via podman, host net,
caps NET_ADMIN/NET_RAW/SYS_NICE/CHOWN; FTLCONF_* env config (upstream unbound,
DHCP 50-200, static lease jupiter, .sol domain, local records)
- unbound: resolveLocalQueries=false (was hijacking resolv.conf to :53 -> boot
DNS deadlock; the real root cause of the earlier failures too)
- password via sops FTLCONF env file; /var/lib/pihole created via tmpfiles
- VM-verified: mercury.sol/jupiter.sol/external all resolve, 0 restarts
mercury is the DHCP server (no lease of its own) so its name wasn't resolvable.
Add explicit dns.hosts A records. VM-verified: both resolve, external unaffected.
Pi's vfat partition isn't mounted at runtime (u-boot reads it pre-boot), so
/boot/firmware doesn't exist -> keyFile moved to the always-mounted root fs.
deploy flash now drops it on the ext4 root partition.
pihole.toml is nix-managed read-only so 'pihole setpassword' fails. pihole-FTL
reads FTLCONF_* env vars (override the toml) — render an env file from the sops
secret pihole_webpassword and feed it via EnvironmentFile. Password stays out of
repo/store. VERIFIED in VM: FTLCONF_webserver_api_password -> API login works.
- after dd, if ~/.config/homelab/<config>/age.txt exists, mount the FAT boot
partition and drop it as sops-age.txt (mercury). Key stays off-repo + out of
the store + out of the image; no manual mount step.
- per-host darman_password (distinct hash) in secrets/{jupiter,vps,mercury}.yaml
-> hashedPasswordFile; different console password per host (ssh still key-only)
- mercury: dedicated age key (on boot partition post-flash), sops-nix wired
- AdGuard: module has no secret hook + writable config -> mutableSettings=true,
admin password set via web setup on first boot (never in repo/store)
- nixosConfigurations.mercury: aarch64, sd-image-aarch64, imports common.nix
- static net placeholders (CHANGE-ME), hostname mercury
- DNS service left undecided: commented pihole (services.pihole-ftl) + adguardhome
- no sops yet (add with the service if it needs a secret)
- services/{samba,avahi,audiobookshelf,containers,caddy,tailscale}.nix
- common.nix grows firewall base + timezone; hosts import what they need
- jupiter/vm/vps import service modules; drop the jupiter/services.nix monolith
- each module opens its own firewall ports; caddy/tailscale shared by hosts
- verified: jupiter/vps/vbox eval + jupiter builds, config equivalent
- services.tailscale auto-registers with headscale using a sops pre-auth key
- trust tailscale0 so LAN services are reachable over the tailnet
- fix missing semicolon on audiobookshelf extraGroups
Installed initrd lacked mmc_block -> can't mount root on eMMC -> no boot.
generate-config runs in the RAM installer and misses mmc modules, and it
overwrites hardware-configuration.nix, so pin them where they merge instead.
kexec/run rebuilds an initrd via cpio|gzip from PATH — absent on ZimaOS, so run
aborted before kexec and the box stayed on ZimaOS. Ship GNU static cpio + busybox
(as gzip) into /tmp/bin, prepend PATH. SSH ControlMaster = one password prompt.
- add nixos-images input; nixosConfigurations.kexec bakes in the ssh login key
- build via config.system.build.kexecInstallerTarball
- deploy: ./deploy kexec <host> streams the installer to /tmp and kexecs
- works around ZimaOS RO root where nixos-anywhere ssh-copy-id fails
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- add darman_password (sha-512 hash) to secrets/jupiter.yaml
- neededForUsers so it lands in /run/secrets-for-users before user setup
- real host uses hashedPasswordFile; VM keeps its test initialPassword
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- disko OS target = internal eMMC /dev/disk/by-id/mmc-C9A551_0xaa057ad0
- mount existing ext4 on mdadm RAID0 (md0) at /mnt/data by fs-uuid, nofail
- boot.swraid.enable to assemble the array; data disk kept out of disko
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- add sops-nix input + module (jupiter only, not the VM)
- secrets/jupiter.yaml: age-encrypted samba_password (safe to commit)
- .sops.yaml: encryption rule for admin age key
- secrets.nix: decrypt samba_password to /run/secrets on the host
- provisioning oneshot reads sops secret (host) or plaintext (VM), single value
- .sops private key stays off-repo (~/.config, /var/lib/sops-nix on host)
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- add disko input; jupiter partitions/formats OS disk declaratively
- hardware-configuration.nix carries kernel modules only (disko owns fileSystems)
- data disk stays a plain unformatted mount, out of disko
- vbox unchanged (virtualbox-image supplies its own disk)
- README: nixos-anywhere remote install + daily rebuild loop
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- systemd oneshot sets SMB password after samba-smbd (activation ran too early)
- caddy vhost reverse_proxy to whoami so :80 actually serves
- vm.nix: throwaway SMB secret for testing; real host uses sops/agenix
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>