2026-03-28 15:57:10 +00:00
# FredOS NixOS Configuration
2026-06-11 10:00:02 +01:00
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.
2026-03-28 15:57:10 +00:00
2026-03-28 16:02:40 +00:00
## Machines
2026-03-28 15:57:10 +00:00
2026-03-28 16:02:40 +00:00
| Hostname | Description |
|---|---|
2026-06-11 10:00:02 +01:00
| FredOS-Gaming | AMD desktop, UEFI/systemd-boot, CachyOS kernel |
2026-03-28 16:02:40 +00:00
| FredOS-Macbook | Intel laptop, UEFI/systemd-boot |
2026-06-11 10:00:02 +01:00
| FredOS-Mediaserver | Intel server, UEFI/systemd-boot — media services **and** the home router |
2026-03-28 15:57:10 +00:00
2026-03-28 16:02:40 +00:00
## Structure
2026-03-28 15:57:10 +00:00
```
2026-06-11 10:00:02 +01:00
├── .forgejo
2026-03-28 19:25:26 +00:00
│ └── workflows
2026-06-11 10:00:02 +01:00
│ └── update.yml # Auto-updates flake.lock daily (self-hosted runner)
2026-04-01 21:14:16 +01:00
├── apps
2026-06-11 10:00:02 +01:00
│ └── zen.nix # Zen browser (flake input)
2026-04-01 21:14:16 +01:00
├── home-manager
2026-06-11 10:00:02 +01:00
│ └── fred.nix # User-level Home Manager config
2026-03-28 19:25:26 +00:00
├── hosts
2026-04-01 21:14:16 +01:00
│ ├── FredOS-Gaming.nix # Gaming: packages, Steam, boot options
2026-06-11 10:00:02 +01:00
│ ├── FredOS-Macbook.nix # Macbook: packages, power management, DWT daemon
│ ├── FredOS-Mediaserver.nix # Mediaserver: packages, SSH, auto-upgrade
2026-03-28 19:25:26 +00:00
│ └── hardware
2026-06-11 10:00:02 +01:00
│ ├── 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
2026-04-01 21:14:16 +01:00
├── services
2026-05-16 12:27:19 +01:00
│ ├── adguard.nix # Network-wide DNS ad blocking
2026-04-07 20:42:19 +01:00
│ ├── arr-interconnect.nix # Cross-service API key wiring for *arr apps
2026-06-11 10:00:02 +01:00
│ ├── authelia.nix # SSO/2FA gateway for the nginx vhosts
2026-04-01 21:14:16 +01:00
│ ├── bazarr.nix # Subtitle management
2026-06-11 10:00:02 +01:00
│ ├── bazarr-sync.nix # Subtitle sync timers (podman container)
2026-04-01 21:14:16 +01:00
│ ├── cloudflare-ddns.nix # Cloudflare dynamic DNS
2026-05-16 12:27:19 +01:00
│ ├── code-server.nix # Browser-based VS Code IDE
2026-06-11 10:00:02 +01:00
│ ├── 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
2026-04-01 21:14:16 +01:00
│ ├── go2rtc.nix # Camera/RTSP streaming
2026-04-07 20:42:19 +01:00
│ ├── homepage.nix # Homepage dashboard with auto-extracted API keys
2026-04-01 21:14:16 +01:00
│ ├── jellyfin.nix # Media server
2026-06-11 10:00:02 +01:00
│ ├── memos.nix # Flatnotes notes app (container)
2026-04-07 20:42:19 +01:00
│ ├── nginx.nix # Reverse proxy + ACME wildcard cert via Cloudflare DNS-01
2026-06-11 10:00:02 +01:00
│ ├── profilarr.nix # Quality profile manager for *arr apps (container)
2026-04-01 21:14:16 +01:00
│ ├── prowlarr.nix # Indexer manager
│ ├── qbittorrent-nox.nix # Torrent client
│ ├── radarr.nix # Movie management
2026-05-16 12:27:19 +01:00
│ ├── router.nix # Mediaserver as home router (NAT, DHCP, nftables)
│ ├── sabnzbd.nix # Usenet downloader
2026-06-11 10:00:02 +01:00
│ ├── server-permissions.nix # Shared media dir permissions
2026-04-01 21:14:16 +01:00
│ └── sonarr.nix # TV management
├── settings
│ ├── audio.nix # PipeWire / audio config
2026-06-11 10:00:02 +01:00
│ ├── desktop.nix # Display manager, theming, flatpak
│ ├── hyprland.nix # Hyprland compositor config (Lua), anyrun
│ ├── quickshell.nix # Quickshell bar/notifications (QML)
2026-04-01 21:14:16 +01:00
│ ├── locale.nix # Locale, timezone, keyboard
2026-04-08 14:03:34 +01:00
│ ├── shell.nix # Fish shell, powerline prompt, fastfetch, nerd fonts
2026-05-16 12:27:19 +01:00
│ ├── stylix.nix # Unified colour theming (wallpaper-derived palette)
2026-06-11 10:00:02 +01:00
│ └── users.nix # User accounts, SSH keys
├── templates # CSS templates recoloured by stylix.nix
2026-03-28 19:25:26 +00:00
├── walls # Wallpapers
2026-06-11 10:00:02 +01:00
├── backup-server.sh # One-shot mediaserver state backup (run manually)
├── ports.toml # WAN → LAN port forwards consumed by router.nix
2026-04-01 21:14:16 +01:00
├── common.nix # Shared config imported by all hosts
2026-06-11 10:00:02 +01:00
├── flake.lock # Auto-generated, updated daily by Forgejo Actions
2026-03-28 19:25:26 +00:00
└── flake.nix # Flake inputs and host definitions
2026-03-28 15:57:10 +00:00
```
2026-03-28 16:02:40 +00:00
## Day-to-day usage
2026-03-28 15:57:10 +00:00
2026-06-11 10:00:02 +01:00
Edit files in the Forgejo repo (or locally and push), then on the machine run:
2026-03-28 15:57:10 +00:00
2026-03-28 16:02:40 +00:00
```bash
update
```
2026-03-28 15:57:10 +00:00
2026-06-11 10:00:02 +01:00
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` ).
2026-03-28 16:02:40 +00:00
Other useful aliases:
```bash
clean # sudo nix-collect-garbage -d
```
2026-03-28 15:57:10 +00:00
---
## Adding a new machine
### 1. Fresh NixOS install
2026-04-01 21:14:16 +01:00
Boot the NixOS installer and complete the standard installation.
2026-03-28 15:57:10 +00:00
2026-03-28 16:02:40 +00:00
### 2. Enable flakes temporarily
2026-03-28 15:57:10 +00:00
2026-06-11 10:00:02 +01:00
(The flake config enables them declaratively, but the stock installer config doesn't.) Add to `/etc/nixos/configuration.nix` and rebuild:
2026-03-28 15:57:10 +00:00
```nix
nix.settings.experimental-features = [ "nix-command" "flakes" ];
```
2026-06-11 10:00:02 +01:00
### 3. Create the hardware config in the repo
2026-03-28 15:57:10 +00:00
2026-06-11 10:00:02 +01:00
Copy `/etc/nixos/hardware-configuration.nix` to `hosts/hardware/FredOS-NEWHOST.nix` and append the hostname and bootloader config:
2026-03-28 15:57:10 +00:00
```nix
networking.hostName = "FredOS-NEWHOST";
2026-03-28 16:02:40 +00:00
boot.loader.systemd-boot.enable = true;
boot.loader.efi.canTouchEfiVariables = true;
2026-03-28 15:57:10 +00:00
```
2026-03-28 16:02:40 +00:00
### 4. Register the host in flake.nix
2026-03-28 15:57:10 +00:00
```nix
2026-06-11 10:00:02 +01:00
FredOS-NEWHOST = mkHost "FredOS-NEWHOST" [];
2026-03-28 15:57:10 +00:00
```
2026-03-28 16:02:40 +00:00
### 5. Add host-specific config
2026-03-28 15:57:10 +00:00
2026-06-11 10:00:02 +01:00
Create `hosts/FredOS-NEWHOST.nix` for machine-specific packages or services. `mkHost` imports it automatically — no changes to `common.nix` needed.
2026-03-28 15:57:10 +00:00
### 6. Switch to the flake
2026-06-11 10:00:02 +01:00
Run once with the explicit hostname:
2026-03-28 15:57:10 +00:00
```bash
2026-06-11 10:00:02 +01:00
sudo nixos-rebuild switch --refresh --flake git+https://forg.gregersen.it/rope/nixos#FredOS -NEWHOST
2026-03-28 15:57:10 +00:00
```
2026-03-28 16:02:40 +00:00
After this succeeds, the plain `update` alias works from then on.
2026-03-28 15:57:10 +00:00
---
2026-03-28 16:02:40 +00:00
## Flake inputs
| Input | Source |
|---|---|
2026-06-11 10:00:02 +01:00
| 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` |
2026-03-28 16:02:40 +00:00
| zen-browser | `github:0xc000022070/zen-browser-flake` |
2026-06-11 10:00:02 +01:00
| nix-cachyos-kernel | `github:xddxdd/nix-cachyos-kernel/release` (own nixpkgs pin — keeps their kernel binary cache usable) |
2026-05-16 12:27:19 +01:00
| proton-cachyos-nix | `github:powerofthe69/proton-cachyos-nix` |
2026-03-28 16:02:40 +00:00
2026-04-07 20:49:04 +01:00
## Mediaserver secrets
2026-06-11 10:00:02 +01:00
Several services on FredOS-Mediaserver require secrets stored on the machine (not in the repo). After a fresh deploy, create these before running `update` :
2026-04-07 20:49:04 +01:00
```bash
# 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
2026-06-11 10:00:02 +01:00
# 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
2026-04-07 20:49:04 +01:00
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
2026-06-11 10:00:02 +01:00
sudo chown root:authelia-main /var/secrets/authelia/*
sudo chmod 640 /var/secrets/authelia/*
2026-04-07 20:49:04 +01:00
2026-06-11 10:00:02 +01:00
# Authelia user database
2026-04-07 21:00:03 +01:00
# 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"
2026-04-07 20:49:04 +01:00
sudo mkdir -p /var/lib/authelia-main
2026-04-07 21:00:03 +01:00
sudo nano /var/lib/authelia-main/users_database.yml
2026-04-07 20:49:04 +01:00
sudo chown authelia-main:authelia-main /var/lib/authelia-main/users_database.yml
```
2026-04-07 22:54:33 +01:00
## Migrating to a new server
2026-06-11 10:00:02 +01:00
When moving FredOS-Mediaserver to new hardware, back up these state directories from the old server (`backup-server.sh` automates most of it):
2026-04-07 22:54:33 +01:00
```bash
# 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
2026-06-11 10:00:02 +01:00
/var/secrets/ # See "Mediaserver secrets" above
2026-04-07 22:54:33 +01:00
# Media files
/mnt/storage/ # The mergerfs pool (torrents, media libraries, audiobooks)
```
Steps:
1. Install NixOS on the new server
2026-06-11 10:00:02 +01:00
2. Update `hosts/hardware/FredOS-Mediaserver.nix` from the new `/etc/nixos/hardware-configuration.nix` (new disk UUIDs, bootloader config)
2026-04-07 22:54:33 +01:00
3. Set up the mergerfs pool and mount at `/mnt/storage`
4. Restore `/var/secrets/` (see Mediaserver secrets section above)
2026-06-11 10:00:02 +01:00
5. Run `sudo nixos-rebuild switch --refresh --flake git+https://forg.gregersen.it/rope/nixos#FredOS-Mediaserver`
2026-04-07 22:54:33 +01:00
6. Stop all services, restore the `/var/lib/` directories listed above, then start services
7. Update Cloudflare DNS if the server's public IP changed
2026-06-11 10:00:02 +01:00
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.
2026-04-07 22:54:33 +01:00
2026-03-28 15:57:10 +00:00
## Notes
2026-06-11 10:00:02 +01:00
- The mediaserver is also the home router: `services/router.nix` owns nftables NAT/firewall and `networking.firewall` is **disabled** on that host. WAN exposure is controlled solely by `ports.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 == "...")` or `lib.elem config.networking.hostName [...]` ; host modules themselves are selected by `mkHost` in `flake.nix`
- `modules/crowdsec/` vendors the crowdsec module rewrite from nixpkgs PR #446307 — delete it (and the `disabledModules` /`imports` lines in `services/crowdsec.nix` ) once that PR lands in the pinned channel