Skip to content

Nomad Translator

The translator converts an InfraSpec into Nomad job specifications. Regional deploys use TranslateForRegion: the same app job ID is submitted into each Nomad region with only the processes eligible for that region.

Translate — Service Jobs

Translate(spec, imageTag, env) creates a Nomad service job. Each non-scheduled process in the infraspec becomes a TaskGroup within the job.

Process → TaskGroup Mapping

InfraSpec.Processes
├── web (port: 8080)      → TaskGroup "web"     [service job]
├── worker (no port)      → TaskGroup "worker"   [service job]
├── cleanup (schedule)    → SKIPPED (separate periodic job)
└── resize (function)     → SKIPPED (on-demand batch job)

Scheduled processes and functions are excluded from the main service job — they get their own jobs via TranslatePeriodic and TranslateBatch.

Docker Task Config

Each TaskGroup contains a single Docker task:

hcl
task "web" {
  driver = "docker"
  config {
    image   = "ghcr.io/user/myapp:abc1234"
    command = "/bin/sh"
    args    = ["-c", "./server"]
    ports   = ["web-http"]
  }
}
  • image — the built and pushed Docker image tag
  • command / args — only set if the process defines a command (overrides Docker CMD)
  • ports — label format: {processName}-http

Port Handling

ScenarioPort TypeNomad Config
Process has portDynamicDynamicPorts: [{Label, To}]; supports multiple allocations per node
Process has port + hostPortStaticReserved only for single-allocation infrastructure such as regional Traefik
Process has no portNoneNo network resource

Endpoint traffic reaches a stable Traefik origin. Traefik discovers every healthy allocation through Consul, so application host ports do not need to be predictable.

Consul Service Registration

Processes with a port are registered as Consul services:

hcl
service {
  name      = "myapp-web"
  port      = "web-http"
  provider  = "consul"

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

Health checks are added when the process defines a health block.

Processes with endpoints also receive opt-in Traefik Consul Catalog tags. The tags define the hostname rule, regional identity, and desired traffic weight; services without traefik.enable=true remain private.

Environment Variables

Environment variables are merged from two sources:

  1. spec.Env — static variables from the infraspec
  2. env parameter — secrets and runtime variables injected by the pipeline
  3. process.env — process-specific overrides

Process env takes final precedence for that task.

For a process with nomadVariables, the pipeline env parameter is not merged at all. Instead, Norn derives nomad/jobs/<job-id> and emits one Nomad template per declared key under secrets/<filename>. Template data, source path, mode (0400), restart behavior, and missing-key handling are fixed by the translator; only a safe basename and the task UID/GID are declarative. This transport is intentionally file-only (env = false): values never appear in task environment fields or command arguments.

Resources

FieldDefaultOverride
CPU100 MHzprocess.resources.cpu
Memory128 MBprocess.resources.memory

Volume Mounts

Volumes declared in the infraspec are mapped to Nomad host volumes:

hcl
volume "signal-data" {
  type   = "host"
  source = "signal-data"
}

volume_mount {
  volume      = "signal-data"
  destination = "/var/lib/signal-cli"
  read_only   = false
}

Update Strategy

All service TaskGroups use this update strategy:

SettingValuePurpose
MaxParallel1Roll one allocation at a time
MinHealthyTime30sMust be healthy for 30 seconds before continuing
AutoReverttrueAutomatically revert to last stable version on failure

Restart Policy

SettingValue
Attempts3
Interval5 minutes
Delay15 seconds
Modedelay

TranslatePeriodic — Cron Jobs

TranslatePeriodic(spec, procName, proc, imageTag, env) creates a Nomad periodic batch job for a process with a schedule.

  • Job ID: {appName}-{processName}
  • Periodic config uses the cron spec type
  • process.timezone, process.env.TZ, or app env.TZ sets the Nomad periodic time_zone
  • Same environment, resource, and volume handling as service jobs
  • No health checks or Consul registration
  • No retries: task restarts are disabled (attempts: 0, mode: fail) and failed allocations cannot be rescheduled (attempts: 0, unlimited: false)
yaml
# infraspec
processes:
  cleanup:
    schedule: "0 3 * * *"
    timezone: America/Chicago
    command: ./cleanup

Translates to:

hcl
job "myapp-cleanup" {
  type = "batch"
  periodic {
    cron     = "0 3 * * *"
    time_zone = "America/Chicago"
    enabled  = true
  }
  # ...
}

TranslateBatch — Function Jobs

TranslateBatch(spec, procName, proc, imageTag, env, jobID) creates a one-shot Nomad batch job for function invocations.

  • Job ID: caller-supplied (includes execution ID for uniqueness)
  • No retries: task restarts are disabled (attempts: 0, mode: fail) and failed allocations cannot be rescheduled (attempts: 0, unlimited: false)
  • function.memory overrides resources.memory if set
  • Same environment and volume handling as other job types