1. The main configuration blocks #
The haproxy.cfg file is structured in several sections (Global, Defaults, Frontend, Backend, Listen, Program, Peers) that define the global behavior, the network topology and the cluster's state replication.
Global section #
The global section defines settings at the system process level (security, performance, logs). It applies to the whole HAProxy instance.
global
log /dev/log local0
maxconn 4096
user haproxy
group haproxy
daemonDefaults section #
The defaults section avoids repetition. The settings defined here (mode, timeouts, logs) are inherited by all the frontend and backend sections that follow.
defaults
log global
mode http
timeout connect 5s
timeout client 50s
timeout server 50sFrontend section #
The frontend defines how HAProxy receives requests. It specifies the listening IP address and port (bind), as well as the routing rules (ACLs) to the backends.
frontend http-in
bind *:80
acl is_static path_beg /static
use_backend static_servers if is_static
default_backend app_serversThe importance of default_backend: This directive defines the default backend to which HAProxy sends requests that match no ACL rule (use_backend). It is strongly recommended to always define one so that HAProxy does not reject requests with a 503 error when no routing condition is met.Backend section #
The backend defines the pool of destination servers. The syntax starts with the backend keyword followed by a unique name. To declare several backends (for example to separate API traffic from web traffic), simply define several blocks with distinct names.
backend app_servers
balance roundrobin
server web1 10.0.0.1:80 check
backend api_servers
balance leastconn
server api1 10.0.0.10:80 checkListen section #
The listen section is a shortcut that combines the features of a frontend and a backend in a single block. It is ideal for simple services such as exposing statistics or pure TCP proxying.
Note: Although convenient, this combined role is not ideal for complex applications made of multiple web domains and server pools. In those cases, merging the functions can needlessly complicate the configuration. We then recommend defining separate frontend and backend sections.
listen stats
bind *:8404
stats enable
stats uri /monitor
stats refresh 5sProgram section #
The program section lets HAProxy launch and monitor external processes (available since v2.2).
Deprecation note: The program feature has been deprecated since HAProxy 3.1 and will be removed in version 3.3. Until then, its behavior changes on reloads: the master process starts the program, but a worker process runs it. It is now recommended to use dedicated process managers such as Systemd, SysVinit, Supervisord or s6-overlays.
program my-agent
command /usr/local/bin/agent-script.sh
user haproxy
group haproxy2. Clustering and high availability: the peers section #
To eliminate any single point of failure (SPOF) and design a redundant architecture, HAProxy relies on clustering through the peers section.
The role of the peers mechanism #
In a highly available infrastructure (active-active or active-passive), several HAProxy instances share the load. To prevent a traffic failover from breaking session persistence or bypassing request limits, the instances must share their in-memory state.
The peers section lets you configure a cluster of HAProxy servers that replicate the content of their stick-tables in real time using a dedicated, ultra-fast binary protocol.
Peers clustering configuration example #
Each member (peer) of the cluster must be listed explicitly with its system host name and its exchange IP:port pair:
peers mon_cluster_ha
peer lb1 10.0.0.201:1024
peer lb2 10.0.0.202:1024Choosing the port and peers security rules #
By default, HAProxy has no standard or official port reserved by IANA for the peers protocol (binary stick-table synchronization). You must explicitly define the port of your choice when configuring the peers section.
However, in the industry and in the official HAProxy documentation, two ports come up almost systematically as de facto standards:
- 1024: the port most commonly used in official examples and HAProxy Technologies deployment guides.
- 1234: also very common in test architectures and community documentation.
Important security rule: The peers binary replication protocol has no authentication or encryption mechanism by default. You must therefore bind these ports on a private/isolated network (administration VLAN or synchronization network) or strictly filter access with firewall rules (iptables/nftables).
Linking a backend to the synchronized table #
To make security tracking or persistence highly available, add the peers keyword to the table declaration:
backend app_servers
balance roundrobin
# Table répliquée à chaud sur l'ensemble du cluster de pairs
stick-table type ip size 100k expire 30s store conn_cur peers mon_cluster_ha
# Limitation globale des connexions simultanées par IP sur le cluster
tcp-request content track-sc0 src
tcp-request content reject if { sc0_conn_cur gt 20 }
server web1 10.0.0.1:80 check
server web2 10.0.0.2:80 check3. Simplifying with includes #
How to organize a complex configuration across several files.
Using configuration directories #
Rather than having a single file of several thousand lines, HAProxy can load an entire directory. This is especially useful in automated environments (Ansible, Puppet) where each application can have its own configuration file.
When starting the service, use the -f argument pointing to a directory. HAProxy concatenates the files it finds in alphabetical order.
haproxy -f /etc/haproxy/haproxy.cfg -f /etc/haproxy/conf.d/Example production layout #
In production with many services, a numeric naming scheme is often used to control the loading order:
/etc/haproxy/
├── haproxy.cfg # Global, Defaults & Peers
└── conf.d/
├── 10-frontend-http.cfg
├── 11-frontend-https.cfg
├── 20-backend-ecommerce.cfg
├── 21-backend-blog.cfg
└── 99-stats.cfgThis structure lets you add or remove an application simply by managing a file in conf.d/, making automation through CI/CD easier.
4. Updates and hot reload #
Changing the configuration without interrupting traffic.
Zero-downtime reload #
HAProxy is designed for high availability. When you change the configuration, you can reload the service without losing active connections. The new process takes over the new sockets while the old one finishes processing in-flight requests.
# Vérifier la syntaxe avant de recharger
haproxy -c -f /etc/haproxy/haproxy.cfg
# Rechargement via systemd
systemctl reload haproxyReloading under Docker (SIGHUP) #
In a Docker environment, docker restart causes a hard cut because it kills the process. To perform a 'hitless' reload after changing your configuration file, send the SIGHUP signal to the container. The HAProxy master process will create a new process with the new configuration while letting the old one finish in-flight requests.
# Recharger la configuration sans redémarrer le conteneur
docker kill -s HUP my-running-haproxyThe Runtime API #
For ultra-dynamic changes (such as changing a backend's weight or putting a server into maintenance) without even reloading the file, HAProxy offers a Runtime API available through a Unix socket. Tools such as socat or the haproxy-cli utility let you interact with this API.
# Exemple : désactiver un serveur via la socket
echo "disable server app_servers/web1" | socat stdio /var/run/haproxy.stat
# Exemple : ajouter un nouveau serveur à chaud avec haproxy-cli
haproxy-cli exec "add server app_servers/web3 10.0.0.3:80 check"5. Security: DoS, IP filtering and WAF #
HAProxy is often the first line of defense of your infrastructure. It filters traffic and analyzes how dangerous it is before it reaches your application servers.
Cluster-level DoS protection #
Thanks to stick-tables and peers replication, DoS protection and volume tracking are no longer limited to a single physical instance but are shared across the entire cluster. If an abusive IP saturates load-balancing node 1, it is blocked on the fly on all nodes through synchronization.
frontend http-in
bind *:80
# Table de suivi synchronisée sur le cluster de pairs
stick-table type ip size 100k expire 30s store conn_rate(10s) peers mon_cluster_ha
# On suit l'IP source
tcp-request connection track-sc0 src
# On rejette si l'IP dépasse le seuil de requêtes cumulé sur le cluster
tcp-request connection reject if { sc0_conn_rate gt 100 }IP security: whitelisting and blacklisting #
IP filtering restricts access to sensitive resources or blocks known attackers.
- Whitelisting (allow list): block everything except authorized IPs (default deny strategy).
- Blacklisting (block list): allow everything except malicious IPs.
# Exemple de Whitelisting pour les stats
acl network_allowed src 127.0.0.1 172.20.0.5
http-request deny if !network_allowed
# Exemple de Blacklisting via un fichier externe
acl bad_guys src -f /etc/haproxy/blacklist.lst
http-request deny if bad_guysWeb Application Firewall (WAF) #
The WAF inspects request content (headers, body, parameters) to block attacks such as SQL injection or XSS (OWASP Top 10). HAProxy can integrate a WAF natively or delegate the analysis through the SPOE (Stream Processing Offload Engine) mechanism to an engine such as ModSecurity.
frontend http-in
# On délégue l'analyse à l'agent ModSecurity via SPOE
filter spoe engine modsecurity config /etc/haproxy/modsec.conf
# On bloque la requête (403) si l'agent identifie une menace
http-request deny deny_status 403 if { var(txn.modsec.code) -m int gt 0 }The modsec.conf file: Note that this file does not contain your security (WAF) rules. It is the configuration of the SPOE agent. It defines how HAProxy communicates with the external service: which messages to send (headers, IP, etc.), response timeouts, and which backend to use to reach the ModSecurity agent.
6. ACLs (Access Control Lists) #
ACLs are the heart of HAProxy's intelligence. They test conditions (URL, headers, IP) to make routing decisions.
Syntax and routing #
An ACL is defined by a name, a test method (fetch) and a value.
frontend http-in
# Teste si le chemin commence par /api
acl is_api path_beg /api
# Teste si le header Host contient 'admin'
acl is_admin hdr_dom(host) admin.mondomaine.com
# Actions basées sur les ACLs
use_backend api-servers if is_api
http-request deny if is_admin !network_allowed7. Observability and monitoring #
HAProxy offers native visibility into the health of traffic and servers.
Statistics dashboard and replication status #
The built-in web interface shows the state of backends and real-time throughput, as well as the network synchronization state and health of each peer in the cluster.
listen stats
bind *:8404
stats enable
stats uri /stats
stats refresh 5s
stats auth admin:password # Optionnel : authentificationPrometheus exposure #
HAProxy can expose its metrics natively so that a Prometheus server can collect them.
frontend stats_and_metrics
bind *:8404
# On expose les métriques sur l'URI /metrics
http-request use-service prometheus-exporter if { path /metrics }OpenTelemetry (OTel) support #
Since version 3.4, HAProxy can send traces directly to an OpenTelemetry collector for complete distributed tracing.
global
otel-exporter my-collector
endpoint otel-collector:4317
frontend http-in
# Envoi de la trace au collecteur
http-request otel-send-trace8. resolvers (dynamic DNS) #
The resolvers directive lets HAProxy dynamically resolve DNS names and follow their changes (scaling, Kubernetes, Docker, service discovery).
Basic structure #
The resolvers section defines a DNS server used by HAProxy to resolve dynamic host names.
resolvers dns
nameserver dns1 127.0.0.11:53
resolve_retries 3
timeout resolve 1s
timeout retry 1s
hold valid 10sUse with a backend #
Resolvers let HAProxy dynamically resolve DNS names and combine them with server-template to handle scaling automatically.
Why server-template? Unlike a static declaration, server-template automatically generates several backend instances (web-1, web-2, web-3) without manual configuration, which is essential with Docker/Kubernetes.
resolvers docker_dns
nameserver docker 127.0.0.11:53
hold valid 5s
backend app
server-template app 1-5 app.local:80 check resolvers docker_dns resolve-prefer ipv4Docker / service discovery #
In Docker, the internal DNS (127.0.0.11) dynamically resolves the containers created by scaling.
Why server-template? Docker guarantees neither a fixed IP nor a stable name. server-template lets HAProxy follow containers automatically without changing the configuration.
resolvers docker
nameserver docker 127.0.0.11:53
hold valid 10s
backend app
server-template web 1-10 web.local:80 check resolvers docker resolve-prefer ipv4Kubernetes / microservices #
In Kubernetes, pods change dynamically as the cluster scales.
Why server-template? Endpoints change constantly. server-template lets HAProxy adapt automatically without a reload or manual configuration.
resolvers kube_dns
nameserver kube 10.96.0.10:53
hold valid 10s
backend api
server-template api 1-20 api.default.svc.cluster.local:80 check resolvers kube_dns resolve-prefer ipv4DNS caching behavior #
The hold directives control how long DNS responses are cached.
- hold valid: validity period of a successful resolution
- hold nx: period for NXDOMAIN responses
- hold refused: period for DNS refusals
resolvers dns
nameserver dns1 8.8.8.8:53
hold valid 30s
hold nx 5s
hold refused 5sConclusion #
Power through modularity.
A structure ready for high availability #
Mastering the anatomy of HAProxy configuration, along with its clustering (peers) and state synchronization features, is the first step to building highly available infrastructures that are resilient to failures and suited to modern production requirements. The clear separation between listening (frontend) and processing (backend), combined with an active cluster of peers, offers industrial-grade flexibility.
