feat(k3s-node): Add k3s-node role and enable on john-endesktop

Add reusable k3s-node role with configurable options for server/agent
modes. Configure john-endesktop as a k3s agent joining the cluster at
10.0.0.222.

Role supports:
- Server or agent role selection
- Configurable server address and token file
- Graceful node shutdown
- Optional firewall port opening
- Cluster initialization for first server

Note: NixOS nodes must be labeled with `k3s-upgrade=disabled` to exclude
them from the system-upgrade-controller, since NixOS manages k3s upgrades
through Nix rather than in-place binary replacement.
This commit is contained in:
2026-01-10 20:08:57 -08:00
parent 565acb1632
commit 346ad3665d
3 changed files with 90 additions and 0 deletions

View File

@@ -104,6 +104,14 @@ with lib;
# User configuration
roles.users.enable = true;
# k3s agent configuration
roles.k3s-node = {
enable = true;
role = "agent";
# serverAddr defaults to https://10.0.0.222:6443
# tokenFile defaults to /etc/k3s/token
};
# Time zone
time.timeZone = "America/Los_Angeles"; # Adjust as needed

View File

@@ -9,6 +9,7 @@ with lib;
./bluetooth
./btrfs
./desktop
./k3s-node
./kodi
./nfs-mounts
./nvidia

View File

@@ -0,0 +1,81 @@
{ lib, config, pkgs, ... }:
with lib;
let
cfg = config.roles.k3s-node;
in
{
options.roles.k3s-node = {
enable = mkEnableOption "Enable k3s 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://10.0.0.222:6443";
description = "URL of k3s server to join (required for agents, used for HA servers)";
};
tokenFile = mkOption {
type = types.path;
default = "/etc/k3s/token";
description = "Path to file containing the cluster join token";
};
clusterInit = mkOption {
type = types.bool;
default = false;
description = "Initialize a new cluster (first server only)";
};
extraFlags = mkOption {
type = types.listOf types.str;
default = [];
description = "Additional flags to pass to k3s";
};
gracefulNodeShutdown = mkOption {
type = types.bool;
default = true;
description = "Enable graceful node shutdown";
};
openFirewall = mkOption {
type = types.bool;
default = true;
description = "Open firewall ports for k3s";
};
};
config = mkIf cfg.enable {
# k3s service configuration
services.k3s = {
enable = true;
role = cfg.role;
tokenFile = cfg.tokenFile;
extraFlags = cfg.extraFlags;
gracefulNodeShutdown.enable = cfg.gracefulNodeShutdown;
serverAddr = if (cfg.role == "agent" || !cfg.clusterInit) then cfg.serverAddr else "";
clusterInit = cfg.role == "server" && cfg.clusterInit;
};
# Firewall rules for k3s
networking.firewall = mkIf cfg.openFirewall {
allowedTCPPorts = [
6443 # k3s API server
10250 # kubelet metrics
] ++ optionals (cfg.role == "server") [
2379 # etcd clients (HA)
2380 # etcd peers (HA)
];
allowedUDPPorts = [
8472 # flannel VXLAN
];
};
};
}