TerraformPilot

Terraform

Terraform with HashiCorp Nomad - Deploy Workloads

Use Terraform with HashiCorp Nomad to deploy and manage workloads. Nomad provider, job specifications, namespaces, ACL policies, and cluster provisioning.

LLuca Berton1 min read

Quick Answer

#
provider "nomad" {
  address = "http://nomad.example.com:4646"
}
 
resource "nomad_job" "web" {
  jobspec = file("${path.module}/jobs/web.nomad.hcl")
}

Nomad Provider Configuration

#
provider "nomad" {
  address   = var.nomad_address
  secret_id = var.nomad_token  # ACL token
  region    = "global"
 
  # TLS
  ca_file   = var.nomad_ca_cert
  cert_file = var.nomad_client_cert
  key_file  = var.nomad_client_key
}

Deploy a Job

#

Inline Job Spec

#
resource "nomad_job" "web" {
  jobspec = <<-EOT
    job "web" {
      datacenters = ["dc1"]
      type        = "service"
 
      group "web" {
        count = 3
 
        network {
          port "http" { to = 8080 }
        }
 
        service {
          name = "web"
          port = "http"
          provider = "consul"
 
          check {
            type     = "http"
            path     = "/health"
            interval = "10s"
            timeout  = "2s"
          }
        }
 
        task "app" {
          driver = "docker"
 
          config {
            image = "myapp/web:${var.app_version}"
            ports = ["http"]
          }
 
          resources {
            cpu    = 500
            memory = 256
          }
 
          env {
            ENVIRONMENT = "${var.environment}"
          }
 
          template {
            data = <<-TPL
              {{ with secret "secret/data/myapp" }}
              DB_PASSWORD={{ .Data.data.db_password }}
              {{ end }}
            TPL
            destination = "secrets/env"
            env         = true
          }
        }
      }
    }
  EOT
}

From File with Variables

#
resource "nomad_job" "api" {
  jobspec = templatefile("${path.module}/jobs/api.nomad.hcl.tpl", {
    image       = "${var.registry}/api:${var.app_version}"
    environment = var.environment
    replicas    = var.environment == "production" ? 3 : 1
    cpu         = var.environment == "production" ? 1000 : 500
    memory      = var.environment == "production" ? 512 : 256
  })
 
  purge_on_destroy = true
}

Namespaces

#
resource "nomad_namespace" "production" {
  name        = "production"
  description = "Production workloads"
 
  quota = nomad_quota_specification.production.name
 
  capabilities {
    enabled_task_drivers  = ["docker", "exec"]
    disabled_task_drivers = ["raw_exec"]
  }
 
  meta = {
    environment = "production"
    owner       = "platform-team"
  }
}
 
resource "nomad_quota_specification" "production" {
  name        = "production-quota"
  description = "Production resource limits"
 
  limits {
    region = "global"
 
    region_limit {
      cpu       = 10000  # MHz
      memory_mb = 8192
    }
  }
}

ACL Policies

#
resource "nomad_acl_policy" "developer" {
  name = "developer"
 
  rules_hcl = <<-HCL
    namespace "development" {
      policy = "write"
      capabilities = ["submit-job", "read-logs", "alloc-exec"]
    }
 
    namespace "production" {
      policy = "read"
    }
 
    node {
      policy = "read"
    }
  HCL
}
 
resource "nomad_acl_token" "developer" {
  name = "developer-token"
  type = "client"
 
  policies = [nomad_acl_policy.developer.name]
}

Variables (Nomad Variables)

#
resource "nomad_variable" "db_config" {
  path      = "app/database"
  namespace = "production"
 
  items = {
    host     = aws_db_instance.main.address
    port     = "5432"
    database = "myapp"
    username = "app"
    password = random_password.db.result
  }
}

Provision Nomad Cluster on AWS

#
resource "aws_instance" "nomad_server" {
  count         = 3
  ami           = data.aws_ami.nomad.id
  instance_type = "t3.medium"
  subnet_id     = aws_subnet.private[count.index].id
 
  user_data = templatefile("${path.module}/scripts/nomad-server.sh", {
    server_count = 3
    datacenter   = "dc1"
    region       = "global"
  })
 
  tags = {
    Name = "nomad-server-${count.index}"
    Role = "nomad-server"
  }
}
 
resource "aws_instance" "nomad_client" {
  count         = var.client_count
  ami           = data.aws_ami.nomad.id
  instance_type = "t3.large"
  subnet_id     = aws_subnet.private[count.index % length(aws_subnet.private)].id
 
  user_data = templatefile("${path.module}/scripts/nomad-client.sh", {
    server_ips = aws_instance.nomad_server[*].private_ip
    datacenter = "dc1"
  })
 
  tags = {
    Name = "nomad-client-${count.index}"
    Role = "nomad-client"
  }
}

Nomad vs Kubernetes

#
FeatureNomadKubernetes
ComplexitySimplerMore complex
Non-container✅ exec, java, raw_execPrimarily containers
SchedulingMulti-region built-inFederation add-on
Service meshConsul ConnectIstio/Linkerd
Learning curveLowerHigher
EcosystemSmallerMassive
#

Conclusion

#

Use the Nomad provider to manage jobs, namespaces, ACL policies, and variables alongside your infrastructure. Template job specs with templatefile() for environment-specific deployments. Nomad is simpler than Kubernetes and supports non-container workloads — ideal when you don't need the full K8s ecosystem.

#Terraform#Nomad#HashiCorp#DevOps#Infrastructure as Code

Share this article