HAProxy bandwidth (bin/bout): bytes in, bytes out, and capacity planning

Every row in HAProxy’s stats output carries two cumulative byte counters: bin (bytes in) and bout (bytes out). They are the ground truth for how much data actually moves through the proxy, per frontend, per backend, and per server. Most teams only look at them when a network link saturates. Used properly, they are a baseline signal, an anomaly detector, and the raw material for bandwidth capacity planning.

This article covers what the counters actually count, the direction semantics that trip up almost everyone, how to derive rates that survive reloads, how compression changes the meaning of bout, and how to turn byte rates into a bandwidth forecast.

What bin and bout measure

bin and bout are fields 8 and 9 (0-indexed) in the stats CSV, and they appear on FRONTEND, BACKEND, and SERVER rows. They are cumulative 64-bit counters that start at zero when the HAProxy process starts. bin counts bytes HAProxy reads on that interface; bout counts bytes HAProxy writes.

One nuance matters before anything else: these counters measure bytes on the wire at HAProxy’s interface, not application payload size. The practical consequence is compression, covered below.

Collect them from the runtime socket:

# Show bin/bout for every frontend, backend, and server row
echo "show stat" | socat unix-connect:/var/run/haproxy.sock stdio | \
  awk -F, '{print $1"/"$2": bytes_in="$9" bytes_out="$10}'

Because the counters are 64-bit, wraparound is not a practical concern at any realistic link speed. You still want rates derived from deltas, not raw counter values, because of reload resets (covered below).

The direction flip between frontend and backend rows

This is the most common operator trap with bin/bout. The meaning of “in” and “out” is from HAProxy’s perspective on that row, and it flips depending on which side of the proxy the row represents:

Row typebin countsbout counts
FRONTENDbytes received from clients (requests)bytes sent to clients (responses)
BACKENDbytes sent to servers (requests)bytes received from servers (responses)
SERVERsame as backend, per individual serversame as backend, per individual server

So on the frontend, bin is request traffic and bout is response traffic. On the backend, bin is still request traffic (now leaving HAProxy toward the server) and bout is still response traffic (arriving from the server). The labels reverse relative to the wire direction even though the traffic being described is the same flow.

flowchart LR
  C[Client]
  H[HAProxy]
  S[Backend server]
  C -->|"request bytes = frontend bin"| H
  H -->|"response bytes = frontend bout"| C
  H -->|"request bytes = backend bin"| S
  S -->|"response bytes = backend bout"| H

In steady state, frontend bin and backend bin grow at roughly the same rate, and frontend bout and backend bout grow at roughly the same rate, because they describe the same requests and responses crossing two interfaces. Compression breaks this symmetry on the response side (see below), and buffering can shift short windows, but a persistent unexplained divergence between the two sides is worth investigating.

Counters reset on reload: deriving rates that survive

bin and bout are per-process counters held in memory. Every reload or restart starts a new process with all counters at zero; there is no persistence across reloads. Three operational rules follow:

  • Derive rates from deltas. Bytes per second is (sample2 - sample1) / interval. Multiply by 8 for bits per second when comparing against link capacity.
  • Detect resets instead of choking on them. If a reload happens between two samples, the delta goes negative and any naive rate calculation produces garbage. Uptime_sec from show info dropping back to a small value is the reliable reload detector; gate your rate math on it.
  • Know what “clear counters” does. On the stats socket, clear counters resets only the max values (qmax, smax, rate_max, and similar). clear counters all resets everything, including the cumulative byte counters, equivalent to a restart from a monitoring perspective. Do not run it on a production proxy unless you intend to wipe baseline state.

A minimal manual rate check, if you need one during an incident:

# Bytes/sec on one frontend, two samples 10s apart (replace "web" with your proxy name)
s1=$(echo "show stat" | socat unix-connect:/var/run/haproxy.sock stdio | awk -F, '$1=="web" && $2=="FRONTEND" {print $10}')
sleep 10
s2=$(echo "show stat" | socat unix-connect:/var/run/haproxy.sock stdio | awk -F, '$1=="web" && $2=="FRONTEND" {print $10}')
echo "bout bytes/sec: $(( (s2 - s1) / 10 ))"

If the number comes back negative or absurd, a reload happened between samples; discard and re-measure. If you scrape the built-in Prometheus endpoint (HAProxy 2.0+), the same counters are exposed as haproxy_frontend_bytes_in_total, haproxy_frontend_bytes_out_total, and the backend/server equivalents, typed as counters, and rate() handles resets natively.

Ratios and baselines: where bin/bout earn their keep

Raw byte rates tell you volume. The ratios built from them tell you shape, and shape changes are where the anomalies live.

  • bout/bin ratio. For a typical web application, responses are larger than requests, so bout/bin is greater than 1 and fairly stable over time. A sudden inversion means upload traffic has overtaken download traffic: large POST/PUT bodies, backup or ingest jobs running through the proxy, media uploads, or something less benign. An inversion is not automatically an incident; it is a baseline deviation that deserves a look, and it is one of the cheaper early hints of data exfiltration through a proxied application.
  • Bytes per request. bout_rate / req_rate gives you average response size on the wire. A steady climb with flat request rates means responses are getting bigger: unbounded API payloads, missing pagination, cache misses pushing large assets through the proxy, or a frontend change that ships more bytes. This is a capacity-relevant trend long before it is an incident.
  • Zero bytes out with normal requests in. If req_rate and bin look normal but bout is near zero, backends are returning empty or near-empty responses. That is a serving anomaly, not a bandwidth one, and it is easy to miss if you only watch request rates and error counters.
  • Low throughput with high session counts. The composite Slowloris pattern in the HAProxy playbook pairs very low bin/bout throughput with high scur and low req_rate: many connections holding slots but moving almost no data. Bandwidth counters are part of that signature.

Per-server rows add one more dimension: byte distribution across servers in a backend. If one server’s bout rate is much higher than its peers at similar request counts, it is serving different (larger) content or receiving a skewed share of heavy requests, which matters for per-server egress planning.

Compression changes what bout means

When HAProxy compression is enabled, bout reflects compressed bytes on the wire, not the original payload. Two consequences:

  • Do not compute payload size from bout. If you use bout to estimate response sizes or data transfer costs while compression is active, you will systematically underestimate the original content. Use comp_in (pre-compression bytes) for the original size and comp_out (post-compression bytes) for wire size; the ratio comp_out / comp_in is the real compression ratio, and comp_byp tells you how much traffic bypassed compression entirely.
  • Frontend and backend rows stop matching on the response side. Backend bout is what HAProxy received from the server; frontend bout is what it sent to the client after compression. A gap between them is expected and is, in fact, your bandwidth saving.

For link-capacity purposes, compressed bout is the correct number, because the wire is what saturates. For application-level analysis (payload growth, response bloat), use comp_in.

Capacity planning with bin/bout

Bandwidth capacity planning with these counters is straightforward once rates are reliable:

  1. Express byte rates as link utilization. Convert the bout delta to bits per second and compare it to the NIC link speed. On a full-duplex link, size each direction independently; bout is almost always the direction that saturates first for web workloads. Alert on sustained utilization approaching link capacity, expressed as a percentage of available bandwidth rather than an absolute number.
  2. Confirm against the OS. Correlate HAProxy’s byte rates with the host’s NIC counters (/proc/net/dev or ip -s link). HAProxy’s view and the kernel’s view should track each other; divergence means something else on the host is consuming the interface, or the proxy is not the only tenant of the link.
  3. Trend peaks, not averages. Record peak bout rate per frontend per day and trend it over weeks. Averages hide the growth; peaks are what hit the ceiling.
  4. Forecast with bytes per request. Multiply projected request-rate growth by the current bout_rate / req_rate to get projected wire-rate growth. If bytes per request is itself trending up, factor that in separately; it compounds.
  5. Plan per-server egress from server rows. Backend server bout rates show the outbound demand each backend node must sustain, which feeds directly into backend NIC and instance sizing.

Sustained bandwidth approaching link or NIC capacity is a ticket-level condition in the HAProxy monitoring playbook: not an immediate outage, but a runway problem with a hard cliff at the end, because a saturated link degrades every proxied flow at once.

Signals to correlate

SignalWhy it mattersWarning sign
bout rate as % of link capacityDirect saturation measure for the direction that fills firstSustained utilization approaching link capacity
bout/bin ratioTraffic shape baseline for the applicationSudden inversion toward upload dominance
bout_rate / req_rateAverage response size on the wireSteady climb: response bloat, cache misses, payload growth
comp_out / comp_inTrue compression ratio when compression is enabledRatio near 1.0: CPU spent compressing incompressible content
OS NIC counters vs bout rateConfirms the proxy’s view matches the wireDivergence: other traffic sharing the interface
Uptime_secDetects the reloads that reset the countersDrop to near zero: discard deltas, recompute rates

How Netdata helps

  • Automatic rate derivation. Netdata collects bin/bout per frontend, backend, and server every second and charts rates directly, so reload-induced counter resets do not produce phantom drops or require manual delta math.
  • Ratio visibility without scripting. Charting bout rate next to req_rate makes bytes-per-request and bout/bin shape changes visible at a glance, which is where the anomalies surface first.
  • Proxy-to-wire correlation. HAProxy byte rates and host NIC counters appear on the same node dashboard, so you can tell proxy traffic from everything else sharing the interface without switching tools.
  • Compression context. When compression is enabled, comp_in, comp_out, and comp_byp sit alongside bout, keeping wire bytes and payload bytes distinct instead of conflated.
  • Per-server comparison. Server-row byte rates per backend make load-distribution skew visible without querying the stats socket by hand.