When it comes to distributing traffic across multiple servers, HAProxy has earned a reputation as a lightweight, reliable, and highly configurable load balancer for Linux environments. Whether you’re running a small web farm or a massive micro‑services architecture, mastering HAProxy gives you the control you need to keep users happy and services resilient. In this guide we’ll walk through everything you need to configure network load balancing on Linux with HAProxy, from installation to fine‑tuning, while highlighting common mistakes and sharing practical tips.
What You’ll Need
- A Linux server (Ubuntu 22.04, Debian 12, or CentOS 9 are all fine) with root or sudo privileges.
- At least two backend servers that will serve the same application (e.g., two Apache or Nginx instances).
- Basic knowledge of Linux command line and networking concepts.
- Optional: A domain name and SSL certificate if you plan to terminate TLS at HAProxy.
Step 1: Install HAProxy
First, bring HAProxy onto your front‑end machine. On Ubuntu or Debian run:
sudo apt update
sudo apt install -y haproxy On CentOS/RHEL you can use the EPEL repository:
sudo dnf install -y epel-release
sudo dnf install -y haproxy After installation, verify the version to ensure you have a recent release (2.6+ is recommended):
haproxy -v If you need a newer version, consider building from source – the official HAProxy site provides clear instructions.
Step 2: Prepare Backend Servers
Each backend server must be reachable from the HAProxy host on the service port (usually 80 for HTTP or 443 for HTTPS). For this example we’ll assume two web servers with IPs 10.0.1.101 and 10.0.1.102 listening on port 80.
Test connectivity:
curl http://10.0.1.101
curl http://10.0.1.102 If you receive the expected HTML output, you’re ready to move on. Remember to open the necessary firewall ports (e.g., ufw allow 80/tcp or firewall-cmd --add-port=80/tcp --permanent).
Step 3: Create a Basic HAProxy Configuration
HAProxy’s main config lives at /etc/haproxy/haproxy.cfg. Back up the original file before editing:
sudo cp /etc/haproxy/haproxy.cfg /etc/haproxy/haproxy.cfg.bak Replace its contents with a minimal, functional configuration:
global
log /dev/log local0
log /dev/log local1 notice
daemon
maxconn 2000
tune.ssl.default-dh-param 2048
defaults
log global
mode http
option httplog
option dontlognull
timeout connect 5000ms
timeout client 50000ms
timeout server 50000ms
errorfile 400 /etc/haproxy/errors/400.http
errorfile 403 /etc/haproxy/errors/403.http
errorfile 408 /etc/haproxy/errors/408.http
errorfile 500 /etc/haproxy/errors/500.http
errorfile 502 /etc/haproxy/errors/502.http
errorfile 503 /etc/haproxy/errors/503.http
errorfile 504 /etc/haproxy/errors/504.http
frontend http_front
bind *:80
default_backend web_back
backend web_back
balance roundrobin
server web1 10.0.1.101:80 check
server web2 10.0.1.102:80 check
This configuration does three things:
- Listens on port 80 for incoming HTTP traffic.
- Uses the
roundrobinalgorithm to distribute requests evenly. - Performs a basic health check (the
checkflag) on each backend server.
Save the file and test the syntax:
sudo haproxy -c -f /etc/haproxy/haproxy.cfg If the output says “Configuration file is valid”, you can start HAProxy:
sudo systemctl enable haproxy
sudo systemctl start haproxy Visit the HAProxy server’s IP address in a browser – you should see the same content as the backend servers, confirming that traffic is being balanced.
Step 4: Enable Advanced Health Checks
Basic TCP checks are fine for simple services, but HTTP‑level health checks give you more confidence. Modify the backend section:
backend web_back
balance roundrobin
option httpchk GET /health
http-check expect status 200
server web1 10.0.1.101:80 check check-ssl verify none
server web2 10.0.1.102:80 check check-ssl verify none
Here we ask each server to respond to /health with a 200 status. Adjust the path to match whatever health endpoint your application exposes (e.g., /status or /ping).
Reload HAProxy without dropping connections:
sudo systemctl reload haproxy Check the HAProxy stats page (if you enable it later) or run echo "show stat" | sudo socat /var/run/haproxy.sock stdio to see health status.
Step 5: Fine‑Tune the Load‑Balancing Algorithm
Round‑robin works for most cases, but HAProxy offers many algorithms:
- leastconn – Sends new connections to the server with the fewest active sessions.
- source – Hashes the client IP, providing session persistence without cookies.
- uri – Hashes the request URI, useful for cache‑friendly workloads.
To switch to leastconn, edit the backend block:
backend web_back
balance leastconn
...
After reloading, you can monitor which algorithm is in effect via the stats socket or the web UI.
Step 6: Secure HAProxy with SSL Termination
Terminating TLS at HAProxy offloads encryption work from your backends and lets you use a single certificate for all traffic. First, obtain a certificate (Let’s Encrypt, self‑signed, or commercial). Place the .crt and .key files in /etc/haproxy/certs/ and combine them:
sudo mkdir -p /etc/haproxy/certs
sudo cat fullchain.pem privkey.pem > /etc/haproxy/certs/haproxy.pem
sudo chmod 600 /etc/haproxy/certs/haproxy.pem
Update the frontend to listen on 443 and use the certificate:
frontend https_front
bind *:443 ssl crt /etc/haproxy/certs/haproxy.pem alpn h2,http/1.1
redirect scheme https if !{ ssl_fc }
default_backend web_back_secure
backend web_back_secure
balance roundrobin
option httpchk GET /health
http-check expect status 200
server web1 10.0.1.101:80 check
server web2 10.0.1.102:80 check
Note the use of alpn to enable HTTP/2 when the client supports it. Reload HAProxy and test with curl -k https://your‑haproxy‑ip or a browser.
Step 7: Test, Monitor, and Automate
Before you declare victory, run a few real‑world tests:
- Use
ab -n 1000 -c 50 http://haproxy_ip/orwrkto generate load and watch the distribution. - Inspect HAProxy logs at
/var/log/haproxy.log(orjournalctl -u haproxyon systemd). - Enable the built‑in stats page for a live dashboard. Add to
frontend http_front:bind *:8404 stats enable stats uri /stats stats refresh 10s stats auth admin:strongpassword
Finally, make HAProxy start on boot (already done with systemctl enable) and consider adding a watchdog script or using systemd’s Restart=on-failure to keep it running.
Common Mistakes to Avoid
Even experienced admins trip over a few pitfalls when configuring HAProxy:
- Forgetting to open firewall ports. If
iptablesorufwblocks 80/443, clients will see connection timeouts. - Mismatched health‑check paths. A typo in
option httpchkwill mark healthy servers as down. - Using “maxconn” too low. The global
maxconnlimits total concurrent connections; set it high enough for your traffic spikes. - Not reloading after config changes. Editing
haproxy.cfgwithoutsystemctl reload haproxyleaves the old config in effect. - Storing certificates with insecure permissions. HAProxy will refuse to start if the PEM file is world‑readable.
Tips and Tricks
Here are a few pro‑level adjustments that can squeeze extra performance and reliability out of HAProxy:
- Enable TCP fast open. Add
tune.tcpka.accept 1andtune.tcpka.queue 1000in theglobalsection. - Use stick tables for rate limiting. Example:
backend web_back stick-table type ip size 1m expire 10s store http_req_rate(10s) tcp-request connection track-sc0 src acl too_fast sc_http_req_rate(0) gt 100 tcp-request connection reject if too_fast - Log in JSON format. Modern log aggregators love JSON. Add
log-format {"time":"%t","src":"%ci","dst":"%si","status":%ST}under
global. - Deploy HAProxy in Docker or Kubernetes. The same
haproxy.cfgworks inside a container; just mount the config and certificates as volumes.
Frequently Asked Questions
Can HAProxy handle UDP traffic?
Yes, HAProxy 2.0+ includes native UDP support. You define a frontend with proto udp and a matching backend. However, for DNS or VoIP workloads you may prefer specialized load balancers like CoreDNS or Kamailio.
Do I need to run HAProxy on a separate machine?
Not necessarily. Small setups can run HAProxy on the same host as one of the backends, but for true high availability you should deploy at least two HAProxy instances behind a virtual IP (using keepalived or Corosync) to avoid a single point of failure.
How do I enable HTTP/2 to the backend servers?
HAProxy can speak HTTP/2 on the client side but only forwards HTTP/1.1 to backends unless you enable mode tcp and let the backends handle TLS termination themselves. For full end‑to‑end HTTP/2 you need backend servers that support it and configure HAProxy with option http-use-htx and http-request set-var(req.backend_proto) str(HTTP/2) – a more advanced topic beyond this guide.
Conclusion
Setting up HAProxy for network load balancing on Linux is a straightforward process once you understand the core concepts: install the binary, define front‑ends and back‑ends, enable health checks, and optionally add SSL termination. By following the steps above, avoiding common pitfalls, and applying the performance tricks, you’ll have a robust, scalable load‑balancing layer that can grow with your infrastructure. Keep the HAProxy stats page handy, monitor logs, and iterate on the configuration as traffic patterns evolve. Happy balancing!
Photo by Kevin Ache on Unsplash





