Skip to main content
Hard-coding an API’s IP and port into a config file works only as long as the service never moves. Tidepool’s web frontend initially used a single static address for its API — fine when the API lived on one stable machine, but brittle once services are relocated, restarted, or scaled. A more resilient approach is to stop asking “Where is it?” and instead ask “Who is it?” — register each API copy under a logical name, and have clients look up that name to get the currently alive addresses. As instances start and stop, the registry’s results change automatically and client configs stay unchanged.
A presentation slide titled "Who Is It, Not Where" showing icons for a document, a Docker container (whale), and a magnifying glass with captions "one IP, typed once," "copies that moved," and "ask by name." Below are four server/container illustrations labeled box-1 through box-4.

How Nomad exposes services

In Nomad, service discovery is configured inside the group block of a job file via a service stanza. Each service includes three core properties:
  • name — the logical name that clients will query (e.g., tidepool-api).
  • port — a port label that refers to a port declared in the job’s network block (not a literal number).
  • provider — the registry that manages service entries (Nomad’s built-in registry or Consul).
Always reference the port label (for example, http) defined in the network block so Nomad can assign ports dynamically at runtime. Do not hard-code numeric ports inside the service block.
A presentation slide titled "The service Block" with icons of a document and a plug and the statement "Port is a label, not a number." Below are Docker whale and server icons arranged over four labeled boxes: box-1 to box-4.

Example Nomad job (HCL)

This example shows a minimal job that registers an http-labelled service with Nomad’s registry and attaches an HTTP health check.
If you omit provider, Nomad assumes consul by default. Explicitly set provider = "nomad" to use Nomad’s built-in service registry.

Health checks: who is actually healthy?

Registering a service name is only part of the story — clients need to know which instances are healthy. That’s why the service block supports a nested check stanza. The check must be nested directly under service (not at top-level or inside task). Common check properties: Nomad will call the check endpoint repeatedly for every service copy. This gives visibility into instance health so operators can act.
A presentation slide titled "The check Block, Nested" showing icons (a shield with a check, a folder, and a stopwatch) and the phrase "Inside service, nowhere else." Along the bottom are Docker whale icons above server racks labeled box-1 through box-4 with green check marks.

Nomad built-in provider: visibility vs. filtering

One important caveat with Nomad’s built-in provider: it records and shows check failures, but it does not filter out failing instances from the service listing. That means a copy with failing checks can still appear in client query results.
A slide titled "No Health Filtering" showing a ringing bell and magnifying-glass icon with the text "Inside service, nowhere else." Below are four server boxes labeled box-1 to box-4 with Docker whale icons and green check marks indicating they passed checks.
So what does a failing check accomplish if the unhealthy copy remains listed? It provides visibility and the option to automate recovery:
  • Visibility: operators can see which instances are failing and investigate.
  • Automatic restarts: you can configure a check_restart block to restart tasks after N consecutive failures. Note that check_restart.limit defaults to 0 (disabled).
A presentation slide titled "check_restart Defaults to Off" showing icons (bell, magnifier, stopwatch) and text explaining that a failed check stays in results and causes "Errors for ten minutes." The bottom shows four checked boxes with server and Docker icons labeled box-1 through box-4.
Be careful: check_restart.limit defaults to 0 (off). If you add a check but do not set a restart limit, a permanently broken instance may remain listed and continue to receive traffic until someone intervenes.

Why choose Consul?

Because the built-in provider does not filter unhealthy instances by default, many teams prefer Consul for service discovery. Consul offers additional capabilities:
  • Health filtering by default — Consul returns only instances whose checks are passing.
  • DNS-based discovery — clients that resolve hostnames can use Consul without Nomad-specific logic.
  • Optional service proxying and mTLS — secure, authenticated communication between services.
A presentation slide titled "What Consul Adds" showing icons for a shield (visibility by default) and a gear (restart if you ask). Below are four illustrated boxes with Docker whale and server icons labeled box-1 through box-4.

Which provider should you use?

Remember that Consul is additional infrastructure: you must operate Consul servers, handle upgrades, and plan for its availability. In Tidepool’s case, Maya continued to use Nomad’s built-in registry because the frontend already implements retries: a bad copy costs a retry, not an outage. Regardless of provider, the static two-year-old address was removed and replaced by a dynamic discovery model that scales and survives instance churn. There is one more block in that configuration file that we haven’t touched yet.

Watch Video