add qemu/libvirt/virt-manager packages and libvirtd service for Arch VM

This commit is contained in:
Harley
2026-07-20 15:17:32 -04:00
parent 44dce2027b
commit 58a1f12018
6 changed files with 4122 additions and 1 deletions
+20 -1
View File
@@ -498,7 +498,7 @@
users.users.harley = {
isNormalUser = true;
shell = lib.mkForce pkgs.zsh;
extraGroups = [ "wheel" "networkmanager" "docker" "input" "uinput" "video"];
extraGroups = [ "wheel" "networkmanager" "docker" "libvirtd" "input" "uinput" "video"];
openssh.authorizedKeys.keys = [ "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIH4OAJ8iC7CQIcedVbU86xgoL4YkMWAG9bU6aj9v4Skb harley@utumno" ];
};
@@ -541,6 +541,14 @@
mpv geeqie ffmpeg yt-dlp
## Virtual display for Sunshine headless streaming (krfb-virtualmonitor)
kdePackages.krfb
## Virtualization (Arch VM)
qemu
libvirt
virt-manager
OVMF
spice-gtk
swtpm
];
############################################################
## ##
@@ -736,6 +744,17 @@
## uninput enable for Sunshine.
hardware.uinput.enable = true;
##
## Libvirt/KVM virtualisation (Arch VM)
virtualisation.libvirtd = {
enable = true;
qemu = {
swtpm.enable = true;
ovmf.enable = true;
ovmf.packages = [ pkgs.OVMF ];
runAsRoot = true;
};
};
##
## 8.1: Docker
##
+985
View File
@@ -0,0 +1,985 @@
{ config, lib, pkgs, ... }:
{
############################################################
## Table of Contents ##
## 1) Nix Configs/Flakes ##
## 2) Boot Option ##
## 3) Filesystems ##
## 4) System Configuration ##
## 5) Networking ##
## |_5.1) Firewall ##
## |_5.2) Wireguard ##
## |_5.3) Pihole ##
## |_5.4) Nginx ##
## 6) Users and Sudo ##
## 7) General Packages ##
## 8) Services, Daemons, Scripts ##
## |_8.1) Docker & Containers ##
## |_8.2) Arrs suite ##
## 9) Customizations & variables ##
## |_9.1) Keyd keyboard settings ##
## |_9.2) Enviroment Variables ##
## ##
## ##
############################################################
############################################################
## ##
## 1: Inital Nix configuration ##
## ##
############################################################
imports = [ ./hardware-configuration.nix ];
## Flakes and nix-command
nix.settings = {
experimental-features = [ "nix-command" "flakes" ];
trusted-users = [ "harley" ];
};
## Allow unfree packages
nixpkgs.config.allowUnfree = true;
## Overlay: stub broken python doc build (docutils bug in 26.11)
nixpkgs.overlays = [
(final: prev: {
python312 = prev.python312.overrideAttrs (old: {
passthru = old.passthru // { doc = null; };
});
})
];
## Auto-upgrade
system.autoUpgrade.enable = false;
############################################################
## ##
## 2: Boot and Kernel ##
## ##
############################################################
## btrfs support in the kernel and systemd boot stuff
boot.loader.efi.canTouchEfiVariables = true;
boot.loader.systemd-boot.enable = true;
boot.initrd.supportedFilesystems = [ "btrfs"];
## SSH and remote LUKS unlocking in initramfs
boot.initrd.network.ssh.enable = true;
boot.initrd.network.ssh.hostKeys = [ ./secrets/initrd/ssh_host_ed25519_key ];
boot.initrd.network.ssh.authorizedKeys = [
"ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIH4OAJ8iC7CQIcedVbU86xgoL4YkMWAG9bU6aj9v4Skb harley@utumno"
"ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIOnvzomqQoTWI7H2GpnqZO8tR7CwwwfUsylGu9IHWpAe u0_a392@localhost"
];
## Initrd convenience: 'unlock' command
boot.initrd.systemd.contents."/root/unlock".text = ''
#!/bin/sh
exec cryptsetup luksOpen /dev/disk/by-uuid/7d3b9cf3-0444-4cc9-80ba-e1401b783b6c nixroot
'';
boot.initrd.systemd.contents."/root/.profile".text = ''
export PATH=$PATH:/root
'';
## DHCP for initrd network (systemd stage 1)
boot.initrd.systemd.network.enable = true;
boot.initrd.systemd.network.networks."10-eno1" = {
matchConfig.Name = "eno1";
networkConfig.DHCP = "ipv4";
};
###########################################################
## ##
## 3: File systems ##
## ##
###########################################################
## Root, 512-GB, SSD-SATA, btrfs
fileSystems."/".options = [ "compress=zstd" "noatime" "space_cache=v2" ];
## 20TB Storage drive, HDD, btrfs
#fileSystems."/storage/20TB" = {
# device = "/dev/disk/by-uuid/ec172aa7-85f7-4375-a661-88fc66221620";
# fsType = "ext4";
# };
## Post-boot unlock + mount 8TB LUKS data drive
systemd.services.unlock-8tb = {
description = "Unlock and mount 8TB storage";
after = [ "local-fs.target" ];
wants = [ "local-fs.target" ];
wantedBy = [ "multi-user.target" ];
serviceConfig.Type = "oneshot";
serviceConfig.RemainAfterExit = true;
script = ''
/run/current-system/sw/bin/cryptsetup luksOpen \
--key-file /root/keyfiles/8tb.key \
/dev/disk/by-uuid/2bc4df99-677b-46d4-a1b8-38dd3c636a2b 8tb
/run/current-system/sw/bin/mount /dev/mapper/8tb /storage/8TB
/run/current-system/sw/bin/mount /storage/8TB/arrs-tmp/sabnzbd
'';
};
## 1TB storage drive, SSD-NVME, btrfs
#fileSystems."/storage/ssd/" = {
#device = " ";
#fstype = "btrfs";
#};
## automated Snapper snapshots for the root filesystem
services.snapper = {
configs."root" = {
SUBVOLUME = "/";
ALLOW_USERS = [ "harley" ];
TIMELINE_CREATE = true;
TIMELINE_CLEANUP = true;
TIMELINE_MIN_AGE = "1800";
TIMELINE_LIMIT_HOURLY = "6";
TIMELINE_LIMIT_DAILY = "7";
TIMELINE_LIMIT_WEEKLY = "4";
TIMELINE_LIMIT_MONTHLY = "6";
TIMELINE_LIMIT_YEARLY = "0";
NUMBER_CLEANUP = true;
NUMBER_LIMIT = "50";
};
};
## snapper for recov
services.snapper.configs."file7" = {
SUBVOLUME = "/mnt/.recov";
ALLOW_USERS = [ "harley" ];
TIMELINE_CREATE = false;
NUMBER_CLEANUP = false;
};
############################################################
## ##
## 4: System Configuration ##
## ##
############################################################
## Time/Locale
time.timeZone = "America/New_York";
i18n.defaultLocale = "en_US.UTF-8";
## Nvidia GPU
services.xserver.videoDrivers = [ "nvidia" ];
hardware.graphics.enable = true;
hardware.nvidia = {
modesetting.enable = true;
powerManagement.enable = true;
open = false;
nvidiaSettings = true;
};
## Greeter user for greetd login on tty6 LOOK INTO THIS, IDK WHAT IT IS
users.users.greeter = {
isSystemUser = true;
group = "nogroup";
extraGroups = [ "video" "input" "seat" ];
description = "greetd login greeter";
};
## greetd PAM service
security.pam.services.greetd = {};
## Display Manager (SDDM) and Plasma 6
services.xserver.enable = true;
services.xserver.displayManager.sddm.enable = true;
services.desktopManager.plasma6.enable = true;
programs.kdeconnect.enable = true;
## Pipewire
services.pipewire = {
enable = true;
pulse.enable = true;
alsa.enable = true;
alsa.support32Bit = true;
};
# Xpadneo for xbox controller bluetooth
hardware.xpadneo.enable = true;
############################################################
## ##
## 5) Networking ##
## ##
############################################################
networking.hostName = "utumno";
networking.networkmanager.enable = true;
# Don't block boot waiting for all network profiles to come online
systemd.services.NetworkManager-wait-online.enable = lib.mkForce false;
hardware.bluetooth = {
enable = true;
powerOnBoot = true;
settings.General.Experimental = true;
};
##
##
## 5.1 Firewall
##
##
networking.firewall = {
## UDP ports and ranges
allowedUDPPorts = [ 51820 51812 5113 ];
allowedUDPPortRanges = [
{ from = 47884; to = 48010; }
];
## TCP ports and ranges
allowedTCPPorts = [ 22 3010 222 8084 443 80 8787 8180 7777 3180 47989 47990 47991 47992 48010 5113 8777 ];
# Enable IP forwarding (needed for Wireguard routing)
enable = true;
};
## Enable Ip forwarding at the kernel level
boot.kernel.sysctl."net.ipv4.ip_forward" = 1;
## SSHD
services.openssh = {
enable = true;
settings = {
PasswordAuthentication = false;
PermitRootLogin = "prohibit-password";
};
};
##
##
## 5.2: Wireguard
##
##
networking.wireguard.interfaces = {
wg0 = {
ips = [ "10.10.10.1/24" ];
listenPort = 51820;
privateKeyFile = "/etc/nixos/secrets/wireguard/wg0-key.key";
# IP forwarding + NAT for wg0 clients to internet/LAN
postSetup = ''
${pkgs.iptables}/bin/iptables -A FORWARD -i wg0 -j ACCEPT
${pkgs.iptables}/bin/iptables -t nat -A POSTROUTING -o eno1 -j MASQUERADE
'';
postShutdown = ''
${pkgs.iptables}/bin/iptables -D FORWARD -i wg0 -j ACCEPT
${pkgs.iptables}/bin/iptables -t nat -D POSTROUTING -o eno1 -j MASQUERADE
'';
peers = [
# Exos — routes remote subnets
{
publicKey = "8rO4j18cPAw6zv8b26PNXFHpEwxqjBq1r8yRmWNOMXY=";
endpoint = "briggs.wiki:51820";
allowedIPs = [
"10.10.10.2/32"
"10.0.0.0/24"
"192.168.3.0/24"
];
}
# Maroon / Roman Phone
{
publicKey = "Sih2qcJtmhdlSwxlaRont7ezwazo0FMyMxTjhWBtpjA=";
allowedIPs = [ "192.168.3.1/32" ];
}
# Palantir (Phone)
{
publicKey = "RUPXirLLTY1KeFIjfwKQtVg3ckj8sLz2QN/AiLZOmFw=";
allowedIPs = [ "192.168.2.1/32" ];
}
# Menegroth
{
publicKey = "DWhEqMQ7/RHkEuPtMrpuhQqnls7o/B6idSJxXJJy6ys=";
allowedIPs = [ "192.168.2.2/32" ];
}
# iPad
{
publicKey = "lqTK2qoaNJ+Md8RZT3TVxN+fAHllTwIG2znZKI3Rt2c=";
allowedIPs = [ "192.168.2.3/32" ];
}
];
};
};
##
##
## 5.3) Pi-hole DNS
##
##
services.pihole-ftl = {
enable = true;
openFirewallDNS = true;
## Upstream DNS servers (Cloudflare and Quad9)
settings = {
dns = {
upstreams = [
"1.1.1.1#53"
"1.0.0.1#53"
"9.9.9.9#53"
];
## Listen on all interfaces
listeningMode = "ALL";
## Block ESNI, ICloud Private Relay, Firefox canary, etc.
blocking.active = true;
blocking.mode = "NULL";
## Rate limiting
rateLimit.count = 1000;
rateLimit.interval = 60;
## Cache
cache.size = 10000;
cache.optimizer = 3600;
## DNSSEC
dnssec = false;
bogusPriv = true;
domainNeeded = false;
## Local DNS records
hosts = [
"10.0.1.49 jellyfin.wingard.pro"
"10.0.1.49 speedtest.wingard.pro"
];
};
## Web server on 8999, admin page at /admin/
webserver = {
port = lib.mkForce "8999";
interface.boxed = "true";
interface.theme = "default-dark";
};
## Privacy level 0 = full stats
misc.privacylevel = 0;
};
## Blocklist: Steven Black's unified hosts
lists = [
{
url = "https://raw.githubusercontent.com/StevenBlack/hosts/master/hosts";
description = "Steven Black's unified adlist";
}
];
};
## Pi-hole web admin dashboard
services.pihole-web.enable = true;
##
##
## 5.4: Nginx
##
##
services.nginx = {
enable = true;
recommendedProxySettings = true;
virtualHosts = {
"jellyfin.wingard.pro" = {
enableACME = false;
addSSL = true;
sslCertificate = "/etc/letsencrypt/live/jellyfin.wingard.pro/fullchain.pem";
sslCertificateKey = "/etc/letsencrypt/live/jellyfin.wingard.pro/privkey.pem";
locations."/" = {
proxyPass = "http://127.0.0.1:8096";
proxyWebsockets = true;
};
};
"speedtest.wingard.pro" = {
enableACME = false;
addSSL = true;
sslCertificate = "/etc/letsencrypt/live/speedtest.wingard.pro/fullchain.pem";
sslCertificateKey = "/etc/letsencrypt/live/speedtest.wingard.pro/privkey.pem";
root = "/opt/openspeedtest";
locations."/" = {
index = "index.html";
tryFiles = "$uri $uri/ /index.html";
};
locations."~ \\.php$" = {
extraConfig = ''
fastcgi_pass unix:/run/phpfpm-openspeedtest.sock;
fastcgi_index index.php;
include ${pkgs.nginx}/conf/fastcgi.conf;
fastcgi_param SCRIPT_FILENAME /opt/openspeedtest$fastcgi_script_name;
'';
};
};
"speedtest-http" = {
listen = [ { addr = "0.0.0.0"; port = 8084; } ];
root = "/opt/openspeedtest";
locations."/" = {
index = "index.html";
tryFiles = "$uri $uri/ /index.html";
};
locations."~ \\.php$" = {
extraConfig = ''
fastcgi_pass unix:/run/phpfpm-openspeedtest.sock;
fastcgi_index index.php;
include ${pkgs.nginx}/conf/fastcgi.conf;
fastcgi_param SCRIPT_FILENAME /opt/openspeedtest$fastcgi_script_name;
'';
};
};
};
};
############################################################
## ##
## 6: Users ##
## ##
############################################################
## setting up sudo wheel group
security.sudo.extraRules = [
{ groups = [ "wheel" ]; commands = [ { command = "ALL"; options = [ "NOPASSWD" ]; } ]; }
];
## Harley
users.users.harley = {
isNormalUser = true;
shell = lib.mkForce pkgs.zsh;
extraGroups = [ "wheel" "networkmanager" "docker" "input" "uinput" "video"];
openssh.authorizedKeys.keys = [ "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIH4OAJ8iC7CQIcedVbU86xgoL4YkMWAG9bU6aj9v4Skb harley@utumno" ];
};
## Roman
users.users.roman = {
isNormalUser = true;
description = "Roman";
home = "/home/roman/";
createHome = true;
shell = pkgs.bash;
extraGroups = [ "wheel" "users" ];
openssh.authorizedKeys.keys = [ " " ];
};
## The Ephemeral user and filesystem
## The actual Ephemeral user
users.users.atomic = {
isNormalUser = true;
description = "Ephemeral User - home directory inside a tmpfs";
home = "/home/atomic";
group = "users";
createHome = false;
extraGroups = [ "wheel" ];
shell = lib.mkForce pkgs.zsh;
openssh.authorizedKeys.keys = [ "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIH4OAJ8iC7CQIcedVbU86xgoL4YkMWAG9bU6aj9v4Skb harley@utumno" ];
};
## filesystem for the Ephemeral user
fileSystems."/home/atomic" = {
device = "tmpfs";
fsType = "tmpfs";
options = [ "mode=0700" "uid=1001" "gid=100" "noatime" ];
};
## Populate the Ephemeral user from seed on boot
systemd.services.populate-atomic-home = {
description = " Populate atomic user's tmpfs home from seed";
after = [ "home-atomic.mount" ];
requires = [ "home-atomic.mount" ];
wantedBy = [ "home-atomic.mount" "systemd-logind.service" ];
script = ''
cp -rT /home/.atomic-persist /home/atomic
chown -R 1001:100 /home/atomic
'';
serviceConfig.Type = "oneshot";
serviceConfig.RemainAfterExit = true;
};
## Periodic Ephemeral home cleaner (reseed from skeleton)
systemd.services.atomic-home-cleaner = {
description = "Wipe atomic home if user has no active logind sessions";
script = ''
if ! pgrep -u atomic >/dev/null 2>&1; then
umount /home/atomic/mnt 2>/dev/null || true
rm -rf /home/atomic/.??* /home/atomic/** 2>/dev/null
cp -rT /home/.atomic-persist/ /home/atomic/
chown -R 1001:100 /home/atomic/
fi
'';
serviceConfig.Type = "oneshot";
};
systemd.timers.atomic-home-cleaner = {
description = "Check every 2 min if atomic user's sessions ended";
wantedBy = [ "timers.target" ];
timerConfig = {
OnBootSec = "1min";
OnUnitActiveSec = "30s";
};
};
## One-shot service to unmount and re-populate atomic's tmpfs home
systemd.services.atomic-fresh = {
description = "Wipe and re-populate atomic's tmpfs home";
after = [ "home-atomic.mount" ];
script = ''
loginctl terminate-user atomic 2>/dev/null || true
sleep 1
rm -rf /home/atomic/.??* /home/atomic/** 2>/dev/null || true
umount /home/atomic 2>/dev/null || true
mount /home/atomic
cp -rT /home/.atomic-persist/ /home/atomic/
chown -R 1001:100 /home/atomic/
'';
serviceConfig.Type = "oneshot";
path = [ pkgs.util-linux ];
serviceConfig.User = "root";
};
############################################################
## ##
## 7: General Packages ##
## ##
############################################################
environment.systemPackages = with pkgs; [
## Cli tools
vim wget curl progress git btop neovim tmux lsof bat ncdu nvtopPackages.full fastfetch
unzip unrar p7zip xclip
testdisk btdu
## Games
lutris rpcs3 mednafen mednafen-server mednaffe dolphin-emu
## System
adwaita-icon-theme tela-circle-icon-theme
sshfs sshpass pciutils usbutils python312 (pkgs.kdePackages.bluedevil) gtkgreet
cage guake android-tools patool pv ghostscript imagemagick nodejs go deno socat
fzf smartmontools shadow ripgrep fd jq tree qdirstat uv
firefox
obsidian
## Multimedia
mpv geeqie ffmpeg yt-dlp
## Virtual display for Sunshine headless streaming (krfb-virtualmonitor)
kdePackages.krfb
];
############################################################
## ##
## 8) Services ##
## ##
############################################################
## Hermes WebUI (port 8787)
#systemd.services.hermes-webui = {
# description = "Hermes Web UI";
# after = [ "network-online.target" ];
# wants = [ "network-online.target" ];
# wantedBy = [ "multi-user.target" ];
# environment = {
# HERMES_WEBUI_HOST = "0.0.0.0";
# HERMES_WEBUI_PORT = "8787";
# HERMES_HOME = "/storage/8TB/Backups/Agents/.hermes";
# HERMES_WEBUI_STATE_DIR = "/storage/8TB/Backups/Agents/.hermes/webui";
# SEARXNG_URL = "http://10.0.1.49:8888";
# };
# serviceConfig = {
# Type = "simple";
# User = "harley";
# WorkingDirectory = "/opt/hermes-webui";
# ExecStart = "/opt/hermes-webui/venv/bin/python server.py";
# Restart = "on-failure";
# RestartSec = 5;
# };
# };
## SearXNG meta search engine (Port 8888)
services.searx = {
enable = true;
openFirewall = true;
redisCreateLocally = true;
settings = {
general.instance_name = "Utumno Search";
search = {
safe_search = 0;
formats = [ "html" "json" ];
autocomplete = "";
};
server = {
port = 8888;
bind_address = "0.0.0.0";
secret_key = "@SEARXNG_SECRET@";
limiter = false;
method = "POST";
image_proxy = false;
};
ui = {
default_theme = "simple";
theme_args.simple_style = "dark";
query_in_title = false;
};
outgoing = {
request_timeout = 5.0;
max_request_timeout = 15.0;
pool_connections = 100;
pool_maxsize = 20;
enable_http2 = true;
};
};
environmentFile = "/opt/searxng/secrets.env";
};
## Gitea (Port 3010)
services.gitea = {
enable = true;
stateDir = "/storage/8TB/Backups/Gitea";
repositoryRoot = "/storage/8TB/Backups/Gitea/repositories";
database.type = "sqlite3";
settings = {
server = {
DOMAIN = "10.0.1.49";
ROOT_URL = "http://10.0.1.49:3010";
HTTP_PORT = 3010;
SSH_PORT = 222;
DISABLE_SSH = false;
LFS_START_SERVER = false;
};
service = {
DISABLE_REGISTRATION = false;
REQUIRE_SIGNIN_VIEW = false;
};
security.INSTALL_LOCK = true;
};
};
## Kiwix ZIM server (port 8098)
services.kiwix-serve = {
enable = true;
port = 8098;
openFirewall = true;
library = {
# Update paths when ZIM files are added/removed
wikipedia = "/storage/20TB/Media/Kiwix/wikipedia_en_all_maxi_2026-02.zim";
archlinux = "/storage/20TB/Media/Kiwix/archlinux_en_all_maxi_2025-08.zim";
ifixit = "/storage/20TB/Media/Kiwix/ifixit_en_all_2025-06.zim";
khanacademy = "/storage/20TB/Media/Kiwix/khanacademy_en_all_2023-03.zim";
survivorlibrary = "/storage/20TB/Media/Kiwix/survivorlibrary.com_en_all_2025-06.zim";
urbanprepper = "/storage/20TB/Media/Kiwix/urban-prepper_en_all_2025-06.zim";
anonymousplanet = "/storage/20TB/Media/Kiwix/anonymousplanet.org_en_all_2025-08.zim";
basedcooking = "/storage/20TB/Media/Kiwix/based.cooking_en_all_2025-08.zim";
};
extraArgs = [ "--skipInvalid" ];
};
## OpenSpeedTest (Port 8084)
services.phpfpm.pools.openspeedtest = {
user = "nginx";
group = "nginx";
settings = {
pm = "dynamic";
"pm.max_children" = 5;
"pm.start_servers" = 2;
"pm.min_spare_servers" = 1;
"pm.max_spare_servers" = 3;
"listen" = "/run/phpfpm-openspeedtest.sock";
};
};
## Immich (Port 2283)
#services.immich = {
#enable = true;
#host = "10.10.10.1";
#mediaLocation = "/storage/8TB/Documents/immich";
#port = 2283;
#openFirewall = true;
#database.enable = true;
#redis.enable = true;
#};
## Roku Remote (Port 7777)
## btop-web system monitor (Port 8777)
systemd.services.btop-web = {
description = "btop-style web system monitor";
after = [ "network-online.target" ];
wants = [ "network-online.target" ];
wantedBy = [ "multi-user.target" ];
serviceConfig = {
Type = "simple";
User = "harley";
WorkingDirectory = "/opt/btop-web";
ExecStart = "${pkgs.python3}/bin/python server.py";
Restart = "always";
RestartSec = 5;
};
};
systemd.services.roku-remote = {
description = "Roku TV Web Remote";
after = [ "network-online.target" ];
wants = [ "network-online.target" ];
wantedBy = [ "multi-user.target" ];
serviceConfig = {
Type = "simple";
User = "harley";
WorkingDirectory = "/opt/roku-remote";
ExecStart = "${pkgs.python3}/bin/python server.py";
Restart = "on-failure";
RestartSec = 5;
};
};
##Sunshine
services.sunshine = {
enable = true;
autoStart = true;
capSysAdmin = true; # only needed for Wayland -- omit this when using with Xorg
openFirewall = true;
};
## uninput enable for Sunshine.
hardware.uinput.enable = true;
##
##
## 8.1: Docker
##
##
virtualisation.docker = {
enable = true;
# Allow non-root users (harley is in docker group)
enableOnBoot = true;
# Use the overlay2 storage driver (default on modern kernels)
storageDriver = "overlay2";
# Auto-prune unused data
autoPrune.enable = true;
autoPrune.dates = "weekly";
};
## Firecrawl web-scraper (Port 3002)
systemd.services.firecrawl = {
description = "Firecrawl web scraping stack";
after = [ "docker.service" "network-online.target" ];
wants = [ "docker.service" "network-online.target" ];
wantedBy = [ "multi-user.target" ];
path = [ pkgs.docker ];
serviceConfig = {
Type = "oneshot";
RemainAfterExit = true;
WorkingDirectory = "/opt/firecrawl";
ExecStart = "${pkgs.docker}/bin/docker compose up -d";
ExecStop = "${pkgs.docker}/bin/docker compose down";
};
};
########## ##########
########## ##########
########## 8.2: ARRS ##########
########## ##########
########## ##########
## PIA VPN + qBittorrent (Port 8088) + SOCKS5 (Port 1080) (Docker compose)
systemd.services.pia-qbit = {
description = "PIA VPN + qBittorrent + SOCKS5 proxy stack";
after = [ "docker.service" "network-online.target" ];
wants = [ "docker.service" "network-online.target" ];
wantedBy = [ "multi-user.target" ];
path = [ pkgs.docker ];
serviceConfig = {
Type = "oneshot";
RemainAfterExit = true;
WorkingDirectory = "/opt/pia-qbit";
ExecStart = "${pkgs.docker}/bin/docker compose up -d";
ExecStop = "${pkgs.docker}/bin/docker compose down";
# Give VPN time to establish before declaring "done"
ExecStartPost = "${pkgs.coreutils}/bin/sleep 10";
};
};
## Bitmagnet (Port 3333)
#services.bitmagnet = {
# enable = true;
#openFirewall = true;
#settings = {
#http_server.local_address = ":3333";
#dht_server.port = 3334;
#postgres = {
#name = "bitmagnet";
#user = "bitmagnet";
#password = "";
#};
#};
#};
#services.postgresql = {
#dataDir = "/storage/20TB/Computer-Related/Bitmagnet-data";
#package = pkgs.postgresql_16;
#};
## Jellyfin (Port 8096)
services.jellyfin = {
enable = true;
dataDir = "/storage/8TB/arrs-tmp/jellyfin";
cacheDir = "/storage/8TB/arrs-tmp/jellyfin-cache";
openFirewall = true;
};
## Prowlarr (port 9696)
services.prowlarr = {
enable = true;
dataDir = "/storage/8TB/arrs-tmp/prowlarr";
openFirewall = true;
};
## Sonarr (port 8989)
services.sonarr = {
enable = true;
dataDir = "/storage/8TB/arrs-tmp/sonarr";
openFirewall = true;
};
## Radarr (port 7878)
services.radarr = {
enable = true;
dataDir = "/storage/8TB/arrs-tmp/radarr";
openFirewall = true;
};
## SABnzbd (port 8180)
fileSystems."/var/lib/sabnzbd" = {
device = "/storage/8TB/arrs-tmp/sabnzbd";
options = [ "bind" "noauto" ];
fsType = "none";
};
services.sabnzbd = {
enable = true;
openFirewall = true;
settings = {
host = "0.0.0.0";
port = 8180;
misc = {
host = "0.0.0.0";
port = 8180;
enable_https = false;
html_login = true;
inet_exposure = 0;
auto_browser = false;
config_lock = false;
check_new_rel = false;
cache_limit = "";
};
};
};
############################################################
## ##
## 9: Customization ##
## ##
############################################################
##
##
## 9.1: Keyd keyboard remappings
##
##
services.keyd = {
enable = true;
keyboards = {
default = {
ids = [ "*" ];
settings = {
main = {
esc = "capslock";
capslock = "overload(nav, esc)";
};
nav = {
w = "up";
a = "left";
s = "down";
d = "right";
b = "C-left";
u = "pageup";
x = "delete";
g = "home";
n = "end";
h = "left";
j = "down";
k = "up";
l = "right";
space = "capslock";
};
};
};
q = {
ids = [ "*" ];
settings = {
main = {
esc = "capslock";
capslock = "overload(nav, esc)";
};
nav = {
w = "up";
a = "left";
s = "down";
d = "right";
h = "left";
j = "down";
k = "up";
l = "right";
space = "capslock";
};
};
};
};
};
##
##
## 9.2 Enviroment Variables
##
##
environment.variables = {
GTK_THEME = "Adwaita:dark";
GTK_ICON_THEME = "Tela-circle-dark";
HERMES_HOME = "/storage/8TB/Backups/Agents/.hermes";
};
## ZSH with Oh-My-Zsh
programs.zsh.enable = true;
programs.zsh.ohMyZsh = {
enable = true;
theme = "jonathan";
plugins = [
"git"
"sudo"
"web-search"
"history"
"extract"
"colored-man-pages"
];
customPkgs = [
(pkgs.runCommand "jonathan-zsh-theme" {
themeFile = builtins.readFile ./jonathan.zsh-theme;
passAsFile = [ "themeFile" ];
} ''
mkdir -p $out/share/zsh/themes
cp "$themeFilePath" $out/share/zsh/themes/jonathan.zsh-theme
'')
];
};
programs.zsh.syntaxHighlighting.enable = true;
programs.zsh.interactiveShellInit = ''
source ${pkgs.zsh-autosuggestions}/share/zsh-autosuggestions/zsh-autosuggestions.zsh
'';
programs.zsh.promptInit = ''
bindkey '^F' autosuggest-accept
bindkey '^[[C' autosuggest-accept
'';
programs.zsh.shellAliases = {
cl = "clear";
tt = "tmux";
tta = "tmux attach";
ttls = "tmux ls";
xclipp = "xclip -selection clipboard";
palantir = "ssh palantir";
rcp = "rsync -avP --partial --append";
};
# Make theme packages available to all users
environment.sessionVariables = {
XDG_DATA_DIRS = [
"${pkgs.tela-circle-icon-theme}/share/icons"
"${pkgs.catppuccin}/share/plasma"
];
};
############################################################
## ##
## ##
## ##
############################################################
## ── State version ────────────────────────────────────────
system.stateVersion = "26.11";
}
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+977
View File
@@ -0,0 +1,977 @@
{ config, lib, pkgs, ... }:
{
############################################################
## Table of Contents ##
## 1) Nix Configs/Flakes ##
## 2) Boot Option ##
## 3) Filesystems ##
## 4) System Configuration ##
## 5) Networking ##
## |_5.1) Firewall ##
## |_5.2) Wireguard ##
## |_5.3) Pihole ##
## |_5.4) Nginx ##
## 6) Users and Sudo ##
## 7) General Packages ##
## 8) Services, Daemons, Scripts ##
## |_8.1) Docker & Containers ##
## |_8.2) Arrs suite ##
## 9) Customizations & variables ##
## |_9.1) Keyd keyboard settings ##
## |_9.2) Enviroment Variables ##
## ##
## ##
############################################################
############################################################
## ##
## 1: Inital Nix configuration ##
## ##
############################################################
imports = [ ./hardware-configuration.nix ];
## Flakes and nix-command
nix.settings = {
experimental-features = [ "nix-command" "flakes" ];
trusted-users = [ "harley" ];
};
## Allow unfree packages
nixpkgs.config.allowUnfree = true;
## Overlay: stub broken python doc build (docutils bug in 26.11)
nixpkgs.overlays = [
(final: prev: {
python312 = prev.python312.overrideAttrs (old: {
passthru = old.passthru // { doc = null; };
});
})
];
## Auto-upgrade
system.autoUpgrade.enable = false;
############################################################
## ##
## 2: Boot and Kernel ##
## ##
############################################################
## btrfs support in the kernel and systemd boot stuff
boot.loader.efi.canTouchEfiVariables = true;
boot.loader.systemd-boot.enable = true;
boot.initrd.supportedFilesystems = [ "btrfs"];
## SSH and remote LUKS unlocking in initramfs
boot.initrd.network.ssh.enable = true;
boot.initrd.network.ssh.hostKeys = [ ./secrets/initrd/ssh_host_ed25519_key ];
boot.initrd.network.ssh.authorizedKeys = [
"ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIH4OAJ8iC7CQIcedVbU86xgoL4YkMWAG9bU6aj9v4Skb harley@utumno"
"ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIOnvzomqQoTWI7H2GpnqZO8tR7CwwwfUsylGu9IHWpAe u0_a392@localhost"
];
## Initrd convenience: 'unlock' command
boot.initrd.systemd.contents."/root/unlock".text = ''
#!/bin/sh
exec cryptsetup luksOpen /dev/disk/by-uuid/7d3b9cf3-0444-4cc9-80ba-e1401b783b6c nixroot
'';
boot.initrd.systemd.contents."/root/.profile".text = ''
export PATH=$PATH:/root
'';
## DHCP for initrd network (systemd stage 1)
boot.initrd.systemd.network.enable = true;
boot.initrd.systemd.network.networks."10-eno1" = {
matchConfig.Name = "eno1";
networkConfig.DHCP = "ipv4";
};
###########################################################
## ##
## 3: File systems ##
## ##
###########################################################
## Root, 512-GB, SSD-SATA, btrfs
fileSystems."/".options = [ "compress=zstd" "noatime" "space_cache=v2" ];
## 20TB Storage drive, HDD, btrfs
#fileSystems."/storage/20TB" = {
# device = "/dev/disk/by-uuid/ec172aa7-85f7-4375-a661-88fc66221620";
# fsType = "ext4";
# };
## Post-boot unlock + mount 8TB LUKS data drive
systemd.services.unlock-8tb = {
description = "Unlock and mount 8TB storage";
after = [ "local-fs.target" ];
wants = [ "local-fs.target" ];
wantedBy = [ "multi-user.target" ];
serviceConfig.Type = "oneshot";
serviceConfig.RemainAfterExit = true;
script = ''
/run/current-system/sw/bin/cryptsetup luksOpen \
--key-file /root/keyfiles/8tb.key \
/dev/disk/by-uuid/2bc4df99-677b-46d4-a1b8-38dd3c636a2b 8tb
/run/current-system/sw/bin/mount /dev/mapper/8tb /storage/8TB
/run/current-system/sw/bin/mount /storage/8TB/arrs-tmp/sabnzbd
'';
};
## 1TB storage drive, SSD-NVME, btrfs
#fileSystems."/storage/ssd/" = {
#device = " ";
#fstype = "btrfs";
#};
## automated Snapper snapshots for the root filesystem
services.snapper = {
configs."root" = {
SUBVOLUME = "/";
ALLOW_USERS = [ "harley" ];
TIMELINE_CREATE = true;
TIMELINE_CLEANUP = true;
TIMELINE_MIN_AGE = "1800";
TIMELINE_LIMIT_HOURLY = "6";
TIMELINE_LIMIT_DAILY = "7";
TIMELINE_LIMIT_WEEKLY = "4";
TIMELINE_LIMIT_MONTHLY = "6";
TIMELINE_LIMIT_YEARLY = "0";
NUMBER_CLEANUP = true;
NUMBER_LIMIT = "50";
};
};
## snapper for recov
services.snapper.configs."file7" = {
SUBVOLUME = "/mnt/.recov";
ALLOW_USERS = [ "harley" ];
TIMELINE_CREATE = false;
NUMBER_CLEANUP = false;
};
############################################################
## ##
## 4: System Configuration ##
## ##
############################################################
## Time/Locale
time.timeZone = "America/New_York";
i18n.defaultLocale = "en_US.UTF-8";
## Nvidia GPU
services.xserver.videoDrivers = [ "nvidia" ];
hardware.graphics.enable = true;
hardware.nvidia = {
modesetting.enable = true;
powerManagement.enable = true;
open = false;
nvidiaSettings = true;
};
## Greeter user for greetd login on tty6 LOOK INTO THIS, IDK WHAT IT IS
users.users.greeter = {
isSystemUser = true;
group = "nogroup";
extraGroups = [ "video" "input" "seat" ];
description = "greetd login greeter";
};
## greetd PAM service
security.pam.services.greetd = {};
## Display Manager (SDDM) and Plasma 6
services.xserver.enable = true;
services.xserver.displayManager.sddm.enable = true;
services.desktopManager.plasma6.enable = true;
programs.kdeconnect.enable = true;
## Pipewire
services.pipewire = {
enable = true;
pulse.enable = true;
alsa.enable = true;
alsa.support32Bit = true;
};
############################################################
## ##
## 5) Networking ##
## ##
############################################################
networking.hostName = "utumno";
networking.networkmanager.enable = true;
# Don't block boot waiting for all network profiles to come online
systemd.services.NetworkManager-wait-online.enable = lib.mkForce false;
hardware.bluetooth = {
enable = true;
powerOnBoot = true;
settings.General.Experimental = true;
};
##
##
## 5.1 Firewall
##
##
networking.firewall = {
## UDP ports and ranges
allowedUDPPorts = [ 51820 51812 5113 ];
allowedUDPPortRanges = [
{ from = 47884; to = 48010; }
];
## TCP ports and ranges
allowedTCPPorts = [ 22 3010 222 8084 443 80 8787 8180 7777 3180 47989 47990 47991 47992 48010 5113 8777 ];
# Enable IP forwarding (needed for Wireguard routing)
enable = true;
};
## Enable Ip forwarding at the kernel level
boot.kernel.sysctl."net.ipv4.ip_forward" = 1;
## SSHD
services.openssh = {
enable = true;
settings = {
PasswordAuthentication = false;
PermitRootLogin = "prohibit-password";
};
};
##
##
## 5.2: Wireguard
##
##
networking.wireguard.interfaces = {
wg0 = {
ips = [ "10.10.10.1/24" ];
listenPort = 51820;
privateKeyFile = "/etc/nixos/secrets/wireguard/wg0-key.key";
# IP forwarding + NAT for wg0 clients to internet/LAN
postSetup = ''
${pkgs.iptables}/bin/iptables -A FORWARD -i wg0 -j ACCEPT
${pkgs.iptables}/bin/iptables -t nat -A POSTROUTING -o eno1 -j MASQUERADE
'';
postShutdown = ''
${pkgs.iptables}/bin/iptables -D FORWARD -i wg0 -j ACCEPT
${pkgs.iptables}/bin/iptables -t nat -D POSTROUTING -o eno1 -j MASQUERADE
'';
peers = [
# Exos — routes remote subnets
{
publicKey = "8rO4j18cPAw6zv8b26PNXFHpEwxqjBq1r8yRmWNOMXY=";
endpoint = "briggs.wiki:51820";
allowedIPs = [
"10.10.10.2/32"
"10.0.0.0/24"
"192.168.3.0/24"
];
}
# Maroon / Roman Phone
{
publicKey = "Sih2qcJtmhdlSwxlaRont7ezwazo0FMyMxTjhWBtpjA=";
allowedIPs = [ "192.168.3.1/32" ];
}
# Palantir (Phone)
{
publicKey = "RUPXirLLTY1KeFIjfwKQtVg3ckj8sLz2QN/AiLZOmFw=";
allowedIPs = [ "192.168.2.1/32" ];
}
# Menegroth
{
publicKey = "DWhEqMQ7/RHkEuPtMrpuhQqnls7o/B6idSJxXJJy6ys=";
allowedIPs = [ "192.168.2.2/32" ];
}
# iPad
{
publicKey = "lqTK2qoaNJ+Md8RZT3TVxN+fAHllTwIG2znZKI3Rt2c=";
allowedIPs = [ "192.168.2.3/32" ];
}
];
};
};
##
##
## 5.3) Pi-hole DNS
##
##
services.pihole-ftl = {
enable = true;
openFirewallDNS = true;
## Upstream DNS servers (Cloudflare and Quad9)
settings = {
dns = {
upstreams = [
"1.1.1.1#53"
"1.0.0.1#53"
"9.9.9.9#53"
];
## Listen on all interfaces
listeningMode = "ALL";
## Block ESNI, ICloud Private Relay, Firefox canary, etc.
blocking.active = true;
blocking.mode = "NULL";
## Rate limiting
rateLimit.count = 1000;
rateLimit.interval = 60;
## Cache
cache.size = 10000;
cache.optimizer = 3600;
## DNSSEC
dnssec = false;
bogusPriv = true;
domainNeeded = false;
## Local DNS records
hosts = [
"10.0.1.49 jellyfin.wingard.pro"
"10.0.1.49 speedtest.wingard.pro"
];
};
## Web server on 8999, admin page at /admin/
webserver = {
port = lib.mkForce "8999";
interface.boxed = "true";
interface.theme = "default-dark";
};
## Privacy level 0 = full stats
misc.privacylevel = 0;
};
## Blocklist: Steven Black's unified hosts
lists = [
{
url = "https://raw.githubusercontent.com/StevenBlack/hosts/master/hosts";
description = "Steven Black's unified adlist";
}
];
};
## Pi-hole web admin dashboard
services.pihole-web.enable = true;
##
##
## 5.4: Nginx
##
##
services.nginx = {
enable = true;
recommendedProxySettings = true;
virtualHosts = {
"jellyfin.wingard.pro" = {
enableACME = false;
addSSL = true;
sslCertificate = "/etc/letsencrypt/live/jellyfin.wingard.pro/fullchain.pem";
sslCertificateKey = "/etc/letsencrypt/live/jellyfin.wingard.pro/privkey.pem";
locations."/" = {
proxyPass = "http://127.0.0.1:8096";
proxyWebsockets = true;
};
};
"speedtest.wingard.pro" = {
enableACME = false;
addSSL = true;
sslCertificate = "/etc/letsencrypt/live/speedtest.wingard.pro/fullchain.pem";
sslCertificateKey = "/etc/letsencrypt/live/speedtest.wingard.pro/privkey.pem";
root = "/opt/openspeedtest";
locations."/" = {
index = "index.html";
tryFiles = "$uri $uri/ /index.html";
};
locations."~ \\.php$" = {
extraConfig = ''
fastcgi_pass unix:/run/phpfpm-openspeedtest.sock;
fastcgi_index index.php;
include ${pkgs.nginx}/conf/fastcgi.conf;
fastcgi_param SCRIPT_FILENAME /opt/openspeedtest$fastcgi_script_name;
'';
};
};
"speedtest-http" = {
listen = [ { addr = "0.0.0.0"; port = 8084; } ];
root = "/opt/openspeedtest";
locations."/" = {
index = "index.html";
tryFiles = "$uri $uri/ /index.html";
};
locations."~ \\.php$" = {
extraConfig = ''
fastcgi_pass unix:/run/phpfpm-openspeedtest.sock;
fastcgi_index index.php;
include ${pkgs.nginx}/conf/fastcgi.conf;
fastcgi_param SCRIPT_FILENAME /opt/openspeedtest$fastcgi_script_name;
'';
};
};
};
};
############################################################
## ##
## 6: Users ##
## ##
############################################################
## setting up sudo wheel group
security.sudo.extraRules = [
{ groups = [ "wheel" ]; commands = [ { command = "ALL"; options = [ "NOPASSWD" ]; } ]; }
];
## Harley
users.users.harley = {
isNormalUser = true;
shell = lib.mkForce pkgs.zsh;
extraGroups = [ "wheel" "networkmanager" "docker" "input" "uinput" "video"];
openssh.authorizedKeys.keys = [ "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIH4OAJ8iC7CQIcedVbU86xgoL4YkMWAG9bU6aj9v4Skb harley@utumno" ];
};
## Roman
users.users.roman = {
isNormalUser = true;
description = "Roman";
home = "/home/roman/";
createHome = true;
shell = pkgs.bash;
extraGroups = [ "wheel" "users" ];
openssh.authorizedKeys.keys = [ " " ];
};
## The Ephemeral user and filesystem
## The actual Ephemeral user
users.users.atomic = {
isNormalUser = true;
description = "Ephemeral User - home directory inside a tmpfs";
home = "/home/atomic";
group = "users";
createHome = false;
extraGroups = [ "wheel" ];
shell = lib.mkForce pkgs.zsh;
openssh.authorizedKeys.keys = [ "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIH4OAJ8iC7CQIcedVbU86xgoL4YkMWAG9bU6aj9v4Skb harley@utumno" ];
};
## filesystem for the Ephemeral user
fileSystems."/home/atomic" = {
device = "tmpfs";
fsType = "tmpfs";
options = [ "mode=0700" "uid=1001" "gid=100" "noatime" ];
};
## Populate the Ephemeral user from seed on boot
systemd.services.populate-atomic-home = {
description = " Populate atomic user's tmpfs home from seed";
after = [ "home-atomic.mount" ];
requires = [ "home-atomic.mount" ];
wantedBy = [ "home-atomic.mount" "systemd-logind.service" ];
script = ''
cp -rT /home/.atomic-persist /home/atomic
chown -R 1001:100 /home/atomic
'';
serviceConfig.Type = "oneshot";
serviceConfig.RemainAfterExit = true;
};
## Periodic Ephemeral home cleaner (reseed from skeleton)
systemd.services.atomic-home-cleaner = {
description = "Wipe atomic home if user has no active logind sessions";
script = ''
if ! pgrep -u atomic >/dev/null 2>&1; then
umount /home/atomic/mnt 2>/dev/null || true
rm -rf /home/atomic/.??* /home/atomic/** 2>/dev/null
cp -rT /home/.atomic-persist/ /home/atomic/
chown -R 1001:100 /home/atomic/
fi
'';
serviceConfig.Type = "oneshot";
};
systemd.timers.atomic-home-cleaner = {
description = "Check every 2 min if atomic user's sessions ended";
wantedBy = [ "timers.target" ];
timerConfig = {
OnBootSec = "1min";
OnUnitActiveSec = "30s";
};
};
## One-shot service to unmount and re-populate atomic's tmpfs home
systemd.services.atomic-fresh = {
description = "Wipe and re-populate atomic's tmpfs home";
after = [ "home-atomic.mount" ];
script = ''
loginctl terminate-user atomic 2>/dev/null || true
sleep 1
rm -rf /home/atomic/.??* /home/atomic/** 2>/dev/null || true
umount /home/atomic 2>/dev/null || true
mount /home/atomic
cp -rT /home/.atomic-persist/ /home/atomic/
chown -R 1001:100 /home/atomic/
'';
serviceConfig.Type = "oneshot";
path = [ pkgs.util-linux ];
serviceConfig.User = "root";
};
############################################################
## ##
## 7: General Packages ##
## ##
############################################################
environment.systemPackages = with pkgs; [
## Cli tools
vim wget curl progress git btop neovim tmux lsof bat ncdu nvtopPackages.full fastfetch
unzip unrar p7zip xclip
testdisk btdu
## Games
lutris rpcs3 mednafen mednafen-server mednaffe dolphin-emu
## System
adwaita-icon-theme tela-circle-icon-theme
sshfs sshpass pciutils usbutils python312 (pkgs.kdePackages.bluedevil) gtkgreet
cage guake android-tools patool pv ghostscript imagemagick nodejs go deno socat
fzf smartmontools shadow ripgrep fd jq tree qdirstat
firefox
## Multimedia
mpv geeqie ffmpeg yt-dlp
## Virtual display for Sunshine headless streaming (krfb-virtualmonitor)
kdePackages.krfb
];
############################################################
## ##
## 8) Services ##
## ##
############################################################
## Hermes WebUI (port 8787)
#systemd.services.hermes-webui = {
# description = "Hermes Web UI";
# after = [ "network-online.target" ];
# wants = [ "network-online.target" ];
# wantedBy = [ "multi-user.target" ];
# environment = {
# HERMES_WEBUI_HOST = "0.0.0.0";
# HERMES_WEBUI_PORT = "8787";
# HERMES_HOME = "/storage/8TB/Backups/Agents/.hermes";
# HERMES_WEBUI_STATE_DIR = "/storage/8TB/Backups/Agents/.hermes/webui";
# SEARXNG_URL = "http://10.0.1.49:8888";
# };
# serviceConfig = {
# Type = "simple";
# User = "harley";
# WorkingDirectory = "/opt/hermes-webui";
# ExecStart = "/opt/hermes-webui/venv/bin/python server.py";
# Restart = "on-failure";
# RestartSec = 5;
# };
# };
## SearXNG meta search engine (Port 8888)
services.searx = {
enable = true;
openFirewall = true;
redisCreateLocally = true;
settings = {
general.instance_name = "Utumno Search";
search = {
safe_search = 0;
formats = [ "html" "json" ];
autocomplete = "";
};
server = {
port = 8888;
bind_address = "0.0.0.0";
secret_key = "@SEARXNG_SECRET@";
limiter = false;
method = "POST";
image_proxy = false;
};
ui = {
default_theme = "simple";
theme_args.simple_style = "dark";
query_in_title = false;
};
outgoing = {
request_timeout = 5.0;
max_request_timeout = 15.0;
pool_connections = 100;
pool_maxsize = 20;
enable_http2 = true;
};
};
environmentFile = "/opt/searxng/secrets.env";
};
## Gitea (Port 3010)
services.gitea = {
enable = true;
stateDir = "/storage/8TB/Backups/Gitea";
repositoryRoot = "/storage/8TB/Backups/Gitea/repositories";
database.type = "sqlite3";
settings = {
server = {
DOMAIN = "10.0.1.49";
ROOT_URL = "http://10.0.1.49:3010";
HTTP_PORT = 3010;
SSH_PORT = 222;
DISABLE_SSH = false;
LFS_START_SERVER = false;
};
service = {
DISABLE_REGISTRATION = false;
REQUIRE_SIGNIN_VIEW = false;
};
security.INSTALL_LOCK = true;
};
};
## Kiwix ZIM server (port 8098)
services.kiwix-serve = {
enable = true;
port = 8098;
openFirewall = true;
library = {
# Update paths when ZIM files are added/removed
wikipedia = "/storage/20TB/Media/Kiwix/wikipedia_en_all_maxi_2026-02.zim";
archlinux = "/storage/20TB/Media/Kiwix/archlinux_en_all_maxi_2025-08.zim";
ifixit = "/storage/20TB/Media/Kiwix/ifixit_en_all_2025-06.zim";
khanacademy = "/storage/20TB/Media/Kiwix/khanacademy_en_all_2023-03.zim";
survivorlibrary = "/storage/20TB/Media/Kiwix/survivorlibrary.com_en_all_2025-06.zim";
urbanprepper = "/storage/20TB/Media/Kiwix/urban-prepper_en_all_2025-06.zim";
anonymousplanet = "/storage/20TB/Media/Kiwix/anonymousplanet.org_en_all_2025-08.zim";
basedcooking = "/storage/20TB/Media/Kiwix/based.cooking_en_all_2025-08.zim";
};
extraArgs = [ "--skipInvalid" ];
};
## OpenSpeedTest (Port 8084)
services.phpfpm.pools.openspeedtest = {
user = "nginx";
group = "nginx";
settings = {
pm = "dynamic";
"pm.max_children" = 5;
"pm.start_servers" = 2;
"pm.min_spare_servers" = 1;
"pm.max_spare_servers" = 3;
"listen" = "/run/phpfpm-openspeedtest.sock";
};
};
## Immich (Port 2283)
#services.immich = {
#enable = true;
#host = "10.10.10.1";
#mediaLocation = "/storage/8TB/Documents/immich";
#port = 2283;
#openFirewall = true;
#database.enable = true;
#redis.enable = true;
#};
## Roku Remote (Port 7777)
## btop-web system monitor (Port 8777)
systemd.services.btop-web = {
description = "btop-style web system monitor";
after = [ "network-online.target" ];
wants = [ "network-online.target" ];
wantedBy = [ "multi-user.target" ];
serviceConfig = {
Type = "simple";
User = "harley";
WorkingDirectory = "/opt/btop-web";
ExecStart = "${pkgs.python3}/bin/python server.py";
Restart = "always";
RestartSec = 5;
};
};
systemd.services.roku-remote = {
description = "Roku TV Web Remote";
after = [ "network-online.target" ];
wants = [ "network-online.target" ];
wantedBy = [ "multi-user.target" ];
serviceConfig = {
Type = "simple";
User = "harley";
WorkingDirectory = "/opt/roku-remote";
ExecStart = "${pkgs.python3}/bin/python server.py";
Restart = "on-failure";
RestartSec = 5;
};
};
##Sunshine
services.sunshine = {
enable = true;
autoStart = true;
capSysAdmin = true; # only needed for Wayland -- omit this when using with Xorg
openFirewall = true;
};
## uninput enable for Sunshine.
hardware.uinput.enable = true;
##
##
## 8.1: Docker
##
##
virtualisation.docker = {
enable = true;
# Allow non-root users (harley is in docker group)
enableOnBoot = true;
# Use the overlay2 storage driver (default on modern kernels)
storageDriver = "overlay2";
# Auto-prune unused data
autoPrune.enable = true;
autoPrune.dates = "weekly";
};
## Firecrawl web-scraper (Port 3002)
systemd.services.firecrawl = {
description = "Firecrawl web scraping stack";
after = [ "docker.service" "network-online.target" ];
wants = [ "docker.service" "network-online.target" ];
wantedBy = [ "multi-user.target" ];
path = [ pkgs.docker ];
serviceConfig = {
Type = "oneshot";
RemainAfterExit = true;
WorkingDirectory = "/opt/firecrawl";
ExecStart = "${pkgs.docker}/bin/docker compose up -d";
ExecStop = "${pkgs.docker}/bin/docker compose down";
};
};
########## ##########
########## ##########
########## 8.2: ARRS ##########
########## ##########
########## ##########
## PIA VPN + qBittorrent (Port 8088) + SOCKS5 (Port 1080) (Docker compose)
systemd.services.pia-qbit = {
description = "PIA VPN + qBittorrent + SOCKS5 proxy stack";
after = [ "docker.service" "network-online.target" ];
wants = [ "docker.service" "network-online.target" ];
wantedBy = [ "multi-user.target" ];
path = [ pkgs.docker ];
serviceConfig = {
Type = "oneshot";
RemainAfterExit = true;
WorkingDirectory = "/opt/pia-qbit";
ExecStart = "${pkgs.docker}/bin/docker compose up -d";
ExecStop = "${pkgs.docker}/bin/docker compose down";
# Give VPN time to establish before declaring "done"
ExecStartPost = "${pkgs.coreutils}/bin/sleep 10";
};
};
## Bitmagnet (Port 3333)
#services.bitmagnet = {
# enable = true;
#openFirewall = true;
#settings = {
#http_server.local_address = ":3333";
#dht_server.port = 3334;
#postgres = {
#name = "bitmagnet";
#user = "bitmagnet";
#password = "";
#};
#};
#};
#services.postgresql = {
#dataDir = "/storage/20TB/Computer-Related/Bitmagnet-data";
#package = pkgs.postgresql_16;
#};
## Jellyfin (Port 8096)
services.jellyfin = {
enable = true;
dataDir = "/storage/8TB/arrs-tmp/jellyfin";
cacheDir = "/storage/8TB/arrs-tmp/jellyfin-cache";
openFirewall = true;
};
## Prowlarr (port 9696)
services.prowlarr = {
enable = true;
dataDir = "/storage/8TB/arrs-tmp/prowlarr";
openFirewall = true;
};
## Sonarr (port 8989)
services.sonarr = {
enable = true;
dataDir = "/storage/8TB/arrs-tmp/sonarr";
openFirewall = true;
};
## Radarr (port 7878)
services.radarr = {
enable = true;
dataDir = "/storage/8TB/arrs-tmp/radarr";
openFirewall = true;
};
## SABnzbd (port 8180)
fileSystems."/var/lib/sabnzbd" = {
device = "/storage/8TB/arrs-tmp/sabnzbd";
options = [ "bind" "noauto" ];
fsType = "none";
};
services.sabnzbd = {
enable = true;
openFirewall = true;
settings = {
host = "0.0.0.0";
port = 8180;
misc = {
host = "0.0.0.0";
port = 8180;
enable_https = false;
html_login = true;
inet_exposure = 0;
auto_browser = false;
config_lock = false;
check_new_rel = false;
cache_limit = "";
};
};
};
############################################################
## ##
## 9: Customization ##
## ##
############################################################
##
##
## 9.1: Keyd keyboard remappings
##
##
services.keyd = {
enable = true;
keyboards = {
default = {
ids = [ "*" ];
settings = {
main = {
esc = "capslock";
capslock = "overload(nav, esc)";
};
nav = {
w = "up";
a = "left";
s = "down";
d = "right";
b = "C-left";
u = "pageup";
x = "delete";
g = "home";
n = "end";
h = "left";
j = "down";
k = "up";
l = "right";
space = "capslock";
};
};
};
q = {
ids = [ "*" ];
settings = {
main = {
esc = "capslock";
capslock = "overload(nav, esc)";
};
nav = {
w = "up";
a = "left";
s = "down";
d = "right";
h = "left";
j = "down";
k = "up";
l = "right";
space = "capslock";
};
};
};
};
};
##
##
## 9.2 Enviroment Variables
##
##
environment.variables = {
GTK_THEME = "Adwaita:dark";
GTK_ICON_THEME = "Tela-circle-dark";
HERMES_HOME = "/storage/8TB/Backups/Agents/.hermes";
};
## ZSH with Oh-My-Zsh
programs.zsh.enable = true;
programs.zsh.ohMyZsh = {
enable = true;
theme = "jonathan";
plugins = [
"git"
"sudo"
"web-search"
"history"
"extract"
"colored-man-pages"
];
customPkgs = [
(pkgs.runCommand "jonathan-zsh-theme" {
themeFile = builtins.readFile ./jonathan.zsh-theme;
passAsFile = [ "themeFile" ];
} ''
mkdir -p $out/share/zsh/themes
cp "$themeFilePath" $out/share/zsh/themes/jonathan.zsh-theme
'')
];
};
programs.zsh.syntaxHighlighting.enable = true;
programs.zsh.interactiveShellInit = ''
source ${pkgs.zsh-autosuggestions}/share/zsh-autosuggestions/zsh-autosuggestions.zsh
'';
programs.zsh.promptInit = ''
bindkey '^F' autosuggest-accept
bindkey '^[[C' autosuggest-accept
'';
programs.zsh.shellAliases = {
cl = "clear";
tt = "tmux";
tta = "tmux attach";
ttls = "tmux ls";
xclipp = "xclip -selection clipboard";
palantir = "ssh palantir";
rcp = "rsync -avP --partial --append";
};
# Make theme packages available to all users
environment.sessionVariables = {
XDG_DATA_DIRS = [
"${pkgs.tela-circle-icon-theme}/share/icons"
"${pkgs.catppuccin}/share/plasma"
];
};
############################################################
## ##
## ##
## ##
############################################################
## ── State version ────────────────────────────────────────
system.stateVersion = "26.11";
}
Symlink
+1
View File
@@ -0,0 +1 @@
/nix/store/z8fhfhzapq6qwprsz3zgi15hvv20sh40-nixos-system-utumno-26.11.20260705.d407951