Skip to content

HAProxy Load Balancer: Complete Configuration Guide

DodaTech Updated 2026-06-23 7 min read

In this tutorial, you'll learn about HAProxy Load Balancer: Complete Configuration Guide. We cover key concepts, practical examples, and best practices.

HAProxy is a high-performance TCP/HTTP load balancer and proxy server widely used to distribute traffic across multiple backend servers. It powers some of the busiest websites on the internet with sub-millisecond latency and minimal resource overhead.

In this tutorial, you will learn to configure HAProxy frontends and backends, write ACL rules for traffic routing, set up SSL termination, configure health checks, enable session persistence, and monitor HAProxy metrics. DodaTech uses HAProxy to distribute load across Durga Antivirus Pro update servers and Doda Browser sync infrastructure.

What You'll Learn

By the end of this guide, you will deploy HAProxy as a load balancer, route traffic based on domain names and URL paths with ACLs, terminate SSL, configure active health checks, and monitor HAProxy stats through its built-in dashboard.

Why Load Balancing Matters

A single server has limits. Load balancing distributes traffic across multiple servers to improve availability, scalability, and fault tolerance. If one server fails, HAProxy automatically routes traffic to healthy servers. This is essential for Web Servers serving production traffic and DevOps infrastructure managing high-availability deployments.

HAProxy Learning Path

flowchart LR
  A[Installation] --> B[Frontend & Backend]
  B --> C[ACL Rules]
  C --> D[SSL Termination]
  D --> E[Health Checks]
  E --> F[Session Persistence]
  F --> G{You Are Here}
  style G fill:#f90,color:#fff

Installation

# Ubuntu / Debian
sudo apt update && sudo apt install haproxy -y

# RHEL / CentOS / Fedora
sudo dnf install haproxy -y

# Verify
sudo systemctl start haproxy
sudo systemctl enable haproxy
sudo systemctl status haproxy --no-pager

Expected output

haproxy.service - HAProxy Load Balancer
   Loaded: loaded (/lib/systemd/system/haproxy.service; enabled; vendor preset: enabled)
   Active: active (running)

Basic Configuration

HAProxy configuration lives in /etc/haproxy/haproxy.cfg. A minimal setup has a frontend (listening point) and a backend (server pool):

global
    log /dev/log local0
    maxconn 4096
    user haproxy
    group haproxy

defaults
    log global
    mode http
    timeout connect 5000ms
    timeout client 50000ms
    timeout server 50000ms

frontend web_frontend
    bind *:80
    default_backend web_servers

backend web_servers
    balance roundrobin
    server web1 10.0.1.1:80 check
    server web2 10.0.1.2:80 check
    server web3 10.0.1.3:80 check

Expected behavior

curl -I http://localhost
# HTTP/1.1 200 OK
# Each request is distributed to web1, web2, or web3 in turn

ACL Rules for Traffic Routing

ACLs let you route traffic based on domain, URL path, headers, or client IP:

frontend web_frontend
    bind *:80
    bind *:443 ssl crt /etc/haproxy/certs/

    # ACLs for domain-based routing
    acl is_api hdr(host) -i api.example.com
    acl is_app hdr(host) -i app.example.com
    acl is_static hdr(host) -i static.example.com

    # ACL for URL path routing
    acl is_health path_beg /health
    acl is_admin path_beg /admin

    # ACL for WebSocket
    acl is_websocket hdr(Upgrade) -i websocket

    # Routing decisions
    use_backend api_servers if is_api
    use_backend app_servers if is_app
    use_backend static_servers if is_static
    default_backend web_servers

backend api_servers
    balance leastconn
    option httpchk GET /health
    server api1 10.0.2.1:3000 check
    server api2 10.0.2.2:3000 check

backend static_servers
    balance roundrobin
    server static1 10.0.3.1:80 check
    server static2 10.0.3.2:80 check

Testing ACL routing

curl -H "Host: api.example.com" http://localhost/api/users
# Routes to api_servers backend

curl -H "Host: app.example.com" http://localhost/dashboard
# Routes to app_servers backend

SSL Termination

Terminate SSL at the load balancer so backend servers handle unencrypted traffic:

# Create certificate directory
sudo mkdir -p /etc/haproxy/certs

# Combine certificate and key into PEM format
sudo cat /etc/letsencrypt/live/example.com/fullchain.pem \
  /etc/letsencrypt/live/example.com/privkey.pem \
  > /etc/haproxy/certs/example.com.pem

sudo chmod 600 /etc/haproxy/certs/example.com.pem
frontend https_frontend
    bind *:443 ssl crt /etc/haproxy/certs/example.com.pem
    http-request redirect scheme https unless { ssl_fc }

    # Security headers
    http-response add-header Strict-Transport-Security "max-age=31536000; includeSubDomains"
    http-response add-header X-Content-Type-Options nosniff
    http-response add-header X-Frame-Options DENY

    default_backend web_servers

frontend http_frontend
    bind *:80
    http-request redirect scheme https code 301

Expected output

curl -I https://example.com
# HTTP/1.1 200 OK
# strict-transport-security: max-age=31536000; includeSubDomains

Health Checks

HAProxy monitors backend servers with active health checks:

backend web_servers
    balance roundrobin
    # Active HTTP health check
    option httpchk GET /health HTTP/1.1\r\nHost:\ example.com
    http-check expect status 200

    # Server with custom check interval
    server web1 10.0.1.1:80 check inter 2000 rise 3 fall 2
    server web2 10.0.1.2:80 check inter 2000 rise 3 fall 2

    # Backup server (only used if all primary servers are down)
    server web-backup 10.0.1.4:80 check backup

Checking server status

echo "show stat" | sudo socat stdio /var/run/haproxy/admin.sock
# Expected columns: pxname, svname, status, weight, act, bck, chkfail, downtime
# web_servers web1 UP 1 1 0 0 0
# web_servers web2 UP 1 1 0 0 0

Session Persistence

Ensure a client always reaches the same backend server:

backend app_servers
    balance roundrobin
    # Cookie-based persistence
    cookie SERVERID insert indirect nocache
    server app1 10.0.4.1:8080 cookie app1 check
    server app2 10.0.4.2:8080 cookie app2 check
    server app3 10.0.4.3:8080 cookie app3 check

For source IP persistence:

backend app_servers
    balance source
    hash-type consistent
    server app1 10.0.4.1:8080 check
    server app2 10.0.4.2:8080 check
    server app3 10.0.4.3:8080 check

Monitoring with Stats Dashboard

Enable the built-in statistics page:

frontend stats_frontend
    bind *:8404
    stats enable
    stats uri /stats
    stats refresh 10s
    stats admin if LOCALHOST
    stats auth admin:securepassword

Expected behavior

curl -u admin:securepassword http://localhost:8404/stats
# Returns HTML statistics page with server status, traffic, and uptime

Common Errors

1. Backend Down

Server is not responding to health checks. Verify the backend service is running and the port is correct. Check sudo journalctl -u haproxy --no-pager | tail.

2. SSL Handshake Failure

The PEM file is malformed or permissions are wrong. Ensure the certificate and key are concatenated in order and chmod 600 is set.

3. High Connection Timeout

Backend servers are slow. Increase timeout server in the defaults section. Check backend server load and database queries.

4. ACL Not Matching

ACL conditions evaluate in order. Use haproxy -f /etc/haproxy/haproxy.cfg -c to validate config syntax and test ACL patterns.

5. Port Already in Use

Another service is listening on the same port. Check with sudo ss -tlnp | grep :80 and stop the conflicting service or change HAProxy's bind port.

6. Stats Dashboard Unauthenticated

Anyone can see the stats page. Always set stats auth with a strong password and restrict the bind address to internal IPs.

Practice Questions

1. What is the difference between roundrobin and leastconn balancing? Roundrobin distributes requests evenly across servers in order. Leastconn sends requests to the server with the fewest active connections, which is better for long-lived connections.

2. How do you check if a backend server is healthy in HAProxy? Use echo "show stat" | sudo socat stdio /var/run/haproxy/admin.sock or check the stats dashboard. Servers with status UP are healthy.

3. What is session persistence and why is it needed? Session persistence ensures a client's requests always go to the same backend server. This is needed for stateful applications that store session data locally rather than in a shared store.

4. Challenge: Blue-green deployment with HAProxy

Configure HAProxy with two backend pools (blue and green) and use an ACL based on a cookie to route 10% of traffic to green:

backend blue
    server app1 10.0.1.1:8080 check
    server app2 10.0.1.2:8080 check

backend green
    server app3 10.0.2.1:8080 check
    server app4 10.0.2.2:8080 check

Add a frontend ACL that routes requests with cookie deploy=green to the green pool and all others to blue.

Mini Project: High-Availability Reverse Proxy

Deploy HAProxy as a central load balancer for three services:

  1. Install HAProxy on a server with both public and private network interfaces
  2. Configure three backend pools:
    • api (Node.js on ports 3000-3001)
    • web (static NGINX servers on ports 80-81)
    • admin (restricted to internal IPs only)
  3. Set up ACLs to route api.example.com, www.example.com, and admin.example.com
  4. Enable SSL termination with a Let's Encrypt certificate
  5. Configure the stats dashboard on port 8404 with authentication
  6. Test failover by stopping one backend server
# Validate config
sudo haproxy -f /etc/haproxy/haproxy.cfg -c

# Reload HAProxy
sudo systemctl reload haproxy

# Test routing
curl -H "Host: api.example.com" https://localhost/api/health
curl -H "Host: www.example.com" https://localhost/

This architecture mirrors how DodaTech balances traffic across Doda Browser update mirrors and Durga Antivirus Pro signature distribution servers.

FAQ

What port does HAProxy listen on by default?

HAProxy does not listen on any port by default. You configure bind directives in frontend sections. Common ports are 80 (HTTP), 443 (HTTPS), and 8404 (stats).

Can HAProxy do SSL termination for multiple domains?

Yes. Create a single PEM file per domain or use a wildcard certificate. HAProxy matches the SNI from the TLS handshake to select the correct certificate.

How does HAProxy compare to NGINX for load balancing?

HAProxy is more feature-rich for TCP/HTTP load balancing with advanced health checks, ACLs, and session persistence. NGINX excels at static file serving and reverse proxy with less configuration complexity.

What is the difference between active and passive health checks?

Active checks periodically test the server by connecting to it. Passive checks monitor real traffic failures. HAProxy supports both and can mark a server down based on consecutive failures.

How do I achieve zero-downtime reloads with HAProxy?

Use sudo systemctl reload haproxy which sends a SIGUSR2 signal. HAProxy spawns a new process that takes over connections while the old process finishes existing ones.

Built by the developers of Doda Browser, DodaZIP, and Durga Antivirus Pro.

Built by the developers of DodaTech

Doda Browser, DodaZIP & Durga Antivirus Pro