|
|
||
|---|---|---|
| .claude/skills/ponytail | ||
| .forgejo/workflows | ||
| apps | ||
| home-manager | ||
| hosts | ||
| modules/crowdsec | ||
| scripts | ||
| services | ||
| settings | ||
| templates | ||
| walls | ||
| .mcp.json | ||
| backup-server.sh | ||
| CLAUDE.md | ||
| common.nix | ||
| flake.lock | ||
| flake.nix | ||
| ports.toml | ||
| readme.md | ||
FredOS NixOS Configuration
Flake-based NixOS configuration for three machines, built and deployed directly from the Forgejo repo at https://forg.gregersen.it/rope/nixos. No local checkout required after initial setup.
Machines
| Hostname | Description |
|---|---|
| FredOS-Gaming | AMD desktop, UEFI/systemd-boot, CachyOS kernel |
| FredOS-Macbook | Intel laptop, UEFI/systemd-boot |
| FredOS-Mediaserver | Intel server, UEFI/systemd-boot — media services and the home router |
Structure
├── .forgejo
│ └── workflows
│ └── update.yml # Auto-updates flake.lock daily (self-hosted runner)
├── apps
│ └── zen.nix # Zen browser (flake input)
├── home-manager
│ └── fred.nix # User-level Home Manager config
├── hosts
│ ├── FredOS-Gaming.nix # Gaming: packages, Steam, boot options
│ ├── FredOS-Macbook.nix # Macbook: packages, power management, DWT daemon
│ ├── FredOS-Mediaserver.nix # Mediaserver: packages, SSH, auto-upgrade
│ └── hardware
│ ├── FredOS-Gaming.nix # AMD GPU, CachyOS kernel overlay, filesystems, hostname
│ ├── FredOS-Macbook.nix # Broadcom WiFi, Intel GPU, filesystems, hostname
│ └── FredOS-Mediaserver.nix # NVIDIA NVENC, data disks, mergerfs pool, hostname
├── modules
│ └── crowdsec # Vendored crowdsec modules (nixpkgs PR #446307, still open)
├── scripts # Helper scripts wrapped as packages on the mediaserver
├── services
│ ├── adguard.nix # Network-wide DNS ad blocking
│ ├── arr-interconnect.nix # Cross-service API key wiring for *arr apps
│ ├── authelia.nix # SSO/2FA gateway for the nginx vhosts
│ ├── bazarr.nix # Subtitle management
│ ├── bazarr-sync.nix # Subtitle sync timers (podman container)
│ ├── cloudflare-ddns.nix # Cloudflare dynamic DNS
│ ├── code-server.nix # Browser-based VS Code IDE
│ ├── crowdsec.nix # Intrusion prevention + nftables bouncer + ntfy alerts
│ ├── dr-server.nix # Dungeon Runners game server (Wine) — currently disabled
│ ├── forgejo-runner.nix # CI runner for forg.gregersen.it
│ ├── frigate.nix # NVR with object detection
│ ├── game-servers.nix # Dockerised 7 Days to Die servers
│ ├── go2rtc.nix # Camera/RTSP streaming
│ ├── homepage.nix # Homepage dashboard with auto-extracted API keys
│ ├── jellyfin.nix # Media server
│ ├── memos.nix # Flatnotes notes app (container)
│ ├── nginx.nix # Reverse proxy + ACME wildcard cert via Cloudflare DNS-01
│ ├── profilarr.nix # Quality profile manager for *arr apps (container)
│ ├── prowlarr.nix # Indexer manager
│ ├── qbittorrent-nox.nix # Torrent client
│ ├── radarr.nix # Movie management
│ ├── router.nix # Mediaserver as home router (NAT, DHCP, nftables)
│ ├── sabnzbd.nix # Usenet downloader
│ ├── server-permissions.nix # Shared media dir permissions
│ └── sonarr.nix # TV management
├── settings
│ ├── audio.nix # PipeWire / audio config
│ ├── desktop.nix # Display manager, theming, flatpak
│ ├── hyprland.nix # Hyprland compositor config (Lua), anyrun
│ ├── quickshell.nix # Quickshell bar/notifications (QML)
│ ├── locale.nix # Locale, timezone, keyboard
│ ├── shell.nix # Fish shell, powerline prompt, fastfetch, nerd fonts
│ ├── stylix.nix # Unified colour theming (wallpaper-derived palette)
│ └── users.nix # User accounts, SSH keys
├── templates # CSS templates recoloured by stylix.nix
├── walls # Wallpapers
├── backup-server.sh # One-shot mediaserver state backup (run manually)
├── ports.toml # WAN → LAN port forwards consumed by router.nix
├── common.nix # Shared config imported by all hosts
├── flake.lock # Auto-generated, updated daily by Forgejo Actions
└── flake.nix # Flake inputs and host definitions
Day-to-day usage
Edit files in the Forgejo repo (or locally and push), then on the machine run:
update
The alias (defined in common.nix) runs sudo nixos-rebuild switch --refresh --flake git+https://forg.gregersen.it/rope/nixos with nix-output-monitor, then shows an nvd diff of what changed. The mediaserver also auto-upgrades daily at 05:15 (system.autoUpgrade).
Other useful aliases:
clean # sudo nix-collect-garbage -d
Adding a new machine
1. Fresh NixOS install
Boot the NixOS installer and complete the standard installation.
2. Enable flakes temporarily
(The flake config enables them declaratively, but the stock installer config doesn't.) Add to /etc/nixos/configuration.nix and rebuild:
nix.settings.experimental-features = [ "nix-command" "flakes" ];
3. Create the hardware config in the repo
Copy /etc/nixos/hardware-configuration.nix to hosts/hardware/FredOS-NEWHOST.nix and append the hostname and bootloader config:
networking.hostName = "FredOS-NEWHOST";
boot.loader.systemd-boot.enable = true;
boot.loader.efi.canTouchEfiVariables = true;
4. Register the host in flake.nix
FredOS-NEWHOST = mkHost "FredOS-NEWHOST" [];
5. Add host-specific config
Create hosts/FredOS-NEWHOST.nix for machine-specific packages or services. mkHost imports it automatically — no changes to common.nix needed.
6. Switch to the flake
Run once with the explicit hostname:
sudo nixos-rebuild switch --refresh --flake git+https://forg.gregersen.it/rope/nixos#FredOS-NEWHOST
After this succeeds, the plain update alias works from then on.
Flake inputs
| Input | Source |
|---|---|
| nixpkgs | github:NixOS/nixpkgs/nixos-26.05 |
| home-manager | github:nix-community/home-manager/release-26.05 |
| stylix | github:nix-community/stylix/release-26.05 |
| zen-browser | github:0xc000022070/zen-browser-flake |
| nix-cachyos-kernel | github:xddxdd/nix-cachyos-kernel/release (own nixpkgs pin — keeps their kernel binary cache usable) |
| proton-cachyos-nix | github:powerofthe69/proton-cachyos-nix |
Mediaserver secrets
Several services on FredOS-Mediaserver require secrets stored on the machine (not in the repo). After a fresh deploy, create these before running update:
# Cloudflare API token (used by DDNS and ACME wildcard cert)
echo -n 'your-cloudflare-api-token' | sudo tee /var/secrets/cloudflare-token
sudo chmod 600 /var/secrets/cloudflare-token
# go2rtc RTSP camera URL
echo -n 'rtsp://username:password@camera-ip:554/stream1' | sudo tee /var/secrets/go2rtc-rtsp-url
sudo chmod 600 /var/secrets/go2rtc-rtsp-url
# CrowdSec ntfy.sh alert topic (injected into the notification config at service start)
echo 'https://ntfy.sh/your-private-topic' | sudo tee /var/secrets/ntfy-url
sudo chmod 600 /var/secrets/ntfy-url
# Forgejo Actions runner registration token (one-time use, KEY=value format)
echo 'TOKEN=YOUR_REGISTRATION_TOKEN' | sudo tee /var/secrets/forgejo-runner-token
sudo chmod 600 /var/secrets/forgejo-runner-token
# Authelia secrets — readable by the authelia-main group
sudo mkdir -p /var/secrets/authelia
echo -n 'random-jwt-secret' | sudo tee /var/secrets/authelia/jwt_secret
echo -n 'random-session-secret' | sudo tee /var/secrets/authelia/session_secret
echo -n 'random-storage-encryption-key' | sudo tee /var/secrets/authelia/storage_encryption_key
sudo chown root:authelia-main /var/secrets/authelia/*
sudo chmod 640 /var/secrets/authelia/*
# Authelia user database
# Create users_database.yml with this structure:
# ---
# users:
# username:
# password: "$argon2id$..." # hashed — see below
# displayname: Display Name
# email: user@example.com
#
# Generate a password hash with:
# nix-shell -p authelia --run "authelia crypto hash generate argon2"
sudo mkdir -p /var/lib/authelia-main
sudo nano /var/lib/authelia-main/users_database.yml
sudo chown authelia-main:authelia-main /var/lib/authelia-main/users_database.yml
Migrating to a new server
When moving FredOS-Mediaserver to new hardware, back up these state directories from the old server (backup-server.sh automates most of it):
# Service databases and config (stop services first)
/var/lib/jellyfin/ # Library database, users, metadata, API keys
/var/lib/sonarr/ # TV library database, config.xml (API key)
/var/lib/radarr/ # Movie library database, config.xml (API key)
/var/lib/prowlarr/ # Indexer database, config.xml (API key)
/var/lib/bazarr/ # Subtitle database and config
/var/lib/qbittorrent/ # Torrent client config and state
/var/lib/authelia-main/ # User database and session storage
# Secrets
/var/secrets/ # See "Mediaserver secrets" above
# Media files
/mnt/storage/ # The mergerfs pool (torrents, media libraries, audiobooks)
Steps:
- Install NixOS on the new server
- Update
hosts/hardware/FredOS-Mediaserver.nixfrom the new/etc/nixos/hardware-configuration.nix(new disk UUIDs, bootloader config) - Set up the mergerfs pool and mount at
/mnt/storage - Restore
/var/secrets/(see Mediaserver secrets section above) - Run
sudo nixos-rebuild switch --refresh --flake git+https://forg.gregersen.it/rope/nixos#FredOS-Mediaserver - Stop all services, restore the
/var/lib/directories listed above, then start services - Update Cloudflare DNS if the server's public IP changed
If starting fresh instead of migrating, the services self-initialize with empty databases — redo initial setup in each web UI; arr-interconnect auto-wires the connections between them.
Notes
- The mediaserver is also the home router:
services/router.nixowns nftables NAT/firewall andnetworking.firewallis disabled on that host. WAN exposure is controlled solely byports.toml; LAN traffic is trusted wholesale. hosts/hardware/files are committed — they contain UUIDs and disk layout but no sensitive credentials- Host-specific behaviour is gated with
lib.mkIf (config.networking.hostName == "...")orlib.elem config.networking.hostName [...]; host modules themselves are selected bymkHostinflake.nix modules/crowdsec/vendors the crowdsec module rewrite from nixpkgs PR #446307 — delete it (and thedisabledModules/importslines inservices/crowdsec.nix) once that PR lands in the pinned channel