Files
homelab/hosts/terra/configuration.nix
T
darmanandClaude Sonnet 5 713d91d5fc terra: finish removing Hermes Agent (module import + secrets)
Follow-up to e5ba1bf — that commit only staged the deleted module file.
Drops the flake module import, opencode_go_api_key/telegram_bot_token
secrets, and the stale hermes-agent.nix cross-reference in ollama's
context_length comment.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-19 22:49:50 +02:00

148 lines
6.3 KiB
Nix

# ---- TERRA ----
{ config, pkgs, lib, inputs, ... }:
let
unstable = import inputs.nixpkgs-unstable {
inherit (pkgs.stdenv.hostPlatform) system;
config = pkgs.config;
};
in
{
imports = [
./hardware-configuration.nix
./disk-config.nix
./secrets.nix
../../common.nix
../../services/containers.nix
../../services/vpn/tailscale.nix
../../services/desktop/desktop-hyprland.nix
../../services/desktop/desktop-apps.nix
../../services/desktop/librechat.nix
];
networking.hostName = "terra";
services.flatpak = {
enable = true;
remotes = [{ name = "flathub"; location = "https://dl.flathub.org/repo/flathub.flatpakrepo"; }];
packages = [
{ appId = "com.github.tchx84.Flatseal"; origin = "flathub"; }
{ appId = "com.blitzfc.qbz"; origin = "flathub"; }
{ appId = "com.discordapp.Discord"; origin = "flathub"; }
{ appId = "org.telegram.desktop"; origin = "flathub"; }
{ appId = "com.bambulab.BambuStudio"; origin = "flathub"; }
];
};
environment.systemPackages = [ unstable.proton-pass-cli ];
# ---- nix-ld: lets generic dynamically-linked Linux binaries run as-is —
# needed for editor extensions (Zed/VSCode LSPs, debuggers, etc.) that
# download prebuilt binaries not built for NixOS. See
# https://nix.dev/permalink/stub-ld ----
programs.nix-ld.enable = true;
# ---- home-manager (user-level config for darman) ----
home-manager.useGlobalPkgs = true;
home-manager.useUserPackages = true;
home-manager.backupFileExtension = "hm-bak";
home-manager.extraSpecialArgs = { inherit unstable inputs; };
home-manager.users.darman = import ./home.nix;
# ---- Boot (UEFI) ----
boot.loader.systemd-boot.enable = true;
boot.loader.efi.canTouchEfiVariables = true;
hardware.cpu.amd.updateMicrocode = true;
# mercury (aarch64) is built/flashed from here. Without this, `nix build`
# for it dies with "platform mismatch" — no qemu binfmt handler registered
# and aarch64-linux missing from nix.settings.extra-platforms. This module
# sets up both (see CLAUDE.md's aarch64 gotcha).
boot.binfmt.emulatedSystems = [ "aarch64-linux" ];
# ---- GPU (Radeon RX 6800 XT / Navi 21) ----
hardware.enableRedistributableFirmware = true;
boot.initrd.kernelModules = [ "amdgpu" ];
# /dev/dri/renderD128 is root:render 0660, so rootless podman containers can
# only reach the GPU if the *host* user is in render. Needed by the Vulkan
# whisper.cpp/llama.cpp containers in ~/Data/Dev/repos/content-trigger-scanner.
users.users.darman.extraGroups = [ "render" "video" ];
# ---- ollama (local LLM server, ROCm on the 6800 XT) ----
# Navi 21 is gfx1030 — officially supported by ROCm, so no
# rocmOverrideGfx/HSA_OVERRIDE_GFX_VERSION needed (that's for gpus ROCm
# doesn't recognize, e.g. RDNA1/gfx101x). The upstream module runs the
# service under DynamicUser with SupplementaryGroups=["render"] and
# DeviceAllow for char-kfd/char-drm/char-fb already, so unlike jellyfin's
# static user it needs no extraGroups wiring here.
services.ollama = {
enable = true;
package = pkgs.ollama-rocm;
# keep in sync with services/desktop/librechat.nix's endpoints.custom
# default model — LibreChat's config schema needs a non-empty default
# even though fetch=true replaces it with whatever's actually pulled.
# gemma4:12b: general chat/coding daily driver, fits fully in 16G VRAM —
# also doubles as the memory-extraction agent (see librechat.nix): a
# 3b model (llama3.2:3b, dropped) couldn't reliably tell the user's
# stated facts apart from its own boilerplate, e.g. saving "I am an AI
# assistant with tool calling capabilities" as the user's personal_info
# after "Hi I'm Erik Simon". Reusing gemma4:12b for both roles also means
# no second model needs to swap into VRAM while it's already the active
# chat model.
# qwen3.6:35b-a3b: MoE (3B active/36B total), ~24GB Q4_K_M — doesn't fit
# in VRAM alone, so ollama offloads the inactive experts to CPU RAM.
# Sparse activation makes that far less painful than it'd be for a dense
# model this size, but still expect it to run slower than the two above.
loadModels = [ "gemma4:12b" "qwen3.6:35b-a3b" ];
# Ollama truncates context far below the model's real window unless
# told otherwise (the OpenAI-compat /v1 route it's reached through has
# no way to set this per-request). 131072 chosen as the practical
# ceiling after load-testing with real prompts, not just idle
# `ollama ps` checks:
# 32768 (31.6k-token prompt) and 65536 (40.8k-token prompt) both stayed
# 100% GPU with VRAM barely moving (~10.1G / ~10.67G of 16G) — KV cache
# cost barely grows with context, likely sliding-window/local attention
# on most of gemma4:12b's layers. At 131072 that stopped being true: a
# ~108k-token prompt pushed VRAM to ~11.4G/16G (still 100% GPU, no CPU
# spillover, negligible GTT) but with visibly shrinking headroom, and
# prefill throughput measurably dropped (~490 -> ~460 tok/s) over just
# the last 13k tokens — filling the full window would take minutes of
# pure prompt processing. Stopped here rather than push further: next
# doubling would risk CPU spillover under any concurrent GPU load
# (desktop compositor, jellyfin transcode) for diminishing benefit.
environmentVariables.OLLAMA_CONTEXT_LENGTH = "131072";
};
# ---- Dev-data disks — NOT in disko, mounted read-write, never wiped ----
fileSystems."/mnt/hdd_01" = {
device = "/dev/disk/by-uuid/b8445126-ec6d-4f88-818a-d9e13031d9a4";
fsType = "ext4";
options = [ "nofail" ];
};
fileSystems."/mnt/ssd_01" = {
device = "/dev/disk/by-uuid/6ca18a9f-27bc-4e58-aea8-de43a0d0ed5d";
fsType = "ext4";
options = [ "nofail" ];
};
# jupiter's samba share (services/network/samba.nix) — mounted on demand so
# terra doesn't stall boot/login when jupiter is off or unreachable.
fileSystems."/mnt/jupiter" = {
device = "//jupiter/data";
fsType = "cifs";
options = [
"credentials=${config.sops.templates."jupiter-smb.credentials".path}"
"uid=1000"
"gid=100"
"nofail"
"x-systemd.automount"
"x-systemd.idle-timeout=60"
"x-systemd.mount-timeout=10s"
"_netdev"
];
};
system.stateVersion = "26.05";
}