{ config, lib, pkgs, ... }: with lib; let cfg = config.services.k3s-cluster; in { options.services.k3s-cluster = { enable = mkEnableOption "k3s cluster node"; role = mkOption { type = types.enum [ "server" "agent" ]; default = "agent"; description = "k3s role: server (control plane) or agent (worker)."; }; serverAddr = mkOption { type = types.str; default = "https://127.0.0.1:6443"; description = "k3s server URL for agents to join."; }; tailscaleIP = mkOption { type = types.nullOr types.str; default = null; description = "This node's Tailscale IP (for --advertise-address)."; }; tokenFile = mkOption { type = types.nullOr types.str; default = "/var/lib/k3s/cluster-token"; description = "File containing the cluster shared secret."; }; withOllama = mkOption { type = types.bool; default = false; description = "Enable Ollama on this node (GPU passthrough)."; }; }; config = mkIf cfg.enable { services.k3s = { enable = true; role = cfg.role; extraFlags = with cfg; concatStringsSep " " ( [ "--flannel-iface=tailscale0" ] ++ optionals (tailscaleIP != null) [ "--advertise-address=${tailscaleIP}" "--node-ip=${tailscaleIP}" ] ++ optionals (role == "server") [ "--cluster-init" "--disable=traefik" "--disable=servicelb" "--kube-controller-manager-arg=bind-address=0.0.0.0" ] ); inherit tokenFile; # Allow pods on control plane (we need the resources) inherit (cfg) serverAddr; }; # Open k3s API port on control plane networking.firewall.allowedTCPPorts = optionals (cfg.role == "server") [ 6443 ]; # ── Ollama (optional, on worker nodes with GPU/NPU) ── services.ollama = mkIf cfg.withOllama { enable = true; acceleration = "opencl"; # Intel N97 iGPU environmentVariables = { OLLAMA_KEEP_ALIVE = "24h"; OLLAMA_HOST = "0.0.0.0:11434"; }; openFirewall = true; }; }; }