diff options
| author | Natasha Moongrave <natasha@256phi.eu> | 2026-06-05 18:44:17 +0200 |
|---|---|---|
| committer | Natasha Moongrave <natasha@256phi.eu> | 2026-06-05 18:44:17 +0200 |
| commit | 3ec6dd09b01ddc9a18a70b15f9e47d0bf27d594b (patch) | |
| tree | 596ff4b6d1a6f73682bc207a54ac0b64921d0884 /hosts/herra/ai.nix | |
| parent | ab57e6eaf9cd973c3e07d9d261d73e10cb1e5de1 (diff) | |
| parent | 80971b27fc79b6edc0693c91f938b358c0f3f543 (diff) | |
Merge branch 'main' of cgit:/srv/git/NixOS-config
Diffstat (limited to 'hosts/herra/ai.nix')
| -rw-r--r-- | hosts/herra/ai.nix | 175 |
1 files changed, 175 insertions, 0 deletions
diff --git a/hosts/herra/ai.nix b/hosts/herra/ai.nix new file mode 100644 index 0000000..df93a24 --- /dev/null +++ b/hosts/herra/ai.nix @@ -0,0 +1,175 @@ +{ + pkgs, + lib, + ... +}: let + puid = "1000"; + pgid = "100"; + odysseusRepo = "https://github.com/pewdiepie-archdaemon/odysseus.git"; + odysseusDir = "/var/lib/odysseus/src"; +in { + # ---------------------------- + # Ollama (native) + # ---------------------------- + services.ollama = { + enable = true; + host = "0.0.0.0"; + port = 11434; + }; + + # ---------------------------- + # Docker + # ---------------------------- + virtualisation.docker.enable = true; + virtualisation.oci-containers.backend = "docker"; + + # ---------------------------- + # Persistent storage + # ---------------------------- + systemd.tmpfiles.rules = [ + "d /var/lib/odysseus/data 0755 root root -" + "d /var/lib/odysseus/logs 0755 root root -" + "d /var/lib/odysseus/ssh 0755 root root -" + "d /var/lib/odysseus/huggingface 0755 root root -" + "d /var/lib/odysseus/chromadb 0755 root root -" + "d /var/lib/odysseus/ntfy 0755 root root -" + "d /var/lib/odysseus/searxng 0777 root root -" + "d /var/lib/odysseus/src 0755 root root -" + ]; + + # ---------------------------- + # SearXNG config + # ---------------------------- + environment.etc."odysseus/searxng/settings.yml".text = '' + use_default_settings: true + server: + secret_key: "change-me-openssl-rand-hex-32" + limiter: false + image_proxy: true + search: + safe_search: 0 + formats: + - html + - json + ''; + + # ---------------------------- + # Build Odysseus from source via systemd oneshot + # ---------------------------- + systemd.services.odysseus-build = { + description = "Build Odysseus Docker image from source"; + wantedBy = ["docker-odysseus.service"]; + before = ["docker-odysseus.service"]; + after = ["docker.service" "network-online.target"]; + wants = ["network-online.target"]; + serviceConfig = { + Type = "oneshot"; + RemainAfterExit = true; + }; + path = [pkgs.git pkgs.docker]; + script = '' + if [ ! -d "${odysseusDir}/.git" ]; then + git clone ${odysseusRepo} ${odysseusDir} + else + git -C ${odysseusDir} pull --ff-only + fi + + NEW_HASH=$(git -C ${odysseusDir} rev-parse HEAD) + OLD_HASH=$(cat /var/lib/odysseus/.last-built-commit 2>/dev/null || echo "") + + if [ "$NEW_HASH" != "$OLD_HASH" ] || ! docker image inspect odysseus:local &>/dev/null; then + echo "Building odysseus image at commit $NEW_HASH..." + docker build -t odysseus:local ${odysseusDir} + echo "$NEW_HASH" > /var/lib/odysseus/.last-built-commit + else + echo "Image up to date at $OLD_HASH, skipping build." + fi + ''; + }; + + # ---------------------------- + # Containers + # ---------------------------- + virtualisation.oci-containers.containers = { + odysseus-chromadb = { + image = "chromadb/chroma:latest"; + ports = ["127.0.0.1:8100:8000"]; + volumes = ["/var/lib/odysseus/chromadb:/chroma/chroma"]; + environment.ANONYMIZED_TELEMETRY = "FALSE"; + }; + + odysseus-searxng = { + image = "searxng/searxng:latest"; + ports = ["127.0.0.1:8080:8080"]; + volumes = [ + "/var/lib/odysseus/searxng:/etc/searxng" + "/etc/odysseus/searxng/settings.yml:/etc/searxng/settings.yml:ro" + ]; + environment.SEARXNG_BASE_URL = "http://localhost:8080/"; + }; + + odysseus-ntfy = { + image = "binwiederhier/ntfy"; + cmd = ["serve"]; + ports = ["127.0.0.1:8091:80"]; + volumes = ["/var/lib/odysseus/ntfy:/var/cache/ntfy"]; + environment.NTFY_BASE_URL = "http://localhost:8091"; + }; + + odysseus = { + image = "odysseus:local"; + ports = ["127.0.0.1:7000:7000"]; + volumes = [ + "/var/lib/odysseus/data:/app/data" + "/var/lib/odysseus/logs:/app/logs" + "/var/lib/odysseus/ssh:/app/.ssh" + "/var/lib/odysseus/huggingface:/app/.cache/huggingface" + ]; + environment = { + APP_BIND = "0.0.0.0"; + APP_PORT = "7000"; + AUTH_ENABLED = "true"; + PUID = puid; + PGID = pgid; + SEARXNG_INSTANCE = "http://host.docker.internal:8080"; + CHROMADB_HOST = "host.docker.internal"; + CHROMADB_PORT = "8000"; + OLLAMA_BASE_URL = "http://host.docker.internal:11434/v1"; + }; + extraOptions = [ + "--add-host=host.docker.internal:host-gateway" + "--pull=never" + ]; + dependsOn = ["odysseus-chromadb" "odysseus-searxng" "odysseus-ntfy"]; + }; + }; + + # Restart policy (mkForce overrides oci-containers' default "always") + systemd.services = { + docker-odysseus-chromadb.serviceConfig.Restart = lib.mkForce "on-failure"; + docker-odysseus-searxng.serviceConfig.Restart = lib.mkForce "on-failure"; + docker-odysseus-ntfy.serviceConfig.Restart = lib.mkForce "on-failure"; + docker-odysseus.serviceConfig.Restart = lib.mkForce "on-failure"; + }; + + # ---------------------------- + # Nginx + local DNS + # ---------------------------- + services.nginx = { + enable = true; + virtualHosts."ai.local" = { + locations."/" = { + proxyPass = "http://127.0.0.1:7000"; + proxyWebsockets = true; + extraConfig = '' + proxy_read_timeout 300s; + proxy_connect_timeout 75s; + ''; + }; + }; + }; + + networking.hosts."127.0.0.1" = ["ai.local"]; + networking.firewall.allowedTCPPorts = [80]; + networking.firewall.interfaces.docker0.allowedTCPPorts = [11434]; +} |
