phase 2: move host files to hosts/{mreow,yarn,muffin}/

This commit is contained in:
primary
2026-04-18 00:47:25 -04:00
parent 6448a0427f
commit 99e98e39b7
13 changed files with 0 additions and 0 deletions

328
hosts/muffin/default.nix Normal file
View File

@@ -0,0 +1,328 @@
{
config,
lib,
pkgs,
hostname,
username,
eth_interface,
service_configs,
options,
...
}:
{
imports = [
./modules/hardware.nix
./modules/zfs.nix
./modules/impermanence.nix
./modules/usb-secrets.nix
./modules/age-secrets.nix
./modules/secureboot.nix
./modules/no-rgb.nix
./modules/security.nix
./modules/ntfy-alerts.nix
./modules/power.nix
./services/postgresql.nix
./services/jellyfin
./services/caddy
./services/immich.nix
./services/gitea.nix
./services/gitea-actions-runner.nix
./services/minecraft.nix
./services/wg.nix
./services/qbittorrent.nix
./services/bitmagnet.nix
./services/arr/prowlarr.nix
./services/arr/sonarr.nix
./services/arr/radarr.nix
./services/arr/bazarr.nix
./services/arr/jellyseerr.nix
./services/arr/recyclarr.nix
./services/arr/arr-search.nix
./services/arr/torrent-audit.nix
./services/arr/init.nix
./services/soulseek.nix
# ./services/llama-cpp.nix
./services/trilium.nix
./services/ups.nix
./services/grafana
./services/bitwarden.nix
./services/firefox-syncserver.nix
./services/matrix
./services/monero
./services/graphing-calculator.nix
./services/ssh.nix
./services/syncthing.nix
./services/ntfy
./services/mollysocket.nix
./services/harmonia.nix
./services/ddns-updater.nix
];
# Hosts entries for CI/CD deploy targets
networking.hosts."192.168.1.50" = [ "server-public" ];
networking.hosts."192.168.1.223" = [ "desktop" ];
# SSH known_hosts for CI runner (pinned host keys)
environment.etc."ci-known-hosts".text = ''
server-public ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIFMjgaMnE+zS7tL+m5E7gh9Q9U1zurLdmU0qcmEmaucu
192.168.1.50 ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIFMjgaMnE+zS7tL+m5E7gh9Q9U1zurLdmU0qcmEmaucu
git.sigkill.computer ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIFMjgaMnE+zS7tL+m5E7gh9Q9U1zurLdmU0qcmEmaucu
git.gardling.com ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIFMjgaMnE+zS7tL+m5E7gh9Q9U1zurLdmU0qcmEmaucu
'';
services.kmscon.enable = true;
# Disable serial getty on ttyS0 to prevent dmesg warnings
systemd.services."serial-getty@ttyS0".enable = false;
# srvos enables vim, i don't want to use vim, disable it here:
programs.vim = {
defaultEditor = false;
}
// lib.optionalAttrs (options.programs.vim ? enable) {
enable = false;
};
# https://github.com/NixOS/nixpkgs/issues/101459#issuecomment-758306434
security.pam.loginLimits = [
{
domain = "*";
type = "soft";
item = "nofile";
value = "4096";
}
];
nix = {
# optimize the store
optimise.automatic = true;
# garbage collection
gc = {
automatic = true;
dates = "weekly";
options = "--delete-older-than 7d";
};
};
# Intel Arc A380 (DG2, 56a5) uses the i915 driver on kernel 6.12.
# The xe driver's iHD media driver integration has buffer mapping
# failures on this GPU/kernel combination. i915 works correctly for
# VAAPI transcode as long as ASPM deep states are disabled for the
# GPU (see modules/power.nix).
hardware.intelgpu.driver = "i915";
# Per-service 2MB hugepage budget calculated in service-configs.nix.
boot.kernel.sysctl."vm.nr_hugepages" = service_configs.hugepages_2m.total_pages;
boot = {
# 6.12 LTS until 2027-03. Kernel 6.18 causes a reproducible ZFS deadlock
# in dbuf_evict due to page allocator changes (__free_frozen_pages).
# https://github.com/openzfs/zfs/issues/18426
kernelPackages = pkgs.linuxPackages_6_12;
loader = {
# Use the systemd-boot EFI boot loader.
# Disabled: ASRock B550M Pro4 AMI UEFI hangs on POST when NixOS
# writes EFI variables (NVRAM corruption). Lanzaboote boot entries
# are discovered via BLS Type #2 on the ESP, so this is not needed.
efi.canTouchEfiVariables = false;
# 1s timeout
timeout = 1;
};
initrd = {
compressor = "zstd";
supportedFilesystems = [ "f2fs" ];
};
};
environment.etc = {
"issue".text = "";
};
# Set your time zone.
time.timeZone = "America/New_York";
hardware.graphics = {
enable = true;
extraPackages = with pkgs; [
libva-vdpau-driver
intel-compute-runtime # OpenCL filter support (hardware tonemapping and subtitle burn-in)
vpl-gpu-rt # QSV on 11th gen or newer
];
};
#fwupd for updating firmware
services.fwupd = {
enable = true;
extraRemotes = [ "lvfs-testing" ];
};
environment.systemPackages = with pkgs; [
helix
lm_sensors
bottom
htop
doas-sudo-shim
neofetch
borgbackup
smartmontools
ripgrep
intel-gpu-tools
iotop
iftop
tmux
wget
powertop
lsof
reflac
pfetch-rs
sbctl
# add `skdump`
libatasmart
];
networking = {
nameservers = [
"1.1.1.1"
"9.9.9.9"
];
hostName = hostname;
hostId = "0f712d56";
firewall.enable = true;
useDHCP = false;
# Disabled because of Jellyfin (various issues)
enableIPv6 = false;
interfaces.${eth_interface} = {
ipv4.addresses = [
{
address = "192.168.1.50";
# address = "10.1.1.102";
prefixLength = 24;
}
];
ipv6.addresses = [
{
address = "fe80::9e6b:ff:fe4d:abb";
prefixLength = 64;
}
];
};
defaultGateway = {
#address = "10.1.1.1";
address = "192.168.1.1";
interface = eth_interface;
};
# TODO! fix this
# defaultGateway6 = {
# address = "fe80::/64";
# interface = eth_interface;
# };
};
users.groups.${service_configs.media_group} = { };
users.users.gitea-runner = {
isSystemUser = true;
group = "gitea-runner";
home = "/var/lib/gitea-runner";
description = "Gitea Actions CI runner";
};
users.groups.gitea-runner = { };
users.users.${username} = {
isNormalUser = true;
extraGroups = [
"wheel"
"video"
"render"
service_configs.media_group
];
hashedPasswordFile = config.age.secrets.hashedPass.path;
};
# https://nixos.wiki/wiki/Fish#Setting_fish_as_your_shell
programs.fish.enable = true;
programs.bash = {
interactiveShellInit = ''
if [[ $(${pkgs.procps}/bin/ps --no-header --pid=$PPID --format=comm) != "fish" && -z ''${BASH_EXECUTION_STRING} ]]
then
shopt -q login_shell && LOGIN_OPTION='--login' || LOGIN_OPTION=""
exec ${pkgs.fish}/bin/fish $LOGIN_OPTION
fi
'';
};
security = {
#lets use doas and not sudo!
doas.enable = true;
sudo.enable = false;
# Configure doas
doas.extraRules = [
{
users = [ username ];
keepEnv = true;
persist = true;
}
];
};
services.murmur = {
enable = true;
openFirewall = true;
welcometext = "meow meow meow meow meow :3 xd";
password = "$MURMURD_PASSWORD";
environmentFile = config.age.secrets.murmur-password-env.path;
port = service_configs.ports.public.murmur.port;
};
# services.botamusique = {
# enable = true;
# settings = {
# server = {port = config.services.murmur.port;
# password = config.services.murmur.password;
# };
# };
# };
# systemd.tmpfiles.rules = [
# "Z /tank/music 775 ${username} users"
# ];
system.stateVersion = "24.11";
}

59
hosts/muffin/disk.nix Normal file
View File

@@ -0,0 +1,59 @@
{ inputs, ... }:
{
imports = [
inputs.disko.nixosModules.disko
];
disko.devices = {
disk = {
main = {
type = "disk";
content = {
type = "gpt";
partitions = {
ESP = {
type = "EF00";
size = "500M";
content = {
type = "filesystem";
format = "vfat";
mountpoint = "/boot";
};
};
persistent = {
size = "20G";
content = {
type = "filesystem";
format = "f2fs";
mountpoint = "/persistent";
};
};
nix = {
size = "100%";
content = {
type = "filesystem";
format = "f2fs";
mountpoint = "/nix";
};
};
};
};
};
};
nodev = {
"/" = {
fsType = "tmpfs";
mountOptions = [
"defaults"
"size=2G"
"mode=755"
];
};
};
};
fileSystems."/persistent".neededForBoot = true;
fileSystems."/nix".neededForBoot = true;
}

62
hosts/muffin/hardware.nix Normal file
View File

@@ -0,0 +1,62 @@
{
config,
lib,
pkgs,
service_configs,
...
}:
let
hddTuneIosched = pkgs.writeShellScript "hdd-tune-iosched" ''
# Called by udev with the partition kernel name (e.g. sdb1).
# Derives the parent disk and applies mq-deadline iosched params.
parent=''${1%%[0-9]*}
dev="/sys/block/$parent"
[ -d "$dev/queue/iosched" ] || exit 0
echo 500 > "$dev/queue/iosched/read_expire"
echo 15000 > "$dev/queue/iosched/write_expire"
echo 128 > "$dev/queue/iosched/fifo_batch"
echo 16 > "$dev/queue/iosched/writes_starved"
echo 4096 > "$dev/queue/max_sectors_kb" 2>/dev/null || true
'';
in
{
boot.initrd.availableKernelModules = [
"xhci_pci"
"ahci"
"usb_storage"
"usbhid"
"sd_mod"
];
boot.initrd.kernelModules = [ "dm-snapshot" ];
boot.kernelModules = [ "kvm-amd" ];
boot.extraModulePackages = [ ];
swapDevices = [ ];
hardware.cpu.amd.updateMicrocode = true;
hardware.enableRedistributableFirmware = true;
# HDD I/O tuning for torrent seeding workload (high-concurrency random reads)
# sharing the pool with latency-sensitive sequential reads (Jellyfin playback).
#
# mq-deadline sorts requests into elevator sweeps, reducing seek distance.
# read_expire=500ms keeps reads bounded so a Jellyfin segment can't queue for
# seconds behind a torrent burst; write_expire=15s lets the scheduler batch
# writes for coalescence (torrent writes are async and tolerate delay).
# The bulk of read coalescence already happens above the scheduler via ZFS
# aggregation (zfs_vdev_aggregation_limit=4M, read_gap_limit=128K,
# async_read_max=32), so the scheduler deadline only needs to be large enough
# to keep the elevator sweep coherent -- 500ms is plenty on rotational disks.
# fifo_batch=128 keeps sweeps long; writes_starved=16 heavily favors reads.
# 4 MiB readahead matches libtorrent piece extent affinity for sequential prefetch.
#
# The NixOS ZFS module hardcodes a udev rule that forces scheduler="none" on all
# ZFS member partitions' parent disks (on both add AND change events). We counter
# it with lib.mkAfter so our rule appears after theirs in 99-local.rules — our
# rule matches the same partition events and sets mq-deadline back, then a RUN
# script applies the iosched params. Only targets rotational, non-removable disks
# (i.e. HDDs, not SSDs or USB).
services.udev.extraRules = lib.mkAfter ''
ACTION=="add|change", KERNEL=="sd[a-z]*[0-9]*", ENV{ID_FS_TYPE}=="zfs_member", ATTR{../queue/rotational}=="1", ATTR{../removable}=="0", ATTR{../queue/scheduler}="mq-deadline", ATTR{../queue/read_ahead_kb}="4096", ATTR{../queue/nr_requests}="512", RUN+="${hddTuneIosched} %k"
'';
}

31
hosts/muffin/home.nix Normal file
View File

@@ -0,0 +1,31 @@
{
pkgs,
lib,
...
}:
{
home.stateVersion = "24.11";
programs.fish = {
enable = true;
interactiveShellInit = ''
# disable greeting
set fish_greeting
# pfetch on shell start (disable pkgs because of execution time)
PF_INFO="ascii title os host kernel uptime memory editor wm" ${lib.getExe pkgs.pfetch-rs}
'';
shellAliases =
let
eza = "${lib.getExe pkgs.eza} --color=always --group-directories-first";
in
{
# from DistroTube's dot files: Changing "ls" to "eza"
ls = "${eza} -al";
la = "${eza} -a";
ll = "${eza} -l";
lt = "${eza} -aT";
};
};
}

View File

@@ -0,0 +1,364 @@
rec {
zpool_ssds = "tank";
zpool_hdds = "hdds";
torrents_path = "/torrents";
services_dir = "/services";
music_dir = "/${zpool_ssds}/music";
media_group = "media";
cpu_arch = "znver3";
ports = {
# Ports exposed to the internet. The flake asserts every public port
# appears in the corresponding firewall allow-list (TCP, UDP, or both).
public = {
http = {
port = 80;
proto = "tcp";
};
https = {
port = 443;
proto = "both";
}; # HTTP/3 QUIC
minecraft = {
port = 25565;
proto = "tcp";
};
syncthing_protocol = {
port = 22000;
proto = "both";
}; # QUIC
syncthing_discovery = {
port = 21027;
proto = "udp";
};
matrix_federation = {
port = 8448;
proto = "both";
}; # HTTP/3 QUIC
coturn = {
port = 3478;
proto = "both";
};
coturn_tls = {
port = 5349;
proto = "both";
};
livekit = {
port = 7880;
proto = "tcp";
};
soulseek_listen = {
port = 50300;
proto = "tcp";
};
monero = {
port = 18080;
proto = "tcp";
};
monero_rpc = {
port = 18081;
proto = "tcp";
}; # restricted public RPC
p2pool_p2p = {
port = 37889;
proto = "tcp";
};
murmur = {
port = 64738;
proto = "both";
};
};
# Ports bound to localhost / VPN only. The flake asserts none of
# these appear in the firewall allow-lists.
private = {
jellyfin = {
port = 8096;
proto = "tcp";
};
torrent = {
port = 6011;
proto = "tcp";
};
# Webhook receiver for the Jellyfin-qBittorrent monitor — Jellyfin pushes
# playback events here so throttling reacts without waiting for the poll.
jellyfin_qbittorrent_monitor_webhook = {
port = 9898;
proto = "tcp";
};
bitmagnet = {
port = 3333;
proto = "tcp";
};
gitea = {
port = 2283;
proto = "tcp";
};
immich = {
port = 2284;
proto = "tcp";
};
soulseek_web = {
port = 5030;
proto = "tcp";
};
vaultwarden = {
port = 8222;
proto = "tcp";
};
syncthing_gui = {
port = 8384;
proto = "tcp";
};
matrix = {
port = 6167;
proto = "tcp";
};
ntfy = {
port = 2586;
proto = "tcp";
};
lk_jwt = {
port = 8081;
proto = "tcp";
};
prowlarr = {
port = 9696;
proto = "tcp";
};
sonarr = {
port = 8989;
proto = "tcp";
};
radarr = {
port = 7878;
proto = "tcp";
};
bazarr = {
port = 6767;
proto = "tcp";
};
jellyseerr = {
port = 5055;
proto = "tcp";
};
monero_zmq = {
port = 18083;
proto = "tcp";
};
p2pool_stratum = {
port = 3334;
proto = "tcp";
};
firefox_syncserver = {
port = 5000;
proto = "tcp";
};
mollysocket = {
port = 8020;
proto = "tcp";
};
grafana = {
port = 3000;
proto = "tcp";
};
prometheus = {
port = 9090;
proto = "tcp";
};
prometheus_node = {
port = 9100;
proto = "tcp";
};
prometheus_apcupsd = {
port = 9162;
proto = "tcp";
};
llama_cpp = {
port = 6688;
proto = "tcp";
};
trilium = {
port = 8787;
proto = "tcp";
};
jellyfin_exporter = {
port = 9594;
proto = "tcp";
};
qbittorrent_exporter = {
port = 9561;
proto = "tcp";
};
igpu_exporter = {
port = 9563;
proto = "tcp";
};
prometheus_zfs = {
port = 9134;
proto = "tcp";
};
harmonia = {
port = 5500;
proto = "tcp";
};
};
};
https = {
certs = services_dir + "/http_certs";
domain = "sigkill.computer";
old_domain = "gardling.com"; # Redirect traffic from old domain
};
gitea = {
dir = services_dir + "/gitea";
domain = "git.${https.domain}";
};
postgres = {
socket = "/run/postgresql";
dataDir = services_dir + "/sql";
shared_buffers_m = 128; # PostgreSQL default; update if you change shared_buffers
};
immich = {
dir = services_dir + "/immich";
};
minecraft = {
parent_dir = services_dir + "/minecraft";
server_name = "main";
memory = {
heap_size_m = 4000;
large_page_size_m = 2;
};
};
torrent = {
SavePath = torrents_path;
TempPath = torrents_path + "/incomplete";
categories = {
anime = torrents_path + "/anime";
archive = torrents_path + "/archive";
audiobooks = torrents_path + "/audiobooks";
books = torrents_path + "/books";
games = torrents_path + "/games";
movies = torrents_path + "/movies";
music = torrents_path + "/music";
musicals = torrents_path + "/musicals";
tvshows = torrents_path + "/tvshows";
};
};
jellyfin = {
dataDir = services_dir + "/jellyfin";
cacheDir = services_dir + "/jellyfin_cache";
};
slskd = rec {
base = "/var/lib/slskd";
downloads = base + "/downloads";
incomplete = base + "/incomplete";
};
vaultwarden = {
path = "/var/lib/vaultwarden";
};
monero = {
dataDir = services_dir + "/monero";
};
p2pool = {
dataDir = services_dir + "/p2pool";
walletAddress = "49b6NT2k7fQHs8JvF7naUvchYwTQmRpoMMXb1KJTg5UcZVmyPJ7n6jgiH8DrvEsMg5GvMjJqPB1c1PTBAYtUTsbeHe5YMBx";
};
matrix = {
dataDir = "/var/lib/continuwuity";
domain = "matrix.${https.domain}";
};
ntfy = {
domain = "ntfy.${https.domain}";
};
mollysocket = {
domain = "mollysocket.${https.domain}";
};
livekit = {
domain = "livekit.${https.domain}";
};
syncthing = {
dataDir = services_dir + "/syncthing";
signalBackupDir = "/${zpool_ssds}/bak/signal";
grayjayBackupDir = "/${zpool_ssds}/bak/grayjay";
};
prowlarr = {
dataDir = services_dir + "/prowlarr";
};
sonarr = {
dataDir = services_dir + "/sonarr";
};
radarr = {
dataDir = services_dir + "/radarr";
};
bazarr = {
dataDir = services_dir + "/bazarr";
};
jellyseerr = {
configDir = services_dir + "/jellyseerr";
};
recyclarr = {
dataDir = services_dir + "/recyclarr";
};
firefox_syncserver = {
domain = "firefox-sync.${https.domain}";
};
grafana = {
dir = services_dir + "/grafana";
domain = "grafana.${https.domain}";
};
trilium = {
dataDir = services_dir + "/trilium";
};
media = {
moviesDir = torrents_path + "/media/movies";
tvDir = torrents_path + "/media/tv";
};
# Per-service 2MB hugepage budget.
# Each value is the service's hugepage consumption in MB, derived from
# its actual memory configuration. The kernel sysctl vm.nr_hugepages
# is set to total_pages so every service gets what it needs.
hugepages_2m = rec {
page_size_m = 2;
# RandomX dataset (2048MB) + cache (256MB) = 2304MB per instance.
# Both monerod and p2pool allocate their own full copy via MAP_HUGETLB.
randomx_instance_m = 2048 + 256;
services = {
minecraft_m = minecraft.memory.heap_size_m; # JVM heap via -XX:+UseLargePages
monerod_m = randomx_instance_m; # block verification dataset
p2pool_m = randomx_instance_m; # mining dataset
postgres_m = postgres.shared_buffers_m; # huge_pages = try (default)
};
total_pages = builtins.foldl' (a: b: a + b) 0 (builtins.attrValues services) / page_size_m;
};
}