콘텐츠로 이동

HAProxy Cheat Sheet

Overview

HAProxy (High Availability Proxy) is a free, open-source software written in C that provides extremely high performance load balancing and proxying for TCP and HTTP-based applications. It has become the de facto standard open-source load balancer, deployed in front of some of the world’s highest-traffic websites including GitHub, Twitter, and Reddit. HAProxy is single-threaded in design but can run multiple worker processes, and its event-driven architecture allows it to handle hundreds of thousands of concurrent connections with minimal memory overhead.

HAProxy operates across Layers 4 and 7 of the OSI model. In Layer 4 (TCP) mode, it routes connections based on IP addresses and ports without inspecting the content. In Layer 7 (HTTP) mode, it can route based on HTTP headers, cookies, paths, query parameters, and arbitrary content matching — making it possible to implement sophisticated routing logic, canary deployments, A/B testing, blue-green deployments, and circuit breaking.

The configuration model centers on four section types: global (process-level settings), defaults (inherited by all frontends and backends), frontend (defines listeners and initial routing), and backend (defines server pools and balancing). Access Control Lists (ACLs) and use_backend directives create conditional routing logic. HAProxy’s stick-table feature provides distributed session tracking for stateful applications. The built-in statistics page gives live visibility into connection rates, error rates, and server health.

Installation

Ubuntu/Debian

# From official repos (older version)
sudo apt update && sudo apt install -y haproxy

# Latest stable via PPA (recommended)
sudo add-apt-repository ppa:vbernat/haproxy-2.8
sudo apt update && sudo apt install -y haproxy=2.8.*

# Verify
haproxy -v
sudo systemctl status haproxy

RHEL/CentOS/Fedora

# Fedora
sudo dnf install -y haproxy

# RHEL 8/9 via EPEL
sudo dnf install -y epel-release
sudo dnf install -y haproxy

# Or install from SCL (Software Collections) for latest version
sudo dnf install -y centos-release-scl
sudo dnf install -y rh-haproxy20-haproxy

Build from Source (Latest)

# Install dependencies
sudo apt install -y build-essential libssl-dev libpcre3-dev zlib1g-dev \
  libsystemd-dev liblua5.3-dev

# Download and build
curl -OL 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 -j$(nproc) TARGET=linux-glibc \
  USE_OPENSSL=1 USE_PCRE=1 USE_ZLIB=1 \
  USE_SYSTEMD=1 USE_LUA=1

sudo make install PREFIX=/usr/local

Docker

# Run HAProxy container
docker run -d \
  --name haproxy \
  -p 80:80 -p 443:443 -p 8404:8404 \
  -v $PWD/haproxy.cfg:/usr/local/etc/haproxy/haproxy.cfg:ro \
  --restart unless-stopped \
  haproxy:2.8

# Reload config without downtime
docker kill -s HUP haproxy

Configuration

Minimal Working Config (/etc/haproxy/haproxy.cfg)

global
    log /dev/log local0
    log /dev/log local1 notice
    chroot /var/lib/haproxy
    stats socket /run/haproxy/admin.sock mode 660 level admin expose-fd listeners
    stats timeout 30s
    user haproxy
    group haproxy
    daemon
    maxconn 100000
    ssl-default-bind-ciphers ECDH+AESGCM:DH+AESGCM:ECDH+AES256:DH+AES256
    ssl-default-bind-options ssl-min-ver TLSv1.2 no-tls-tickets
    tune.ssl.default-dh-param 2048

defaults
    log     global
    mode    http
    option  httplog
    option  dontlognull
    timeout connect  5s
    timeout client   50s
    timeout server   50s
    errorfile 400 /etc/haproxy/errors/400.http
    errorfile 502 /etc/haproxy/errors/502.http
    errorfile 503 /etc/haproxy/errors/503.http

frontend http_front
    bind *:80
    default_backend web_servers
    # Redirect to HTTPS
    redirect scheme https code 301 if !{ ssl_fc }

frontend https_front
    bind *:443 ssl crt /etc/haproxy/certs/
    default_backend web_servers

backend web_servers
    balance roundrobin
    option httpchk GET /health
    server web1 192.168.1.10:8080 check
    server web2 192.168.1.11:8080 check
    server web3 192.168.1.12:8080 check backup

Core Commands

CommandDescription
haproxy -f haproxy.cfg -cValidate configuration file
haproxy -f haproxy.cfgStart HAProxy with config
haproxy -sf $(cat /var/run/haproxy.pid)Graceful reload (keeps connections)
haproxy -st $(cat /var/run/haproxy.pid)Hard reload (terminates connections)
sudo systemctl reload haproxyGraceful reload via systemd
sudo systemctl restart haproxyFull restart via systemd
echo "show info" | socat stdio /run/haproxy/admin.sockShow runtime info
echo "show stat" | socat stdio /run/haproxy/admin.sockShow stats (CSV)
echo "show servers state" | socat stdio /run/haproxy/admin.sockShow server states
echo "disable server backend/server1" | socat stdio /run/haproxy/admin.sockDisable a backend server
echo "enable server backend/server1" | socat stdio /run/haproxy/admin.sockEnable a backend server
echo "set weight backend/server1 50" | socat stdio /run/haproxy/admin.sockSet server weight
echo "show table" | socat stdio /run/haproxy/admin.sockShow stick tables
echo "clear counters all" | socat stdio /run/haproxy/admin.sockReset all counters
hatop -s /run/haproxy/admin.sockInteractive terminal dashboard (requires hatop)

Advanced Usage

ACL-Based Routing

frontend http_front
    bind *:80

    # Define ACLs
    acl is_api path_beg /api/
    acl is_static path_end .css .js .png .jpg .gif .ico .woff .woff2
    acl is_websocket hdr(Upgrade) -i websocket
    acl is_mobile hdr(User-Agent) -i -m sub mobile android iphone
    acl host_admin hdr(Host) -i admin.example.com
    acl src_internal src 10.0.0.0/8 192.168.0.0/16

    # Apply routing based on ACLs
    use_backend api_servers    if is_api
    use_backend static_servers if is_static
    use_backend ws_servers     if is_websocket
    use_backend admin_backend  if host_admin src_internal
    default_backend web_servers

backend api_servers
    balance leastconn
    option httpchk GET /api/health
    http-check expect status 200
    server api1 192.168.1.20:8000 check
    server api2 192.168.1.21:8000 check

backend static_servers
    balance roundrobin
    server cdn1 192.168.1.30:80 check
    server cdn2 192.168.1.31:80 check

backend ws_servers
    balance leastconn
    timeout tunnel 1h
    option http-server-close
    server ws1 192.168.1.40:8080 check

Stick Tables for Session Persistence

backend web_servers
    balance roundrobin

    # Cookie-based persistence
    cookie SERVERID insert indirect nocache

    server web1 192.168.1.10:8080 check cookie s1
    server web2 192.168.1.11:8080 check cookie s2

# IP-hash persistence without cookies
backend sticky_servers
    balance source
    hash-type consistent
    server web1 192.168.1.10:8080 check
    server web2 192.168.1.11:8080 check

# Stick table for rate limiting
frontend http_front
    bind *:80
    stick-table type ip size 1m expire 10m store http_req_rate(10s),conn_cur,gpc0
    http-request track-sc0 src
    # Block if > 100 requests in 10s
    http-request deny if { sc_http_req_rate(0) gt 100 }
    # Block if > 50 concurrent connections
    http-request deny if { sc_conn_cur(0) gt 50 }
    default_backend web_servers

Health Checks

backend app_servers
    # HTTP health check
    option httpchk GET /health HTTP/1.1\r\nHost:\ example.com
    http-check expect status 200-399

    # Custom check interval
    default-server inter 3s fall 3 rise 2 slowstart 30s

    # TCP check only
    # option tcp-check

    # External check script
    # external-check path "/etc/haproxy/checks"
    # external-check command /etc/haproxy/checks/mycheck.sh

    server app1 192.168.1.10:8080 check
    server app2 192.168.1.11:8080 check
    server app3 192.168.1.12:8080 check weight 50
    server backup 192.168.1.13:8080 check backup

Rate Limiting and WAF-like Rules

frontend http_front
    bind *:80

    # Stick table for rate tracking
    stick-table type ip size 200k expire 30s store http_req_rate(30s),http_err_rate(30s),conn_rate(3s)
    http-request track-sc0 src

    # Rate limiting rules
    # Block bots hitting 404 too frequently
    acl too_many_errors sc_http_err_rate(0) gt 20
    # Block high connection rate
    acl too_many_connections sc_conn_rate(0) gt 100
    # Block too many requests per 30s
    acl too_many_requests sc_http_req_rate(0) gt 300

    http-request deny deny_status 429 if too_many_errors
    http-request deny deny_status 429 if too_many_connections
    http-request deny deny_status 429 if too_many_requests

    # Block bad User-Agents
    acl bad_ua hdr_sub(user-agent) -i curl wget python-requests nikto sqlmap
    http-request deny if bad_ua

    # Block direct IP access (require Host header)
    acl has_host hdr(host) -m found
    http-request deny if !has_host

    # Require specific header for API
    acl is_api path_beg /api/
    acl has_api_key hdr(X-API-Key) -m found
    http-request deny if is_api !has_api_key

    default_backend web_servers

TLS Termination and Certificate Management

global
    ssl-default-bind-ciphers ECDH+AESGCM:ECDH+AES256:ECDH+AES128:!aNULL:!MD5:!DSS
    ssl-default-bind-options ssl-min-ver TLSv1.2 no-tls-tickets
    tune.ssl.default-dh-param 4096

frontend https_front
    bind *:443 ssl crt /etc/haproxy/certs/ alpn h2,http/1.1
    # HSTS header
    http-response set-header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload"

    # SNI-based routing
    use_backend api_backend if { ssl_fc_sni api.example.com }
    use_backend admin_backend if { ssl_fc_sni admin.example.com }
    default_backend web_servers

# HTTP to HTTPS redirect
frontend http_redirect
    bind *:80
    redirect scheme https code 301

# Certificate bundle: cat cert.pem privkey.pem > /etc/haproxy/certs/example.com.pem

Statistics Dashboard

frontend stats
    bind *:8404
    # Restrict to internal IPs
    acl internal src 127.0.0.0/8 10.0.0.0/8 192.168.0.0/16
    http-request deny if !internal

    stats enable
    stats uri /stats
    stats refresh 10s
    stats show-legends
    stats show-node
    stats auth admin:strongpassword
    stats admin if TRUE
# Access stats page
curl -u admin:strongpassword http://localhost:8404/stats
# Or via browser at http://localhost:8404/stats

Lua Scripting

global
    lua-load /etc/haproxy/lua/rate_limit.lua

frontend http_front
    bind *:80
    http-request lua.rate_check
    default_backend web_servers
-- /etc/haproxy/lua/rate_check.lua
core.register_action("rate_check", { "http-req" }, function(txn)
    local src = txn.f:src()
    -- Custom Lua logic here
    txn:set_var("req.rate_ok", true)
end)

Common Workflows

Blue-Green Deployment

backend blue_backend
    server blue1 192.168.1.10:8080 check
    server blue2 192.168.1.11:8080 check

backend green_backend
    server green1 192.168.1.20:8080 check
    server green2 192.168.1.21:8080 check

frontend http_front
    bind *:80
    # Switch traffic via environment variable or ACL
    # Use HAProxy runtime API to switch:
    # echo "set map /etc/haproxy/maps/active_env.map / green" | socat ...
    use_backend green_backend if { var(txn.env) -m str green }
    default_backend blue_backend
# Switch to green deployment
echo "set map /etc/haproxy/active.map / green_backend" \
  | socat stdio /run/haproxy/admin.sock

# Drain blue servers
echo "set server blue_backend/blue1 state drain" \
  | socat stdio /run/haproxy/admin.sock

Canary Deployment (Traffic Splitting)

backend canary_backend
    balance roundrobin
    server canary 192.168.1.99:8080 check weight 10

backend stable_backend
    balance roundrobin
    server stable1 192.168.1.10:8080 check weight 90
    server stable2 192.168.1.11:8080 check weight 90

frontend http_front
    bind *:80
    # 10% traffic to canary, 90% to stable
    use_backend canary_backend if { rand(10) lt 1 }
    default_backend stable_backend

Draining a Server for Maintenance

# Gracefully drain connections from a server
echo "set server web_servers/web1 state drain" \
  | socat stdio /run/haproxy/admin.sock

# Wait for connections to drain, then disable
echo "set server web_servers/web1 state maint" \
  | socat stdio /run/haproxy/admin.sock

# Re-enable after maintenance
echo "set server web_servers/web1 state ready" \
  | socat stdio /run/haproxy/admin.sock

Monitoring and Alerting

# Parse stats via CLI
echo "show stat" | socat stdio /run/haproxy/admin.sock | \
  awk -F, '{print $1,$2,$18,$19,$20}' | column -t

# Check specific backend status
echo "show servers state web_servers" | socat stdio /run/haproxy/admin.sock

# Export Prometheus metrics (requires haproxy_exporter or Prometheus module)
# Built-in since HAProxy 2.0:
# Listen on :8405/metrics for Prometheus format

# Log analysis
grep 'web1' /var/log/haproxy.log | awk '{print $NF}' | sort | uniq -c

Tips and Best Practices

PracticeDetails
Always validate configRun haproxy -c -f haproxy.cfg before every reload
Use systemctl reloadPrefer reload over restart to avoid connection drops
Set maxconn appropriatelyDefault is often too low; tune based on available memory and file descriptors
Enable stats socketstats socket /run/haproxy/admin.sock is essential for runtime management
option forwardforAlways add to pass real client IP to backends via X-Forwarded-For header
option http-server-closePrefer over option forceclose; allows connection reuse to clients
Health check tuningSet inter, fall, rise in default-server for consistent health behavior
slowstart for warm-upUse slowstart 60s on backend servers to ramp traffic after restart
Stick table sizeSize stick tables with size directive based on expected unique clients
Separate SSL terminationConsider offloading TLS to HAProxy and using HTTP internally for simpler certs
tcp-check for non-HTTPUse TCP health checks for databases, message queues, and custom protocols
Log format customizationDefine log-format in defaults section to include response time and backend name