HAProxy can make your application unreachable if you configure it to listen on an IP address that doesn’t exist on any of your network interfaces.
Here’s how to set up HAProxy for high availability, breaking down its core components: frontend, backend, and listen directives.
Let’s start with a basic HAProxy configuration file (haproxy.cfg):
global
log /dev/log local0
log /dev/log local1 notice
maxconn 4000
user haproxy
group haproxy
daemon
defaults
log global
mode http
option httplog
option dontlognull
timeout connect 5000ms
timeout client 50000ms
timeout server 50000ms
frontend http_frontend
bind *:80
default_backend http_backend
backend http_backend
balance roundrobin
server app1 192.168.1.10:80 check
server app2 192.168.1.11:80 check
Frontend: The Public Face
The frontend section defines how HAProxy accepts incoming connections. Think of it as the reception desk of your application. It specifies the IP addresses and ports HAProxy will listen on.
In our example:
frontend http_frontend
bind *:80
default_backend http_backend
bind *:80: This tells HAProxy to listen on all available network interfaces (*) on port80. This is the standard port for HTTP traffic. You could also specify a specific IP address, likebind 192.168.0.100:80, if you only want HAProxy to listen on that particular interface.default_backend http_backend: This is a crucial directive. It tells HAProxy that any traffic arriving on this frontend should be directed to thehttp_backendfor processing. You can have multipledefault_backenddirectives or more complex rules usingacl(Access Control List) anduse_backendto route traffic to different backends based on request criteria.
Backend: The Workers
The backend section defines the group of servers that will actually handle the requests. These are your application servers. HAProxy will distribute the incoming traffic among these servers.
In our example:
backend http_backend
balance roundrobin
server app1 192.168.1.10:80 check
server app2 192.168.1.11:80 check
balance roundrobin: This specifies the load balancing algorithm.roundrobinis the simplest: HAProxy sends each incoming request to the next server in the list, cycling through them. Other common algorithms includeleastconn(sends requests to the server with the fewest active connections) andsource(hashes the source IP address to consistently send a client to the same server).server app1 192.168.1.10:80 check: This defines a specific backend server.app1: A descriptive name for the server.192.168.1.10:80: The IP address and port of the actual application server.check: This enables health checking for the server. HAProxy will periodically send a small request (e.g., an HTTP GET to/) toapp1on port80. If the server doesn’t respond within the configured timeout or returns an error, HAProxy will mark it as "down" and stop sending traffic to it until it becomes healthy again.
Listen: The Combined Approach
The listen directive is a shorthand that combines both frontend and backend configurations into a single block. It’s useful for simpler setups where you don’t need complex routing logic.
Here’s an equivalent configuration using listen:
listen http_listener
bind *:80
mode http
balance roundrobin
server app1 192.168.1.10:80 check
server app2 192.168.1.11:80 check
listen http_listener: Defines a listening service namedhttp_listener.- The directives within this block (
bind,mode,balance,server) are the same as you would find in separatefrontendandbackendsections. HAProxy treats this as a frontend that immediately directs traffic to the listed servers using the specified balancing method.
This listen block essentially says: "Listen on all interfaces, port 80, for HTTP traffic, and distribute it using round-robin to app1 and app2, checking their health."
The most surprising thing about HAProxy’s traffic distribution is that it can be incredibly granular, even down to individual HTTP headers or cookies, allowing for sophisticated routing that goes far beyond simple IP-based balancing.
Consider this scenario where you want to send requests for API version 1 to one set of servers and API version 2 to another:
frontend api_frontend
bind *:8080
acl api_v1 hdr(Host) -i api.example.com
acl api_v2 hdr(Host) -i api-v2.example.com
use_backend api_v1_backend if api_v1
use_backend api_v2_backend if api_v2
default_backend api_v1_backend # Fallback for requests not matching v2
backend api_v1_backend
server api1_v1 192.168.2.10:8000 check
server api2_v1 192.168.2.11:8000 check
backend api_v2_backend
server api1_v2 192.168.2.20:8000 check
server api2_v2 192.168.2.21:8000 check
Here, acl api_v1 hdr(Host) -i api.example.com creates a condition that is true if the incoming request’s Host header matches api.example.com (case-insensitive). use_backend api_v1_backend if api_v1 then directs traffic that satisfies the api_v1 ACL to the api_v1_backend. This level of control allows you to manage diverse application versions or services behind a single entry point.
One aspect of HAProxy’s health checking that often trips people up is the inter (interval) and fall/rise parameters. By default, HAProxy checks every 2 seconds (inter 2000ms), marks a server down after 3 failures (fall 3), and marks it back up after 2 successes (rise 2). If you have a server that’s slow to respond but fundamentally healthy, you might see it flapping between up and down states. Adjusting these values, for example server app1 192.168.1.10:80 check inter 5000ms fall 5 rise 3, can provide more stability by giving servers more time to recover before being marked as unhealthy, and requiring more consecutive successes to be considered healthy again.
The next step in mastering HAProxy is understanding how to implement SSL/TLS termination and manage certificate rotation.