Why model Nomad jobs in Python?

Nomad accepts JSON jobs through its API and CLI, while operators commonly author jobs in HCL. nomad-pydantic uses the JSON representation as its boundary because it is the native structure exchanged with Nomad and maps directly to nested Pydantic models.

Jobs form a typed hierarchy

A Nomad job contains task groups, and each task group contains colocated tasks. The model hierarchy preserves that structure:

Job
└── TaskGroup
    ├── Task
    │   ├── Resources
    │   ├── Service
    │   └── VolumeMount
    ├── NetworkResource
    └── Volume

Validation therefore happens before scheduling. Duplicate task names, invalid periodic job types, and mutually exclusive CPU settings fail when models are created rather than after submission to a cluster.

Python names and Nomad names serve different boundaries

Python callers use snake_case, such as task_groups, memory_mb, and kill_timeout. Rendered jobs use Nomad’s API names, such as TaskGroups, MemoryMB, and KillTimeout.

Nomad’s JSON API encodes durations as nanoseconds. Models accept readable values such as 30s and 1m30s, then render their integer nanosecond equivalents. This keeps YAML and Python configuration readable without changing the wire format.

Configuration and lifecycle are separate concerns

Job describes desired scheduler state. NomadConfiguration associates that job with a local JSON artifact. NomadClient performs lifecycle operations through the Nomad CLI. Separating these layers allows rendering and validation without a cluster and allows lifecycle tests to replace process execution with a fake runner.

nomad job status -json combines the job summary with allocations, the latest deployment, and evaluations. JobStatus preserves that structure. Its running, complete, failed, and stopped properties derive lifecycle state from current-version allocations and task-group summary counts rather than from Nomad’s coarse running/dead job status.

This mirrors the boundary used by supervisor-pydantic and systemd-pydantic: models describe workload configuration, while a client handles runtime state.

airflow-nomad composes these layers into an Airflow lifecycle: Airflow registers the modeled job, monitors JobStatus, handles retriggers, and stops or purges the job according to the DAG configuration.