{ self, config, lib, pkgs, ... }: let inherit (self.inputs.kartei) hosts; # consul server peers; stockholm policy (krebs/3modules/kartei/overlay.nix), # not kartei data consulHosts = [ "aergia" "blue" "coaxmetal" "daedalus" "green" "icarus" "ignavia" "littleT" "massulus" "neoprism" "orange" "radio" "shodan" "skynet" "styx" "ubik" "yellow" ]; in { services.consul = { enable = true; # dropPrivileges = false; webUi = true; # interface.bind = "retiolum"; extraConfig = { bind_addr = hosts.${config.networking.hostName}.nets.retiolum.ip4.addr; bootstrap_expect = 3; server = true; # retry_join = config.services.consul.extraConfig.start_join; retry_join = map (name: lib.head hosts.${name}.nets.retiolum.aliases) consulHosts; rejoin_after_leave = true; # try to fix random lock loss on leader reelection retry_interval = "3s"; # Autopilot configuration for better automatic failure handling autopilot = { # Automatically clean up dead servers cleanup_dead_servers = true; # How long a server must be stable before promoting server_stabilization_time = "10s"; # Allow cluster to operate with 2 of 3 nodes (majority quorum) min_quorum = 2; }; # Performance tuning - slightly relaxed for network tolerance performance = { # 2 = default, gives more time for slow/unstable networks # 1 was too aggressive and caused leader flapping raft_multiplier = 2; }; # Reconnect timeout for failed nodes (default 72h, reduce for faster cleanup) reconnect_timeout = "24h"; # Leave on terminate for cleaner shutdowns leave_on_terminate = true; }; }; # Add systemd override for easier recovery systemd.services.consul = { # Add pre-start script to clean up potential issues preStart = '' # Ensure proper permissions chown -R consul:consul /var/lib/consul ''; # Add recovery commands to service serviceConfig = { # Allow consul to restart on failure Restart = "on-failure"; RestartSec = "5s"; StartLimitBurst = 3; StartLimitInterval = "60s"; }; }; # Add recovery helper script environment.systemPackages = [ (pkgs.writeScriptBin "consul-recover" '' #!${pkgs.stdenv.shell} set -e echo "Consul Recovery Tool" echo "===================" echo "" case "''${1:-}" in force-leave) if [ -z "''$2" ]; then echo "Usage: consul-recover force-leave " exit 1 fi echo "Force removing node ''$2 from cluster..." consul force-leave "''$2" ;; reset-cluster) echo "WARNING: This will completely reset the local consul cluster data!" echo "Only use this if the cluster is completely broken." read -p "Are you sure? (yes/no): " confirm if [ "''$confirm" = "yes" ]; then echo "Stopping consul..." systemctl stop consul echo "Clearing consul data..." rm -rf /var/lib/consul/* echo "Starting consul..." systemctl start consul echo "Reset complete. This node will rejoin or form a new cluster." else echo "Cancelled." fi ;; bootstrap-single) echo "WARNING: This will force this node to bootstrap as a single-node cluster!" echo "Only use this in emergency recovery scenarios." read -p "Are you sure? (yes/no): " confirm if [ "''$confirm" = "yes" ]; then echo "Stopping consul..." systemctl stop consul echo "Clearing consul data..." rm -rf /var/lib/consul/* echo "Creating temporary bootstrap configuration..." cat /etc/consul.json | ${pkgs.jq}/bin/jq '.bootstrap_expect = 1' > /tmp/consul-bootstrap.json echo "Starting consul in bootstrap mode..." consul agent -server -data-dir=/var/lib/consul -config-file=/tmp/consul-bootstrap.json -config-file=/etc/consul-addrs.json -config-dir=/etc/consul.d & CONSUL_PID=$! sleep 10 echo "Checking cluster status..." if consul operator raft list-peers; then echo "Bootstrap successful! Restarting with normal configuration..." kill $CONSUL_PID systemctl start consul else echo "Bootstrap failed!" kill $CONSUL_PID 2>/dev/null || true exit 1 fi else echo "Cancelled." fi ;; status) echo "Consul Cluster Status:" echo "=====================" echo "" echo "Members:" consul members || echo "Failed to get members (no leader?)" echo "" echo "Raft Peers:" consul operator raft list-peers || echo "Failed to get raft peers (no leader?)" echo "" echo "Service Status:" systemctl status consul --no-pager | head -20 ;; *) echo "Usage: consul-recover " echo "" echo "Commands:" echo " status - Show cluster status" echo " force-leave - Force remove a node from the cluster" echo " reset-cluster - Completely reset local consul data" echo " bootstrap-single - Emergency: Bootstrap as single-node cluster" ;; esac '') ]; environment.etc."consul.d/testservice.json".text = builtins.toJSON { service = { name = "testing"; }; }; networking.firewall.interfaces.retiolum = { allowedTCPPorts = [ 8300 8301 8302 8400 8500 8600 ]; allowedUDPPorts = [ 8301 8302 8500 ]; }; }