Server
Difficulty: Intermediate
11 min read

HAProxy: Load Balancing and High Availability

Learn how to configure HAProxy to distribute traffic across your servers, ensure high availability and optimize the performance of your applications.

Back to tutorials
What is HAProxy?
HAProxy (High Availability Proxy) is an open source load balancer and reverse proxy, renowned for its exceptional performance and reliability in production. Used by companies such as GitHub, Reddit and Stack Overflow, it handles millions of simultaneous connections with minimal resource consumption. This tutorial covers the installation, configuration and best practices for setting up HAProxy in your infrastructure.

Prerequisites

  • Operating system: Debian 12+, Ubuntu 22.04+ or CentOS/RHEL 8+
  • Privileges: root or sudo access
  • Network: At least 2 backend servers to test load balancing
  • Resources: 1 vCPU, 512 MB RAM minimum (HAProxy is very lightweight)
  • Ports: 80, 443 available on the HAProxy server

Installation

Installation via the repositories (Debian/Ubuntu)

The simplest method to install HAProxy on Debian or Ubuntu:

sudo apt update
sudo apt install -y haproxy

Check the installed version:

haproxy -v

Installation from the official PPA (recent version)

To get a more recent version with the latest features:

sudo apt install -y software-properties-common
sudo add-apt-repository ppa:vbernat/haproxy-2.8 -y
sudo apt update
sudo apt install -y haproxy=2.8.*

Compiling from source

For full control over the compilation options:

# Build dependencies
sudo apt install -y build-essential libssl-dev libpcre3-dev zlib1g-dev

# Download and compile
wget https://www.haproxy.org/download/2.8/src/haproxy-2.8.5.tar.gz
tar xzf haproxy-2.8.5.tar.gz
cd haproxy-2.8.5

make TARGET=linux-glibc USE_OPENSSL=1 USE_PCRE=1 USE_ZLIB=1
sudo make install

Enable and start the service:

sudo systemctl enable haproxy
sudo systemctl start haproxy
sudo systemctl status haproxy
Installation complete
HAProxy is now installed. Make sure the service is active before moving on to the configuration.

HAProxy Architecture

HAProxy relies on three main components that define the traffic flow:

  • Frontend: Entry point for traffic. It defines the listening address and port, the protocol (HTTP/TCP) and the routing rules to the backends.
  • Backend: Group of servers that receive the traffic. It defines the distribution algorithm, the health checks and the list of target servers.
  • Listen: Combination of a frontend and a backend in a single block. Convenient for simple configurations.
                   +-----------+
  Client --------->| Frontend  |
                   | (port 80) |
                   +-----+-----+
                         |
                    ACL / Routing
                    /
           +-------+--+    +--+-------+
           | Backend  |    | Backend  |
           |   web    |    |   api    |
           +--+----+--+    +--+----+--+
              |    |          |    |
           srv1  srv2      srv3  srv4

Basic Configuration

The main configuration file is located at /etc/haproxy/haproxy.cfg. It is structured into sections:

global section

System parameters that apply to the entire HAProxy process:

global
    log /dev/log local0
    log /dev/log local1 notice
    chroot /var/lib/haproxy
    stats socket /run/haproxy/admin.sock mode 660 level admin
    stats timeout 30s
    user haproxy
    group haproxy
    daemon

    # SSL parameters
    ssl-default-bind-ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256
    ssl-default-bind-ciphersuites TLS_AES_128_GCM_SHA256:TLS_AES_256_GCM_SHA384
    ssl-default-bind-options ssl-min-ver TLSv1.2 no-tls-tickets

defaults section

Default values inherited by the frontends and backends:

defaults
    log     global
    mode    http
    option  httplog
    option  dontlognull
    timeout connect 5000
    timeout client  50000
    timeout server  50000
    errorfile 400 /etc/haproxy/errors/400.http
    errorfile 403 /etc/haproxy/errors/403.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

HTTP Load Balancing

The most common configuration consists of distributing HTTP traffic across several web servers.

Frontend and backend configuration

frontend http_front
    bind *:80
    default_backend http_back

backend http_back
    balance roundrobin
    option httpchk GET /health
    http-check expect status 200
    server web1 192.168.1.10:80 check inter 3s fall 3 rise 2
    server web2 192.168.1.11:80 check inter 3s fall 3 rise 2
    server web3 192.168.1.12:80 check inter 3s fall 3 rise 2 backup

Distribution algorithms

HAProxy offers several algorithms suited to different use cases:

# Roundrobin: cyclic distribution (default)
balance roundrobin

# Leastconn: server with the fewest active connections
# Ideal for requests of varying durations
balance leastconn

# Source: session affinity by source IP
# The same client always goes to the same server
balance source

# URI: hash on the URI to optimize caching
balance uri

# First: fills one server before moving to the next
# Useful for reducing the number of active servers
balance first

HTTP health checks

Health checks verify that the backend servers are operational:

backend http_back
    balance roundrobin
    option httpchk GET /health
    http-check expect status 200

    # inter: interval between checks (default 2s)
    # fall: number of failures before declaring DOWN
    # rise: number of successes before declaring UP
    server web1 192.168.1.10:80 check inter 3s fall 3 rise 2 weight 100
    server web2 192.168.1.11:80 check inter 3s fall 3 rise 2 weight 100
Tip
Use balance leastconn for applications with variable response times (API, WebSocket) and balance roundrobin for homogeneous applications (static pages, CDN).

TCP Load Balancing

HAProxy can also distribute raw TCP traffic for services such as MySQL, PostgreSQL or Redis.

MySQL load balancing (reads)

listen mysql_cluster
    bind *:3306
    mode tcp
    balance leastconn
    option mysql-check user haproxy_check

    server mysql1 192.168.1.20:3306 check inter 5s fall 3 rise 2
    server mysql2 192.168.1.21:3306 check inter 5s fall 3 rise 2
    server mysql3 192.168.1.22:3306 check inter 5s fall 3 rise 2 backup

Redis load balancing

listen redis_cluster
    bind *:6379
    mode tcp
    balance first
    option tcp-check

    tcp-check send PING

    tcp-check expect string +PONG

    server redis1 192.168.1.30:6379 check inter 3s fall 3 rise 2
    server redis2 192.168.1.31:6379 check inter 3s fall 3 rise 2
Warning
For MySQL, create a dedicated user for health checks with no password: CREATE USER 'haproxy_check'@'%';. Only distribute writes to the master node.

SSL/TLS Termination

HAProxy can handle SSL termination to offload cryptographic processing from your backend servers.

Preparing the certificate

HAProxy requires a PEM file combining the certificate and the private key:

# Combine certificate + private key + intermediate chain
cat /etc/letsencrypt/live/example.com/fullchain.pem \
    /etc/letsencrypt/live/example.com/privkey.pem \
    > /etc/haproxy/certs/example.com.pem

# Secure the file
chmod 600 /etc/haproxy/certs/example.com.pem

HTTPS configuration with redirection

frontend https_front
    bind *:80
    bind *:443 ssl crt /etc/haproxy/certs/example.com.pem

    # HTTP to HTTPS redirection
    http-request redirect scheme https unless { ssl_fc }

    # Security headers
    http-response set-header Strict-Transport-Security "max-age=31536000; includeSubDomains"

    default_backend http_back

backend http_back
    balance roundrobin
    # Forward the client's real IP
    option forwardfor
    http-request set-header X-Forwarded-Proto https if { ssl_fc }

    server web1 192.168.1.10:80 check
    server web2 192.168.1.11:80 check

ACLs and Routing

ACLs (Access Control Lists) let you route traffic conditionally based on various criteria.

Routing by domain name

frontend http_front
    bind *:80

    # ACLs based on the Host header
    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

    # Routing to the matching backends
    use_backend api_back if is_api
    use_backend app_back if is_app
    use_backend static_back if is_static
    default_backend app_back

Routing by URL path

frontend http_front
    bind *:80

    # ACLs based on the path
    acl is_api path_beg /api/
    acl is_admin path_beg /admin/
    acl is_websocket hdr(Upgrade) -i websocket

    use_backend api_back if is_api
    use_backend admin_back if is_admin
    use_backend ws_back if is_websocket
    default_backend web_back

Routing by header and HTTP method

frontend http_front
    bind *:80

    # Routing by HTTP method
    acl is_post method POST
    acl is_get method GET

    # Routing by custom header
    acl is_mobile hdr_sub(User-Agent) -i mobile
    acl is_json hdr(Accept) -i application/json

    use_backend api_write if is_post is_api
    use_backend api_read if is_get is_api
    use_backend mobile_back if is_mobile

Advanced Health Checks

Health checks allow HAProxy to detect and automatically remove failing servers.

HTTP health checks

backend web_back
    option httpchk
    http-check send meth GET uri /health ver HTTP/1.1 hdr Host www.example.com
    http-check expect status 200

    # Check with expected content
    http-check expect string "status":"ok"

    server web1 192.168.1.10:80 check inter 5s fall 3 rise 2
    server web2 192.168.1.11:80 check inter 5s fall 3 rise 2

Custom TCP health checks

backend smtp_back
    mode tcp
    option tcp-check

    tcp-check connect
    tcp-check expect string 220
    tcp-check send EHLO haproxy

    tcp-check expect string 250
    tcp-check send QUIT


    server smtp1 192.168.1.40:25 check

Health check options

backend app_back
    # Check every 5 seconds
    # DOWN after 3 consecutive failures
    # UP after 2 consecutive successes
    server app1 192.168.1.10:8080 check inter 5s fall 3 rise 2

    # Server in drain mode (finishes current sessions)
    server app2 192.168.1.11:8080 check drain

    # Backup server (enabled only if all others are DOWN)
    server app3 192.168.1.12:8080 check backup

    # Slowstart: gradual ramp-up of the load (60 seconds)
    server app4 192.168.1.13:8080 check slowstart 60s

Statistics and Monitoring

HAProxy includes a very comprehensive statistics page to monitor the state of your infrastructure in real time.

Enabling the stats page

listen stats
    bind *:8404
    stats enable
    stats uri /stats
    stats refresh 10s
    stats admin if LOCALHOST
    stats auth admin:YourPassword

Then go to http://your-server:8404/stats to view the metrics.

Available metrics

  • Sessions: Active and total connections, rate per second
  • Bytes: Inbound and outbound traffic per server
  • Status: State of each server (UP, DOWN, DRAIN, MAINT)
  • Response time: Average, max, percentiles
  • Errors: Connection errors, timeouts, HTTP 4xx/5xx codes

Prometheus export

HAProxy 2.x natively supports exporting metrics in Prometheus format:

frontend prometheus
    bind *:8405
    http-request use-service prometheus-exporter if { path /metrics }
    no log
Advanced monitoring
Combine the Prometheus exporter with Grafana for comprehensive monitoring dashboards. Ready-to-use HAProxy dashboards are available on Grafana.com.

High Availability

To prevent HAProxy itself from becoming a single point of failure (SPOF), deploy two instances with Keepalived.

Active/passive architecture

              Virtual IP (VIP)
              192.168.1.100
                    |
            +-------+-------+
            |               |
    +-------+---+   +---+-------+
    | HAProxy 1 |   | HAProxy 2 |
    |  (MASTER) |   |  (BACKUP) |
    +-----+-----+   +-----+-----+
          |               |
    +-----+-----+---------+-----+
    |           |               |
  srv1        srv2            srv3

Installing and configuring Keepalived

sudo apt install -y keepalived

MASTER node configuration (/etc/keepalived/keepalived.conf):

vrrp_script check_haproxy {
    script "/usr/bin/killall -0 haproxy"
    interval 2
    weight 2
}

vrrp_instance VI_1 {
    state MASTER
    interface eth0
    virtual_router_id 51
    priority 101
    advert_int 1

    authentication {
        auth_type PASS
        auth_pass YourPassword
    }

    virtual_ipaddress {
        192.168.1.100/24
    }

    track_script {
        check_haproxy
    }
}

BACKUP node configuration (identical except):

vrrp_instance VI_1 {
    state BACKUP
    priority 100
    # The rest is identical to the MASTER
}
# Start Keepalived on both nodes
sudo systemctl enable keepalived
sudo systemctl start keepalived

# Check that the VIP is active on the MASTER
ip addr show eth0 | grep 192.168.1.100

Securing

Rate limiting

Protect your backends against abuse by limiting the number of requests per IP:

frontend http_front
    bind *:80

    # Tracking table by IP
    stick-table type ip size 100k expire 30s store http_req_rate(10s)

    # Track requests by IP
    http-request track-sc0 src

    # Block if more than 100 requests in 10 seconds
    http-request deny deny_status 429 if { sc_http_req_rate(0) gt 100 }

Blacklist and whitelist

frontend http_front
    bind *:80

    # Blacklist from a file
    acl is_blacklisted src -f /etc/haproxy/blacklist.txt
    http-request deny if is_blacklisted

    # Whitelist for administration
    acl is_admin_ip src 10.0.0.0/8
    acl is_admin_path path_beg /admin
    http-request deny if is_admin_path !is_admin_ip

Security headers

frontend http_front
    bind *:443 ssl crt /etc/haproxy/certs/example.com.pem

    # Security headers
    http-response set-header X-Frame-Options DENY
    http-response set-header X-Content-Type-Options nosniff
    http-response set-header X-XSS-Protection "1; mode=block"
    http-response set-header Referrer-Policy strict-origin-when-cross-origin
    http-response set-header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload"

    # Hide the backend server version
    http-response del-header Server
Security
Always change the default password of the statistics page and restrict access by IP. Never leave the admin socket accessible without authentication.

Troubleshooting

Checking the configuration

# Validate the configuration syntax
sudo haproxy -c -f /etc/haproxy/haproxy.cfg

# Reload without service interruption
sudo systemctl reload haproxy

Diagnostic commands

# View the HAProxy logs
sudo journalctl -u haproxy -f

# Check listening ports
sudo ss -tlnp | grep haproxy

# Interact with the admin socket
echo "show stat" | sudo socat stdio /run/haproxy/admin.sock

# Show the state of the backends
echo "show servers state" | sudo socat stdio /run/haproxy/admin.sock

# Disable a server for maintenance
echo "disable server http_back/web1" | sudo socat stdio /run/haproxy/admin.sock

# Re-enable a server
echo "enable server http_back/web1" | sudo socat stdio /run/haproxy/admin.sock

Common errors

  • 503 Service Unavailable: All backend servers are DOWN. Check the health checks and network connectivity.
  • 502 Bad Gateway: The backend responds but with an error. Check the application server logs.
  • 408 Request Timeout: The client or backend is too slow. Increase the timeout client or timeout server values.
  • Connection refused: Check that the backend service is actually listening on the configured port.
Hint
Use haproxy -c -f /etc/haproxy/haproxy.cfg systematically before every reload to validate your configuration and avoid service interruptions.

Conclusion

HAProxy is an essential tool for any infrastructure that requires load distribution and high availability. With this tutorial, you have the knowledge to:

  • Install and configure HAProxy on a Linux server
  • Distribute HTTP and TCP traffic across several servers
  • Set up SSL/TLS termination
  • Route traffic with conditional ACLs
  • Monitor your infrastructure via the built-in statistics
  • Ensure high availability with Keepalived
  • Secure your load balancer with rate limiting and headers

To go further, explore HAProxy's advanced features such as PROXY protocol support, dynamic maps, Lua scripts for custom logic, and multithreading to fully leverage multi-core servers.

Written by

Morgann Riu

Cybersecurity and Linux administration expert. I share my knowledge through free tutorials and training to help system administrators and developers secure their infrastructures.

Share this tutorial

Did you enjoy this article?

Comments

Checklist Sécurité Linux

30 points essentiels pour sécuriser un serveur Linux. Recevez aussi les nouveaux tutoriels par email.

Pas de spam. Désabonnement en 1 clic.