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 type | bin counts | bout counts |
|---|---|---|
| FRONTEND | bytes received from clients (requests) | bytes sent to clients (responses) |
| BACKEND | bytes sent to servers (requests) | bytes received from servers (responses) |
| SERVER | same as backend, per individual server | same 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_secfromshow infodropping 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 countersresets only the max values (qmax,smax,rate_max, and similar).clear counters allresets 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/binis 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_rategives 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_rateandbinlook normal butboutis 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/boutthroughput with highscurand lowreq_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
boutto estimate response sizes or data transfer costs while compression is active, you will systematically underestimate the original content. Usecomp_in(pre-compression bytes) for the original size andcomp_out(post-compression bytes) for wire size; the ratiocomp_out / comp_inis the real compression ratio, andcomp_byptells you how much traffic bypassed compression entirely. - Frontend and backend rows stop matching on the response side. Backend
boutis what HAProxy received from the server; frontendboutis 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:
- Express byte rates as link utilization. Convert the
boutdelta to bits per second and compare it to the NIC link speed. On a full-duplex link, size each direction independently;boutis 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. - Confirm against the OS. Correlate HAProxy’s byte rates with the host’s NIC counters (
/proc/net/devorip -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. - Trend peaks, not averages. Record peak
boutrate per frontend per day and trend it over weeks. Averages hide the growth; peaks are what hit the ceiling. - Forecast with bytes per request. Multiply projected request-rate growth by the current
bout_rate / req_rateto get projected wire-rate growth. If bytes per request is itself trending up, factor that in separately; it compounds. - Plan per-server egress from server rows. Backend server
boutrates 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
| Signal | Why it matters | Warning sign |
|---|---|---|
| bout rate as % of link capacity | Direct saturation measure for the direction that fills first | Sustained utilization approaching link capacity |
| bout/bin ratio | Traffic shape baseline for the application | Sudden inversion toward upload dominance |
| bout_rate / req_rate | Average response size on the wire | Steady climb: response bloat, cache misses, payload growth |
| comp_out / comp_in | True compression ratio when compression is enabled | Ratio near 1.0: CPU spent compressing incompressible content |
| OS NIC counters vs bout rate | Confirms the proxy’s view matches the wire | Divergence: other traffic sharing the interface |
| Uptime_sec | Detects the reloads that reset the counters | Drop to near zero: discard deltas, recompute rates |
How Netdata helps
- Automatic rate derivation. Netdata collects
bin/boutper 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
boutrate next toreq_ratemakes bytes-per-request andbout/binshape 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, andcomp_bypsit alongsidebout, 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.
Related guides
- HAProxy 502 Bad Gateway: backend connection and response failures
- HAProxy 503 Service Unavailable: no server is available to handle this request
- HAProxy 504 Gateway Timeout: timeout server and the backend timeout cascade
- HAProxy backend losing servers: active server count and cascade risk
- HAProxy backend queue building (qcur): requests waiting for a free server slot
- HAProxy compression overhead: comp_byp, CPU cost, and when compression backfires
- HAProxy connect time (ctime) high: network latency and backend accept-queue overflow
- HAProxy connection errors (econ): backend connections refused, timed out, or reset
- HAProxy scur approaching slim: the concurrent-session saturation signal
- HAProxy 5xx delta: telling HAProxy-generated errors from backend errors
- HAProxy health checks green but the application is broken: when UP does not mean healthy
- HAProxy health check L4TOUT and L4CON: server marked DOWN and unreachable






