Set up Nomad multi-node cluster with TLS encryption and Consul integration

Advanced 45 min Apr 21, 2026 131 views
Ubuntu 24.04 Debian 12 AlmaLinux 9 Rocky Linux 9

Deploy a production-ready HashiCorp Nomad cluster with server and client nodes, TLS encryption, and Consul integration for service discovery. Includes workload deployment and monitoring configuration.

Prerequisites

  • Root or sudo access
  • 3+ servers for production cluster
  • Docker installed on client nodes
  • Basic understanding of containers and networking

What this solves

HashiCorp Nomad provides container orchestration and workload scheduling across multiple nodes. This tutorial sets up a production-ready Nomad cluster with TLS encryption for secure communication and Consul integration for service discovery and cluster coordination.

Step-by-step installation

Update system packages

Start by updating your package manager to ensure you get the latest versions.

sudo apt update && sudo apt upgrade -y
sudo apt install -y curl unzip
sudo dnf update -y
sudo dnf install -y curl unzip

Install HashiCorp GPG key and repository

Add the official HashiCorp repository to get the latest stable versions of Nomad and Consul.

wget -O- https://apt.releases.hashicorp.com/gpg | sudo gpg --dearmor -o /usr/share/keyrings/hashicorp-archive-keyring.gpg
echo "deb [signed-by=/usr/share/keyrings/hashicorp-archive-keyring.gpg] https://apt.releases.hashicorp.com $(lsb_release -cs) main" | sudo tee /etc/apt/sources.list.d/hashicorp.list
sudo apt update
sudo dnf install -y dnf-plugins-core
sudo dnf config-manager --add-repo https://rpm.releases.hashicorp.com/RHEL/hashicorp.repo

Install Nomad and Consul

Install both Nomad for container orchestration and Consul for service discovery.

sudo apt install -y nomad consul
sudo dnf install -y nomad consul

Create Nomad and Consul directories

Set up the directory structure for configuration files, data, and certificates.

sudo mkdir -p /etc/nomad.d /opt/nomad/data /etc/consul.d /opt/consul/data
sudo mkdir -p /opt/nomad/tls /opt/consul/tls
sudo chown -R nomad:nomad /etc/nomad.d /opt/nomad
sudo chown -R consul:consul /etc/consul.d /opt/consul

Generate TLS certificates for Nomad

Create a Certificate Authority and generate certificates for secure cluster communication.

cd /opt/nomad/tls
sudo nomad tls ca create
sudo nomad tls cert create -server -region global -dc dc1
sudo nomad tls cert create -client -region global -dc dc1
sudo chown -R nomad:nomad /opt/nomad/tls
sudo chmod 600 /opt/nomad/tls/*.pem

Generate Consul encryption key

Create a gossip encryption key for secure Consul cluster communication.

CONSUL_ENCRYPT_KEY=$(consul keygen)
echo "Consul encryption key: $CONSUL_ENCRYPT_KEY"
Note: Save this encryption key. You'll need it for all Consul nodes in the cluster.

Configure Consul cluster

Configure Consul server (on server nodes)

Create the Consul server configuration for cluster coordination and service discovery.

datacenter = "dc1"
data_dir = "/opt/consul/data"
log_level = "INFO"
server = true
bootstrap_expect = 3
ui_config {
  enabled = true
}
bind_addr = "{{ GetInterfaceIP \"eth0\" }}"
client_addr = "0.0.0.0"
retry_join = ["203.0.113.10", "203.0.113.11", "203.0.113.12"]
encrypt = "REPLACE_WITH_CONSUL_ENCRYPT_KEY"
ca_file = "/opt/consul/tls/ca-cert.pem"
cert_file = "/opt/consul/tls/server.pem"
key_file = "/opt/consul/tls/server-key.pem"
verify_incoming = true
verify_outgoing = true
verify_server_hostname = true
connect {
  enabled = true
}

Configure Consul client (on client nodes)

Create the Consul client configuration for nodes that will run Nomad clients.

datacenter = "dc1"
data_dir = "/opt/consul/data"
log_level = "INFO"
server = false
bind_addr = "{{ GetInterfaceIP \"eth0\" }}"
retry_join = ["203.0.113.10", "203.0.113.11", "203.0.113.12"]
encrypt = "REPLACE_WITH_CONSUL_ENCRYPT_KEY"
ca_file = "/opt/consul/tls/ca-cert.pem"
cert_file = "/opt/consul/tls/client.pem"
key_file = "/opt/consul/tls/client-key.pem"
verify_incoming = true
verify_outgoing = true
connect {
  enabled = true
}

Generate Consul TLS certificates

Create TLS certificates for secure Consul communication.

cd /opt/consul/tls
sudo consul tls ca create
sudo consul tls cert create -server -dc dc1
sudo consul tls cert create -client -dc dc1
sudo chown -R consul:consul /opt/consul/tls
sudo chmod 600 /opt/consul/tls/*.pem

Configure Nomad cluster

Configure Nomad server (on server nodes)

Create the Nomad server configuration with TLS encryption and Consul integration.

datacenter = "dc1"
data_dir = "/opt/nomad/data"
log_level = "INFO"
bind_addr = "0.0.0.0"
server {
  enabled = true
  bootstrap_expect = 3
  server_join {
    retry_join = ["203.0.113.10:4648", "203.0.113.11:4648", "203.0.113.12:4648"]
  }
}
client {
  enabled = false
}
consul {
  address = "127.0.0.1:8500"
  ssl = true
  ca_file = "/opt/consul/tls/ca-cert.pem"
  cert_file = "/opt/consul/tls/client.pem"
  key_file = "/opt/consul/tls/client-key.pem"
  server_service_name = "nomad"
  client_service_name = "nomad-client"
  auto_advertise = true
  server_auto_join = true
  client_auto_join = true
}
tls {
  http = true
  rpc = true
  ca_file = "/opt/nomad/tls/nomad-agent-ca.pem"
  cert_file = "/opt/nomad/tls/global-server-nomad.pem"
  key_file = "/opt/nomad/tls/global-server-nomad-key.pem"
  verify_server_hostname = true
  verify_https_client = true
}
ui_config {
  enabled = true
}
telemetry {
  collection_interval = "1s"
  disable_hostname = true
  prometheus_metrics = true
  publish_allocation_metrics = true
  publish_node_metrics = true
}

Configure Nomad client (on client nodes)

Create the Nomad client configuration for nodes that will run workloads.

datacenter = "dc1"
data_dir = "/opt/nomad/data"
log_level = "INFO"
bind_addr = "0.0.0.0"
server {
  enabled = false
}
client {
  enabled = true
  servers = ["203.0.113.10:4647", "203.0.113.11:4647", "203.0.113.12:4647"]
  node_class = "worker"
  options {
    "driver.raw_exec.enable" = "1"
    "driver.docker.enable" = "1"
  }
}
consul {
  address = "127.0.0.1:8500"
  ssl = true
  ca_file = "/opt/consul/tls/ca-cert.pem"
  cert_file = "/opt/consul/tls/client.pem"
  key_file = "/opt/consul/tls/client-key.pem"
  auto_advertise = true
  client_auto_join = true
}
tls {
  http = true
  rpc = true
  ca_file = "/opt/nomad/tls/nomad-agent-ca.pem"
  cert_file = "/opt/nomad/tls/global-client-nomad.pem"
  key_file = "/opt/nomad/tls/global-client-nomad-key.pem"
  verify_server_hostname = true
  verify_https_client = true
}
telemetry {
  collection_interval = "1s"
  disable_hostname = true
  prometheus_metrics = true
  publish_allocation_metrics = true
  publish_node_metrics = true
}

Install Docker on client nodes

Install Docker to run containerized workloads on Nomad clients.

curl -fsSL https://get.docker.com -o get-docker.sh
sudo sh get-docker.sh
sudo usermod -aG docker nomad
sudo systemctl enable --now docker
sudo dnf config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo
sudo dnf install -y docker-ce docker-ce-cli containerd.io
sudo usermod -aG docker nomad
sudo systemctl enable --now docker

Configure systemd services

Create systemd service files for Consul and Nomad with proper dependencies.

[Unit]
Description=Consul
Documentation=https://www.consul.io/
Requires=network-online.target
After=network-online.target
ConditionFileNotEmpty=/etc/consul.d/consul.hcl

[Service]
Type=notify
User=consul
Group=consul
ExecStart=/usr/bin/consul agent -config-dir=/etc/consul.d/
ExecReload=/bin/kill -HUP $MAINPID
KillMode=process
Restart=on-failure
LimitNOFILE=65536

[Install]
WantedBy=multi-user.target

Create Nomad systemd service

Configure the Nomad systemd service with proper startup order.

[Unit]
Description=Nomad
Documentation=https://www.nomadproject.io/docs/
Requires=network-online.target
After=network-online.target consul.service
Wants=consul.service
ConditionFileNotEmpty=/etc/nomad.d/nomad.hcl

[Service]
Type=notify
User=nomad
Group=nomad
ExecStart=/usr/bin/nomad agent -config=/etc/nomad.d/nomad.hcl
ExecReload=/bin/kill -HUP $MAINPID
KillMode=process
Restart=on-failure
LimitNOFILE=65536
KillSignal=SIGINT

[Install]
WantedBy=multi-user.target

Start the services

Enable and start Consul first, then Nomad to ensure proper cluster formation.

sudo systemctl daemon-reload
sudo systemctl enable --now consul
sudo systemctl status consul

sleep 10

sudo systemctl enable --now nomad
sudo systemctl status nomad

Configure firewall rules

Open required ports

Configure firewall rules for Consul and Nomad cluster communication.

sudo ufw allow 8300/tcp comment 'Consul server RPC'
sudo ufw allow 8301/tcp comment 'Consul serf LAN'
sudo ufw allow 8301/udp comment 'Consul serf LAN'
sudo ufw allow 8302/tcp comment 'Consul serf WAN'
sudo ufw allow 8302/udp comment 'Consul serf WAN'
sudo ufw allow 8500/tcp comment 'Consul HTTP API'
sudo ufw allow 8600/tcp comment 'Consul DNS'
sudo ufw allow 8600/udp comment 'Consul DNS'
sudo ufw allow 4646/tcp comment 'Nomad HTTP API'
sudo ufw allow 4647/tcp comment 'Nomad RPC'
sudo ufw allow 4648/tcp comment 'Nomad serf'
sudo ufw allow 20000:32000/tcp comment 'Nomad dynamic ports'
sudo ufw --force enable
sudo firewall-cmd --permanent --add-port=8300/tcp --comment='Consul server RPC'
sudo firewall-cmd --permanent --add-port=8301/tcp --comment='Consul serf LAN'
sudo firewall-cmd --permanent --add-port=8301/udp --comment='Consul serf LAN'
sudo firewall-cmd --permanent --add-port=8302/tcp --comment='Consul serf WAN'
sudo firewall-cmd --permanent --add-port=8302/udp --comment='Consul serf WAN'
sudo firewall-cmd --permanent --add-port=8500/tcp --comment='Consul HTTP API'
sudo firewall-cmd --permanent --add-port=8600/tcp --comment='Consul DNS'
sudo firewall-cmd --permanent --add-port=8600/udp --comment='Consul DNS'
sudo firewall-cmd --permanent --add-port=4646/tcp --comment='Nomad HTTP API'
sudo firewall-cmd --permanent --add-port=4647/tcp --comment='Nomad RPC'
sudo firewall-cmd --permanent --add-port=4648/tcp --comment='Nomad serf'
sudo firewall-cmd --permanent --add-port=20000-32000/tcp --comment='Nomad dynamic ports'
sudo firewall-cmd --reload

Deploy and monitor workloads

Deploy a sample application

Create a simple Nomad job to test cluster functionality.

job "nginx-example" {
  datacenters = ["dc1"]
  type = "service"

  group "web" {
    count = 3

    network {
      port "http" {
        static = 8080
        to     = 80
      }
    }

    service {
      name = "nginx-web"
      port = "http"
      tags = ["web", "nginx"]

      check {
        type     = "http"
        path     = "/"
        interval = "10s"
        timeout  = "3s"
      }
    }

    task "nginx" {
      driver = "docker"

      config {
        image = "nginx:alpine"
        ports = ["http"]
      }

      resources {
        cpu    = 100
        memory = 128
      }
    }
  }
}

Submit the job

Deploy the application using the Nomad CLI with TLS certificates.

export NOMAD_ADDR=https://203.0.113.10:4646
export NOMAD_CACERT=/opt/nomad/tls/nomad-agent-ca.pem
export NOMAD_CLIENT_CERT=/opt/nomad/tls/global-client-nomad.pem
export NOMAD_CLIENT_KEY=/opt/nomad/tls/global-client-nomad-key.pem

nomad job run /tmp/nginx-example.nomad
nomad job status nginx-example

Configure service monitoring

Set up Prometheus configuration to scrape Nomad and Consul metrics. You can integrate this with Prometheus and Grafana monitoring for comprehensive observability.

global:
  scrape_interval: 15s

scrape_configs:
  - job_name: 'nomad'
    consul_sd_configs:
      - server: '203.0.113.10:8500'
        scheme: https
        tls_config:
          ca_file: /opt/consul/tls/ca-cert.pem
          cert_file: /opt/consul/tls/client.pem
          key_file: /opt/consul/tls/client-key.pem
        services: ['nomad']
    relabel_configs:
      - source_labels: [__meta_consul_service]
        target_label: job

  - job_name: 'consul'
    consul_sd_configs:
      - server: '203.0.113.10:8500'
        scheme: https
        tls_config:
          ca_file: /opt/consul/tls/ca-cert.pem
          cert_file: /opt/consul/tls/client.pem
          key_file: /opt/consul/tls/client-key.pem
        services: ['consul']
    metrics_path: /v1/agent/metrics
    params:
      format: ['prometheus']

Verify your setup

# Check cluster status
nomad server members
nomad node status
consul members

Verify TLS is working

curl -k https://203.0.113.10:4646/v1/status/leader curl -k https://203.0.113.10:8500/v1/status/leader

Check job status

nomad job status nomad alloc status

Test service discovery

consul catalog services consul catalog nodes

Access the web interfaces at https://203.0.113.10:4646 for Nomad and https://203.0.113.10:8500 for Consul.

Common issues

SymptomCauseFix
Nodes not joining clusterFirewall blocking portsCheck firewall rules and ensure all required ports are open
TLS certificate errorsCertificate paths wrongVerify certificate files exist and have correct permissions (600)
Docker jobs failingDocker not accessibleAdd nomad user to docker group: sudo usermod -aG docker nomad
Consul health checks failingService registration issuesCheck Consul logs: journalctl -u consul -f
High memory usageDefault resource limitsTune GC settings in Nomad config: gc_interval = "1m"
Jobs not schedulingNo eligible nodesCheck node eligibility: nomad node status -verbose

Next steps

Running this in production?

Want this handled for you? Running this at scale adds a second layer of work: capacity planning, failover drills, cost control, and on-call. Our managed platform covers monitoring, backups and 24/7 response by default.

Automated install script

Run this to automate the entire setup

Need help?

Don't want to manage this yourself?

We handle managed devops services for businesses that depend on uptime. From initial setup to ongoing operations.