Files
lovey/nixos/configuration.nix
Jason WittyandClaude Opus 5.5 cff8cf640e Keep 275 W power cap at boot
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-26 21:07:54 -07:00

165 lines
6.9 KiB
Nix

# llm-nixos: VM 200 on lovey. 2x RTX 3090 via VFIO, model weights on virtiofs /models.
{ config, lib, pkgs, ... }:
{
imports = [ ./hardware-configuration.nix ./gpu-unlock.nix ];
boot.loader.systemd-boot.enable = true;
boot.loader.efi.canTouchEfiVariables = true;
boot.loader.systemd-boot.configurationLimit = 5; # initrds carry ~84 MB of gpu-unlock firmware
boot.kernelPackages = pkgs.linuxPackages; # LTS-ish default, keeps the NVIDIA module happy
networking.hostName = "llm-nixos";
# Static: lovey-fans only accepts GPU temps from this address
networking.useDHCP = false;
networking.interfaces.ens18.ipv4.addresses = [ { address = "192.168.1.67"; prefixLength = 24; } ];
networking.defaultGateway = { address = "192.168.1.1"; interface = "ens18"; };
networking.nameservers = [ "1.1.1.1" "8.8.8.8" ];
networking.firewall.enable = true;
networking.firewall.allowedTCPPorts = [ 22 8080 ]; # SSH, Strata API (key required)
time.timeZone = "America/Los_Angeles";
i18n.defaultLocale = "en_US.UTF-8";
nixpkgs.config.allowUnfree = true;
nix.settings = {
experimental-features = [ "nix-command" "flakes" ];
# prebuilt CUDA packages, so nothing CUDA-heavy compiles locally
substituters = [ "https://cache.nixos.org" "https://cuda-maintainers.cachix.org" ];
trusted-public-keys = [
"cache.nixos.org-1:6NCHdD59X431o0gWypbMrAURkbJ16ZPMQFGspcDShjY="
"cuda-maintainers.cachix.org-1:0dq3bujKpuEPMCX6U4WylrUDZ9JyUG0VpVZa7CNfq5E="
];
};
# NVIDIA: open kernel module (Ampere is supported), headless, persistence daemon
services.xserver.videoDrivers = [ "nvidia" ];
hardware.graphics.enable = true;
hardware.nvidia = {
open = true;
package = config.boot.kernelPackages.nvidiaPackages.stable;
nvidiaPersistenced = true;
nvidiaSettings = false;
};
# Cap both 3090s at 275 W (default 350 W); reapplied on every boot. 275 vs 250: +4.6% prompt tok/s, see benchmarks/standard
systemd.services.nvidia-power-limit = {
description = "Set NVIDIA GPU power limit";
after = [ "nvidia-persistenced.service" ];
requires = [ "nvidia-persistenced.service" ];
wantedBy = [ "multi-user.target" ];
serviceConfig = {
Type = "oneshot";
RemainAfterExit = true;
ExecStart = "${config.hardware.nvidia.package.bin}/bin/nvidia-smi -pl 275";
};
};
# lovey's fan daemon (lovey-fans) can't see passed-through GPUs; send it the hottest one every 2 s
systemd.services.gpu-temp-push = {
description = "Push hottest GPU temp to lovey fan control";
after = [ "nvidia-persistenced.service" "network-online.target" ];
wants = [ "network-online.target" ];
requires = [ "nvidia-persistenced.service" ];
wantedBy = [ "multi-user.target" ];
path = [ config.hardware.nvidia.package.bin pkgs.coreutils ];
script = ''
while true; do
t=$(nvidia-smi --query-gpu=temperature.gpu --format=csv,noheader,nounits | sort -n | tail -1)
echo "$t" > /dev/udp/192.168.1.53/7777 || true
sleep 2
done
'';
serviceConfig.Restart = "always";
};
hardware.nvidia-container-toolkit.enable = true; # GPUs inside podman containers (--device nvidia.com/gpu=all)
virtualisation.podman.enable = true;
# Model weights shared from the host (lovey:/mnt/models); don't block boot if it's missing
fileSystems."/models".options = [ "nofail" ];
# Pinned/locked memory for CUDA, NCCL and vLLM
security.pam.loginLimits = [
{ domain = "*"; type = "-"; item = "memlock"; value = "unlimited"; }
];
systemd.settings.Manager.DefaultLimitMEMLOCK = "infinity";
# Prebuilt Python wheels (pip/uv vLLM, torch) need a normal dynamic loader
programs.nix-ld.enable = true;
# libcuda/libnvidia-ml for pip/uv wheels (torch, vLLM); on NixOS they live in /run/opengl-driver/lib
programs.nix-ld.libraries = [ config.hardware.nvidia.package pkgs.stdenv.cc.cc.lib pkgs.zlib ];
services.qemuGuest.enable = true;
services.openssh = {
enable = true;
settings.PasswordAuthentication = false;
settings.PermitRootLogin = "prohibit-password";
};
users.users.jason = {
isNormalUser = true;
extraGroups = [ "wheel" "video" ];
openssh.authorizedKeys.keys = [
"ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAINZMA1RumOjm198i2Jdaa1lw7vTWnnZKRuYwYhLp6YKx jason@cosmic"
];
};
# Strata inference engine: source checkout + venv in /var/lib/strata, model data on /models/strata
users.groups.strata = { };
users.users.strata = {
isSystemUser = true;
group = "strata";
extraGroups = [ "video" ];
home = "/var/lib/strata";
createHome = true;
shell = pkgs.bashInteractive; # setup runs as this user
};
nix.settings.trusted-users = [ "root" "jason" ];
systemd.tmpfiles.rules = [ "d /models/strata 0750 strata strata -" ];
# The API server (setup's run-iq3_s.sh) with offline mode and no downloads. The API key and engine args live in
# strata-iq3_s.json, which setup wrote; host 0.0.0.0, port 8080.
systemd.services.strata = {
description = "Strata inference server (Qwen3.8-Flash-Next IQ3_S)";
after = [ "network-online.target" "nvidia-persistenced.service" "nvidia-power-limit.service" ];
wants = [ "network-online.target" ];
requires = [ "nvidia-persistenced.service" ];
unitConfig.RequiresMountsFor = [ "/models" ];
wantedBy = [ "multi-user.target" ];
environment = {
HOME = "/var/lib/strata";
# The engine's RUNPATH points libcuda at the CUDA toolkit's stub; the real driver must come first.
LD_LIBRARY_PATH = "/run/opengl-driver/lib";
SSL_CERT_FILE = "/etc/ssl/certs/ca-certificates.crt";
STRATA_NO_ACQUIRE = "1";
};
serviceConfig = {
User = "strata";
Group = "strata";
WorkingDirectory = "/var/lib/strata/Strata";
ExecStart = "/var/lib/strata/Strata/.venv/bin/python /var/lib/strata/Strata/serve/server.py --engine strata --config /var/lib/strata/Strata/strata-iq3_s.json --port 8080 --offline";
Restart = "on-failure";
RestartSec = 10;
LimitMEMLOCK = "infinity"; # the expert arena and KV are pinned host memory
# Hardening: image paths can name local files, so the server sees only its own state and the models
NoNewPrivileges = true;
ProtectSystem = "strict";
ProtectHome = true;
ReadWritePaths = [ "/var/lib/strata" "/models/strata" ];
PrivateTmp = true;
ProtectKernelTunables = true;
ProtectKernelModules = true;
ProtectControlGroups = true;
RestrictSUIDSGID = true;
LockPersonality = true;
};
};
users.users.root.openssh.authorizedKeys.keys = config.users.users.jason.openssh.authorizedKeys.keys;
security.sudo.wheelNeedsPassword = false; # key-only login; set a password with `passwd` for console use
environment.systemPackages = with pkgs; [
git vim htop btop nvtopPackages.nvidia pciutils tmux uv python3 wget curl
cudaPackages.cuda_nvcc # nvcc for building custom kernels; runtime libs come from the wheels
];
system.stateVersion = "26.05";
}