Skip to content

Load Balancing and Health

A service with one server is a direction. A service with several is a small promise: if these are interchangeable, spread the work, and stop sending visitors to the one that fell over. Both halves of that promise were exercised in the lab (transcript t05) with the fern service running on two ports of the same machine — the geography was trivial, the mechanism identical to servers on different continents.

Several servers, one service

  services:
    fern:
      loadBalancer:
        servers:
          - url: "http://127.0.0.1:9001"
          - url: "http://127.0.0.1:9003"

Nothing else changes — not the router, not the name, not the visitor's experience, except that four identical requests answered fern, fern-two, fern, fern-two: the default strategy is round robin, each healthy server in turn. The backends must genuinely be interchangeable for this to be safe. Two copies of a stateless service are; two copies of something that keeps sessions, uploads or a database in local files are two different services wearing one name, and no proxy setting fixes that — it is the commonest load-balancing mistake and it is an architecture mistake, not a Traefik mistake.

Health checks: the active kind

Traefik notices a dead server on its own eventually — a refused connection fails a real request, at a real visitor's expense. An active health check has Traefik ask, on a schedule, so the finding happens before a visitor does:

        healthCheck:
          path: /healthz
          interval: 5s
          timeout: 3s

The backend must answer that path with a success status; the lab's backends answer /healthz with 200 (and the transcript shows the whole play): a server was killed, one interval passed, and every subsequent request went to the survivor. The server was started again, and within an interval it was back in rotation. No configuration changed at any point — the list of servers is an intention; health is a measurement.

Choose the path deliberately. Checking / proves the server process answers; checking a purpose-built /healthz can also prove its database and disk are reachable, at the risk of marking every server down for a shared database's bad day. Intervals are a trade: the lab's 5 seconds is eager; production checks every 10–30 seconds are typical, and the right number depends on how much traffic a dead server may eat before eviction — stated as a trade-off because it is one, not a default to copy.

Sticky sessions, briefly

If a service truly cannot share state, sticky: true on the load balancer pins each visitor to one server with a cookie. Treat it as a splint, not a cure: the pinned server dying still ends those sessions, and the load stops balancing evenly as regulars accumulate. The longer answer is shared state in the service; the honest short one is that Traefik offers the splint knowingly.

What health checks do not do

They do not restart anything, alert anybody, or explain anything. A service can be healthily answering /healthz while returning errors for every real page, if the check path is shallower than the failure. And when all servers fail the check, the service answers 503 — Traefik saying "I know this site, and it is currently not there", which is a different sentence from a routing 404 and worth being able to hear (chapter 15).