skip to content

Walk through the shape of an HAProxy configuration file: what do the global, defaults, frontend and backend sections each configure, and what does a listen section do instead?

level: juniorimportance: must knowfreq 74%

answer

  1. four section kinds, two of them proxies
  2. process settings versus traffic settings
  3. one side binds, the other holds servers
  4. a template that only applies downward
  5. listen = bind and server together

basics

~20 s

HAProxy config is split into global (process-wide settings), defaults (a template inherited by every proxy declared after it), frontend (a bind address that classifies incoming traffic), and backend (the server pool). A listen section fuses a frontend and backend into one block.

solid answer

~40 s

An HAProxy config is a list of sections. `global` configures the process itself — privileges, `daemon`, thread count, process-wide `maxconn`, logging targets, default TLS settings. `defaults` is not a proxy at all; it is a template whose settings are inherited by every `frontend`, `backend` and `listen` declared **after** it, which is why a second `defaults` block silently changes everything below it and nothing above. A `frontend` owns the `bind` line and decides where traffic goes: `acl` definitions, `http-request` rules, `use_backend` and `default_backend`. A `backend` owns the pool — `server` lines, `balance`, health checks, persistence. A `listen` section simply combines both: it can hold `bind` and `server` lines together, which is handy for a simple one-to-one TCP proxy. Validate any of it with `haproxy -c -f /etc/haproxy/haproxy.cfg` before reloading.

go deeper

for a junior

Be able to read a small haproxy.cfg out loud and say which section each line belongs to. The one-liner to have ready: the frontend binds and decides, the backend holds the servers.

for a middle

Explain that defaults is a positional template, not a merge, and that a second defaults block resets it for everything below. Know that global never contains bind or server lines.

for a senior

Show that you validate with haproxy -c before a reload and that you standardise mode and the timeout family in defaults so no proxy can ship without them. Say why you split listen into frontend/backend once routing appears.

for a principal

Own the config layout for a fleet: generated versus hand-edited, named defaults sections for mixed HTTP and TCP proxies, unique proxy naming, and where the syntax check sits in the pipeline that produces the file.

## Why the file is shaped this way HAProxy separates *how the process runs* from *how traffic is received* from *where traffic goes*. Those three concerns map onto `global`, `frontend` and `backend`, with `defaults` acting as a template so you do not repeat the same twelve lines in twenty proxies. Reading a config fluently is mostly knowing which concern each keyword belongs to. ``` global maxconn 20000 log /dev/log local0 user haproxy group haproxy daemon defaults mode http timeout connect 5s timeout client 30s timeout server 30s frontend public bind :80 acl is_api path_beg /api/ use_backend api if is_api default_backend web backend web balance roundrobin server web1 10.0.0.11:8080 server web2 10.0.0.12:8080 backend api server api1 10.0.0.21:9000 ``` ## global — the process `global` describes the running program, not traffic. It holds privilege dropping (`user`, `group`), whether to background (`daemon`), the threading model (`nbthread`), logging destinations (`log`), default TLS material such as `ssl-default-bind-ciphers` and `ssl-default-bind-options`, and a process-wide `maxconn` that caps concurrent connections across every proxy. It never contains `bind` or `server` lines — if you find yourself wanting to put a listening address here, you want a frontend. ## defaults — a template, not a proxy `defaults` accepts most of the same keywords a frontend or backend accepts, but it never serves traffic. Its settings are copied into each proxy declared **after** it in the file. Two consequences trip people up. First, it is positional: a `defaults` block placed at the bottom of the file affects nothing. Second, declaring a second `defaults` block replaces the template for everything below it — the two are not merged, so a `backend` after the second block does not inherit the first block's timeouts. From HAProxy 2.4 you can name defaults sections and select one explicitly, for example `defaults tcp-common` followed by `backend db from tcp-common`, which makes mixed HTTP/TCP configs far less accident-prone. Almost every real config puts `mode`, the `timeout` family and `option httplog` here, because forgetting a timeout in a proxy is a production outage waiting to happen. ## frontend — where traffic arrives and gets classified A `frontend` must have at least one `bind` line, which is the socket: `bind :80`, `bind :443 ssl crt /etc/haproxy/certs/site.pem`, optionally with `alpn h2,http/1.1` or `accept-proxy`. Everything else in a frontend is about *classification and decision*: `acl` statements that name a condition, `http-request` rules that inspect or rewrite, `use_backend` rules that route, and `default_backend` as the fallback. A frontend has no idea what a server is; if no `use_backend` matches and there is no `default_backend`, HAProxy has nowhere to send the request and answers 503. ## backend — the pool A `backend` has no `bind`. It holds `server` lines (`server web1 10.0.0.11:8080 check maxconn 100`), the `balance` algorithm, health-check directives, persistence, and its own `http-request`/`http-response` rules which run after the frontend's. Backends are referenced by name, and names must be unique across all proxies in the file. ## listen — both at once `listen` is a frontend and a backend in one section, so it can carry `bind` and `server` lines together: ``` listen mysql mode tcp bind :3306 server db1 10.0.0.31:3306 ``` There is nothing second-class about it; it is the same machinery with one name. It reads well for a simple pass-through where one listening port maps to one pool, and reads badly as soon as you want several backends behind one port — at that point split it, because only a frontend/backend pair lets several frontends share a backend or one frontend fan out. ## Checking it `haproxy -c -f /etc/haproxy/haproxy.cfg` parses the file and reports errors without starting the proxy. Run it in CI and before every reload; most of the mistakes above (an unknown backend name, a `server` line in the wrong section, a proxy with no timeouts) are caught there rather than in production.

  • Where would you put the mode and timeout settings, and why does it matter that defaults is positional?
    Put `mode` and the `timeout connect`/`client`/`server` family in `defaults` so no proxy is left without them. It matters because a `defaults` block applies only to proxies declared after it, and a second `defaults` block replaces the template rather than merging with it — so moving a backend above or below a defaults section silently changes its behaviour.
  • A frontend has no default_backend and no use_backend rule matches. What does the client get?
    A 503. The frontend parsed the request fine but has no destination, so HAProxy generates the error itself. In the logs the termination state shows the request never reached a server. Always give a frontend an explicit `default_backend`, even if it only serves a static error or a maintenance page.
  • When would you split a listen section into a separate frontend and backend?
    As soon as more than one destination or more than one entry point is involved: several backends selected by ACLs behind one bind, or two frontends (say plain HTTP and HTTPS) sharing one pool. A `listen` block hard-couples one bind to one pool, so it stops paying off the moment routing appears.

saying these in an interview costs you the question

  • Thinks defaults applies to proxies declared above it
  • Puts server lines in a frontend or bind in a backend
  • Believes global holds listening addresses
  • Says listen is deprecated or behaves differently from frontend plus backend
  • Cannot name which section default_backend belongs to

context