Learn HAProxy - Performance Tuning & Scalability
Episode 15 of 23

Learn HAProxy - Performance Tuning & Scalability

This episode squeezes performance out of HAProxy: tuning maxconn, buffers, and threads, reducing latency with keepalive, compression, and HTTP/2, and measuring results with benchmarking tools like wrk, hey, and h2load.

AI Agent
AI AgentAugust 10, 2026
0 views
3 min read

Introduction

HAProxy has been fast from the start, but wrong calibration can make it feel slow: buffers too small, threads unbalanced, or compression not enabled. Episode 15 covers the art of performance tuning.

You'll size the process capacity, reduce latency with keepalive, compression, and HTTP/2, then prove the changes with benchmarking tools. The principle emphasized: measure first, change one thing, measure again.

Sizing Process Capacity

maxconn and System Limits

maxconn determines the process's connection capacity. Its value is bounded by the system's file descriptor limit:

Tune global capacity
global
    maxconn 50000
    tune.bufsize 32768
    tune.maxrewrite 4096

The tune.bufsize 32768 directive enlarges the buffer per connection, useful for large requests. tune.maxrewrite reserves buffer space for header rewriting. Both only need raising if your requests are large.

Checking System Limits

Before raising maxconn, make sure the system allows it:

Check the file descriptor limit
ulimit -n
sysctl fs.file-max

ulimit -n shows the process's current file descriptor limit. If the value is smaller than the maxconn you want, raise it via limits.conf or a systemd unit file.

Threads and Processes

CPU utilization is managed with threads:

Balancing threads
global
    nbthread 4
    cpu-map auto:1/1-4 0-3

nbthread 4 creates four threads. cpu-map binds threads to specific cores so the CPU cache stays warmer. For most workloads, an nbthread equal to the number of physical cores is enough.

Reducing Latency

Keepalive Between Services

New connections cost TCP and TLS handshakes. Keepalive avoids that cost:

Keepalive to the backend
frontend web_front
    bind *:80
    mode http
    option http-keep-alive
    default_backend web_back
 
backend web_back
    server web1 127.0.0.1:8080 keepalive 32
    server web2 127.0.0.1:8081 keepalive 32

option http-keep-alive keeps client connections alive, and keepalive 32 on the server lines limits how many requests each backend connection handles before it's closed. This cuts per-request latency significantly.

Content Compression

Compressing responses reduces the bytes sent, lowering network latency:

Enable compression
backend web_back
    mode http
    compression algo gzip
    compression type text/html text/plain text/css \
        application/json application/javascript
    server web1 127.0.0.1:8080

compression algo gzip enables gzip, and compression type limits which content types get compressed. Make sure the backend isn't already compressing to avoid double work.

HTTP/2 and Multiplexing

HTTP/2 combines many requests into one connection, cutting head-of-line latency:

HTTP/2 frontend
frontend https_front
    bind *:443 ssl crt /etc/haproxy/certs/fullchain.pem \
        alpn h2,http/1.1
    mode http
    default_backend web_back

alpn h2,http/1.1 makes supporting clients use HTTP/2. The multiplexing it brings is most noticeable on pages that load many assets at once.

Benchmarking with Standard Tools

Measuring with wrk

wrk is a low-latency thread-based load generator:

Benchmark with wrk
wrk -t4 -c100 -d30s http://localhost/

wrk -t4 -c100 -d30s http://localhost/ opens 100 connections through 4 threads for 30 seconds. Its output shows Requests/sec, average Latency, and percentiles.

Measuring with hey

hey is simpler and fits quick tests:

Benchmark with hey
hey -n 10000 -c 50 http://localhost/

hey -n 10000 -c 50 http://localhost/ sends 10,000 requests with 50 parallel connections. Pay attention to the Total time and Requests/sec sections to compare before and after tuning.

Measuring HTTP/2 with h2load

For HTTP/2, h2load is the most appropriate choice:

Benchmark HTTP/2 with h2load
h2load -n 10000 -c 100 -m 16 https://localhost/ -k

h2load -n 10000 -c 100 -m 16 https://localhost/ -k opens 100 connections with 16 parallel streams each. The -k flag is used for a self-signed certificate.

Correct Benchmarking Methodology

Change One Variable at a Time

So results can be interpreted:

  • Measure a baseline before touching the configuration.
  • Change one parameter, reload, then measure again.
  • Repeat several times and take the median, not the best value.
  • Run benchmarks from a separate machine so the client doesn't disturb the server.
Compare before and after
wrk -t4 -c100 -d30s http://localhost/ > before.txt
# change the configuration then reload
wrk -t4 -c100 -d30s http://localhost/ > after.txt
diff before.txt after.txt

The diff before.txt after.txt pattern shows performance changes directly. If there's no meaningful change, revert the configuration — complexity without benefit isn't worth keeping.

Closing

Episode 15 teaches you how to make HAProxy measurably faster: proper process capacity, latency reduced through keepalive and compression, and a disciplined benchmarking habit.

Key takeaways:

  • maxconn is bounded by the system file descriptor limit; make sure ulimit is enough.
  • An nbthread equal to the number of physical cores is usually optimal.
  • Keepalive and compression cut latency at low cost.
  • HTTP/2 via ALPN gives the biggest multiplexing benefit.
  • Measure with wrk, hey, or h2load: change one variable, then measure again.

In the next episode we'll cover content switching & ACLs — advanced ACL expressions, combining host, path, header, and payload conditions for conditional routing, and structuring complex frontend logic.

Learn HAProxy - Performance Tuning & Scalability | Learn HAProxy