No description
Find a file
rope 8f9e491160 LocalSend: use module to open 53317
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-07 19:28:23 +01:00
.forgejo/workflows update workflow: always commit so scheduled runs have a consistent label 2026-05-19 19:54:21 +01:00
apps Patch Zen window sync blank tabs 2026-10-05 11:58:58 +01:00
docs Stylix follows nixpkgs, weekly GC, drop VMs and Flatnotes 2026-09-23 10:03:30 +01:00
home-manager USB drives: yazi g m, udiskie Open → yazi 2026-09-26 19:56:01 +01:00
hosts Eee: Extreme Tux Racer 2026-10-05 22:50:51 +01:00
modules/crowdsec crowdsec: vendor PR #446307 rewrite to fix bootstrap 2026-04-25 15:00:32 +01:00
patches Patch Zen window sync blank tabs 2026-10-05 11:58:58 +01:00
scripts Make boot greeter frameless and match Gaming display mode 2026-09-14 19:37:31 +01:00
services Add shared Stardew Valley tracker 2026-10-04 19:53:06 +01:00
settings USB drives: yazi g m, udiskie Open → yazi 2026-09-26 19:56:01 +01:00
templates zen: correct the color-scheme comment — it's light-dark(), not system colours 2026-09-10 09:52:44 +01:00
walls Upload files to "walls" 2026-09-01 15:13:37 -07:00
.gitignore Stylix follows nixpkgs, weekly GC, drop VMs and Flatnotes 2026-09-23 10:03:30 +01:00
.mcp.json Add mcp-nixos MCP server (nix run) 2026-06-25 10:40:40 +01:00
AGENTS.md Show power menu on power button press 2026-09-18 19:16:39 +01:00
CLAUDE.md Add FredOS-Rescue: Claude Code rescue USB 2026-10-05 21:22:14 +01:00
common.nix LocalSend: use module to open 53317 2026-10-07 19:28:23 +01:00
devices.toml router: un-isolate the camera 2026-09-12 17:23:33 +01:00
flake.lock Update Stardew tracker (harvest dates) 2026-10-06 10:07:49 +01:00
flake.nix Add FredOS-Rescue: Claude Code rescue USB 2026-10-05 21:22:14 +01:00
ports.toml router-ui: web management page for ports, devices, traffic, speedtest 2026-08-15 12:15:54 +01:00
readme.md Drop recursive media chown and migration backup script 2026-09-23 10:08:13 +01:00
REPO-REVIEW.md Harden deployment, authentication, firewall and media storage failure paths 2026-09-14 11:51:04 +01:00

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
│   ├── jellyfin.nix                 # Media server
│   ├── 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
├── 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.


Running the desktop on someone else's machine

nixosModules.gaming is the whole FredOS-Gaming desktop — Hyprland, quickshell, stylix theming, Steam, the lot — with hosts/hardware/FredOS-Gaming.nix left out. Import it from your own flake alongside your own /etc/nixos/hardware-configuration.nix, which stays on your machine and never touches this repo, so nix flake update keeps pulling my changes without ever overwriting your disks and GPU:

{
  inputs.fredos.url = "git+https://forg.gregersen.it/rope/nixos";

  outputs = { self, fredos, ... }: {
    nixosConfigurations.mine = fredos.inputs.nixpkgs.lib.nixosSystem {
      modules = [
        fredos.nixosModules.gaming
        ./hardware-configuration.nix
      ];
    };
  };
}

Then sudo nixos-rebuild switch --flake .#mine.

Notes:

  • The hostname stays FredOS-Gaming — every module in here is gated on it, so changing it switches the desktop off.
  • The module drops my build server and my SSH keys, and defaults system.stateVersion, nixpkgs.hostPlatform and systemd-boot so a stock hardware-configuration.nix is enough. All mkDefault, so override freely.
  • The CachyOS kernel and its binary cache live in the hardware file, so you won't get them. Copy those lines out of hosts/hardware/FredOS-Gaming.nix if you want them.
  • The user is fred with passwordless sudo. passwd fred after the first switch.
  • My monitor layout (DP-3, 3440x1440@180) is in hosts/FredOS-Gaming.nix; override wayland.windowManager.hyprland.settings.monitor for yours.

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:

# 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:

  1. Install NixOS on the new server
  2. Update hosts/hardware/FredOS-Mediaserver.nix from the new /etc/nixos/hardware-configuration.nix (new disk UUIDs, bootloader config)
  3. Set up the mergerfs pool and mount at /mnt/storage
  4. Restore /var/secrets/ (see Mediaserver secrets section above)
  5. Run sudo nixos-rebuild switch --refresh --flake git+https://forg.gregersen.it/rope/nixos#FredOS-Mediaserver
  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

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.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