Created
August 23, 2025 21:56
-
-
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)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| { | |
| 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