Skip to content

Instantly share code, notes, and snippets.

@ppenguin
Created August 23, 2025 21:56
Show Gist options
  • Select an option

  • Save ppenguin/5a85f502b62f95dfd9d981484990d34e to your computer and use it in GitHub Desktop.

Select an option

Save ppenguin/5a85f502b62f95dfd9d981484990d34e to your computer and use it in GitHub Desktop.
Quick paste of a single node (i.e. server+client) NixOS `nomad+consul+vault` config (used for a non-redundant homelab deployment)
{
pkgs,
lib,
config,
...
}: let
npkgs = pkgs;
nomadpkg = pkgs.nomad_1_9;
nomadAddr = "192.168.1.10";
nomadSvrAddrs = [nomadAddr];
crtBaseName = "nomad.${config.networking.hostName}";
vcrtBaseName = "vault.${config.networking.hostName}";
in {
environment = {
systemPackages = [nomadpkg];
sessionVariables = {
NOMAD_ADDR = "http://${nomadAddr}:4646";
};
etc = {
# TODO: (?) move certs to sops-nix like other secrets?
"vault/tls/${vcrtBaseName}.crt" = {
source = ../../files/ssl + "/${vcrtBaseName}/${vcrtBaseName}.crt";
mode = "0444";
user = "vault";
group = "vault";
};
"vault/tls/${vcrtBaseName}.key" = {
source = ../../files/ssl + "/${vcrtBaseName}/${vcrtBaseName}.key";
mode = "0400";
user = "vault";
group = "vault";
};
"nomad/tls/${crtBaseName}.crt" = {
source = ../../files/ssl + "/${crtBaseName}/${crtBaseName}.crt";
mode = "0444";
};
"nomad/tls/${crtBaseName}.key" = {
source = ../../files/ssl + "/${crtBaseName}/${crtBaseName}.key";
mode = "0400";
};
"consul/tls/consul-agent-ca.pem" = {
source = ../../files/ssl/consul/consul-agent-ca.pem;
mode = "0444";
user = "consul";
group = "consul";
};
"consul/tls/dc1-server-consul-0.pem" = {
source = ../../files/ssl/consul/dc1-server-consul-0.pem;
mode = "0444";
user = "consul";
group = "consul";
};
"consul/tls/dc1-server-consul-0-key.pem" = {
source = ../../files/ssl/consul/dc1-server-consul-0-key.pem;
mode = "0444";
user = "consul";
group = "consul";
};
};
};
security.pki.certificateFiles = [
"${pkgs.cacert}/etc/ssl/certs/ca-bundle.crt"
../../files/ssl/CA/private-lan-RootCA.crt
];
# see https://www.claudiokuenzler.com/blog/890/first-steps-consul-interpret-communication-errors-between-node
networking.firewall = {
allowedTCPPorts = [
8200
8201 # vault
8500
8503 # consul
# FIXME:
# now we suddenly need to expose nomad services ports?!
# or not, because nginx-local at 8888 still works without this...
# 8989
# 9696
# 9091
];
allowedUDPPorts = [
8503 # consul gRPC-TLS <= oh wait, this is on TCP !!!
8600 # consul DNS
];
allowedTCPPortRanges = [
{
from = 4646;
to = 4648;
} # nomad
{
from = 8300;
to = 8302;
} # nomad
];
allowedUDPPortRanges = [
{
from = 4648;
to = 4648;
} # nomad
{
from = 8300;
to = 8302;
} # consul
];
};
services = {
consul = {
enable = true;
webUi = true;
package = npkgs.consul;
forceAddrFamily = "ipv4";
interface = {
# bind = "wg_nomad";
# advertise = "wg_nomad";
};
extraConfig = {
bind_addr = nomadAddr;
client_addr = lib.concatStringsSep " " ["127.0.0.1" nomadAddr]; # https://developer.hashicorp.com/consul/docs/agent/config/cli-flags#_client
server = true;
bootstrap_expect = 1; # number of servers for this cluster / implies bootstrap => i.e. one server may self-elect. Best turn off if more than one server
connect = {enabled = true;}; # for service mesh, i.e. without this we can't use connect/sidecar in nomad nodes
# TLS settings
verify_incoming = true;
verify_outgoing = true;
verify_server_hostname = true;
ca_file = "/etc/consul/tls/consul-agent-ca.pem";
cert_file = "/etc/consul/tls/dc1-server-consul-0.pem";
key_file = "/etc/consul/tls/dc1-server-consul-0-key.pem";
auto_encrypt = {allow_tls = true;};
};
};
nomad = {
enable = true;
package = nomadpkg;
# NOTE: for nomad server we could do this, but for client we need rootfull containers because nomad rootless is not mature enough.
# So because this is a server+client node we don't drop privileges
dropPrivileges = false;
enableDocker = false;
extraPackages = with pkgs; [cni-plugins consul dmidecode];
extraSettingsPlugins = with pkgs; [nomad-driver-podman];
settings = {
datacenter = "adg25priv";
bind_addr = nomadAddr;
# advertise = "wg-nomad";
telemetry = {
publish_allocation_metrics = true;
publish_node_metrics = true;
prometheus_metrics = true;
};
server = {
enabled = true;
bootstrap_expect = 1; # number of servers for this cluster (?)
raft_multiplier = 5; # more relaxed timing requirements for communication with peers
node_gc_threshold = "1h";
job_gc_threshold = "10m";
deployment_gc_threshold = "10m";
eval_gc_threshold = "10m";
batch_eval_gc_threshold = "1h";
# self doesn't need to join
# server_join = {
# retry_join = [ nomadAddr ];
# retry_max = 3;
# retry_interval = "15s";
# };
};
# NOTE: microserv is special as a server and client
client = {
enabled = true;
node_pool = "priv";
# network_interface = "wg_nomad";
host_network = {
"public" = {
# cidr = "203.0.113.0/24";
interface = config.networking.interfaces.eno1.name;
# reserved_ports = "61022"; # the ports that other services not managed by nomad might occupy
};
};
servers = nomadSvrAddrs;
cni_path = "${pkgs.cni-plugins}/bin"; # !!! important
host_volume = {
"mariadb" = {
path = "/DATA/nomad-vol/mariadb";
read_only = false;
};
"postgres" = {
path = "/DATA/nomad-vol/postgres";
read_only = false;
};
"bunkerweb" = {
path = "/DATA/nomad-vol/bunkerweb";
read_only = false;
};
};
};
vault = {
enabled = true;
address = "https://${config.services.vault.address}";
create_from_role = "nomad-cluster";
# token created with VAULT_TOKEN=$TF_VAR_VAULT_ROOT_TOKEN vault token create -policy nomad_server -period 72h -orphan
# FIXME: update
token = "<CHANGEME_SECRET>";
cert_file = "/etc/nomad/tls/${crtBaseName}.crt"; # nomad runs as a DynamicUser and has no access to this: "${config.services.vault.tlsCertFile}";
key_file = "/etc/nomad/tls/${crtBaseName}.key";
};
};
};
vault = {
enable = true;
package = pkgs.vault-bin; # only -bin includes UI
address = "${nomadAddr}:8200";
storageBackend = "file"; # FIXME: remember to backup /var/lib/vault in production!!! (consul kv is discouraged by HCP)
tlsCertFile = "/etc/vault/tls/${vcrtBaseName}.crt";
tlsKeyFile = "/etc/vault/tls/${vcrtBaseName}.key";
extraConfig = ''
disable_sealwrap = true # enterprise feature
cluster_addr = "https://${nomadAddr}:8201"
api_addr = "https://${nomadAddr}:8200"
ui = true
# disable_mlock = true
'';
};
}; #>services
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment