# HashiCorp Nomad: container orchestration without Kubernetes

> HashiCorp Nomad orchestrates containers and processes with one binary and no complex Kubernetes control plane. Setup, job file, and a server cluster.

HashiCorp Nomad is a container and general-process orchestrator that teams pick instead of Kubernetes when the cluster is small and the complexity of a K8s control plane isn't worth it. One binary, one config file, declarative job files in HCL. Let's cover the install, the structure of a job file, and a typical cluster built from several VDS instances.

## What Nomad is and how it differs from Kubernetes

Nomad solves the same problem as Kubernetes: place containers on suitable servers, watch that they're alive, and restart them on failure. But Nomad doesn't drag along a separate network layer, a built-in DNS, and dozens of CRDs — it's a single static Go binary that runs both as a cluster server and as an agent on a worker machine.

The key difference for VDS infrastructure: Nomad schedules not only Docker containers but also plain binaries, Java applications, and virtual machines equally well through task drivers. For service discovery and networking, Nomad relies on a separate product — [Consul](https://www.zevshost.net/en/kb/consul-service-discovery/) — rather than carrying that inside itself.

## Installing Nomad on a single server

On Ubuntu, Nomad installs from the official HashiCorp repository with one sequence of commands:

```
curl -fsSL 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 apt install nomad
nomad agent -dev -bind=0.0.0.0 -log-level=INFO
```

The `-dev` mode runs Nomad as a single node acting as both server and client at once — enough for a test. The web UI comes up on port 4646, showing the cluster's nodes and running jobs.

## The job file: describing how to run a container

The unit of work in Nomad is a job, described in HCL. A minimal example for running nginx in a container:

```
job "web" {
  datacenters = ["dc1"]
  type        = "service"

  group "web" {
    count = 2

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

    task "nginx" {
      driver = "docker"

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

      resources {
        cpu    = 200
        memory = 256
      }
    }
  }
}
```

The command `nomad job run web.nomad.hcl` starts the job, and `count = 2` means two instances of the task, placed by Nomad on the cluster's available servers automatically.

## A multi-server cluster: server and client roles

In production, Nomad separates roles: `server` nodes hold the cluster state and make scheduling decisions, `client` nodes actually run the tasks. The recommended server count is 3 or 5 for Raft quorum, and clients — as many as the load requires.

| Criterion | Nomad | Kubernetes |
| --- | --- | --- |
| Installation | One binary, a config a few lines long | kubeadm, a control plane of several components |
| Task types | Docker, exec, Java, QEMU virtual machines | Mostly containers |
| Service discovery | Through separate Consul | Built-in DNS and kube-proxy |
| Entry threshold | Low, one YAML-like job file | High, dozens of concepts and API objects |

## Integrating with Consul for service discovery

Nomad by itself doesn't know how one task finds another's address. Consul closes that gap: every Nomad task registers in Consul as a service with a health check, and other tasks find it by a DNS name like `web.service.consul`. The Nomad plus Consul combination covers both scheduling and service discovery without a single line of Kubernetes YAML.

## Updating jobs with zero downtime

A rolling update strategy is described right in the job file with an `update` block: Nomad brings up new task instances gradually, checks their health, and only then shuts down the old ones.

- `max_parallel = 1` — update one instance at a time
- `health_check = "checks"` — consider an instance healthy based on health-check results, not just on the fact that it started
- `auto_revert = true` — automatically roll back the job if the new version fails its health check

## When Nomad is not the right fit

If a team already uses the Kubernetes ecosystem — Helm charts, operators, ready-made CRDs — switching to Nomad means rewriting all of that from scratch, and the payoff doesn't justify the cost. For one or two servers with no need for quorum or complex scheduling, it's simpler to use [Docker Swarm](https://www.zevshost.net/en/kb/docker-swarm-nastroika/) or a lightweight cluster based on [k3s](https://www.zevshost.net/en/kb/k3s-kubernetes-vps/) if the Kubernetes ecosystem is still needed.

## Summary: a short checklist

Nomad is worth taking when you need orchestration of mixed workloads — not just Docker — with a minimal entry threshold and without the full weight of Kubernetes.

- One binary instead of dozens of control-plane components
- 3 or 5 servers for Raft quorum in production
- Consul for service discovery when tasks need to find each other
- An `update` block in the job file for zero-downtime updates

---

Source: https://www.zevshost.net/en/kb/nomad-orkestraciya/
