HAProxy can do more than just HTTP load balancing; it’s a beast at L4 (TCP) load balancing too, and it’s surprisingly simple to get going.
Let’s see HAProxy in action, balancing TCP traffic for a PostgreSQL cluster.
frontend pgsql_frontend
bind *:5432
mode tcp
default_backend pgsql_backend
backend pgsql_backend
mode tcp
balance roundrobin
server pgsql_node1 192.168.1.10:5432 check
server pgsql_node2 192.168.1.11:5432 check
server pgsql_node3 192.168.1.12:5432 check
This configuration does something counterintuitive: it steers clients to a specific PostgreSQL server based on a simple round-robin algorithm, without ever inspecting the SQL queries themselves. The magic is that HAProxy operates at Layer 4, just shuffling raw TCP packets.
Here’s the breakdown:
-
frontend pgsql_frontend: This section defines where HAProxy listens for incoming connections.bind *:5432: HAProxy will listen on all network interfaces (*) on port5432, the default PostgreSQL port.mode tcp: This is the crucial part. It tells HAProxy to operate at the TCP level, not the HTTP level. It won’t parse or understand the application protocol.default_backend pgsql_backend: All traffic hitting this frontend will be sent to thepgsql_backend.
-
backend pgsql_backend: This section defines the pool of servers that will handle the traffic.mode tcp: Again, specifies TCP mode for the backend.balance roundrobin: HAProxy will distribute incoming connections to the servers in the backend pool sequentially. The first connection goes topgsql_node1, the second topgsql_node2, and so on.server pgsql_node1 192.168.1.10:5432 check: This defines a backend server.pgsql_node1: A human-readable name for the server.192.168.1.10:5432: The IP address and port of the actual PostgreSQL server.check: HAProxy will periodically send a simple TCP SYN packet to this server to verify it’s alive and responsive. If it doesn’t get a SYN-ACK back within a short timeout, it marks the server as down and stops sending traffic to it.
This setup is incredibly powerful for protocols where you don’t need or can’t do application-level inspection. Think databases, SSH, or any custom TCP service. HAProxy just becomes a dumb pipe, reliably directing traffic based on connection counts or simple health checks.
The real power of mode tcp is its simplicity and speed. Because HAProxy isn’t parsing application data, it has much lower overhead. It’s essentially just managing TCP connections and forwarding packets. This makes it extremely efficient for high-volume, low-latency TCP traffic.
What many people don’t realize is that HAProxy’s check directive for TCP mode isn’t just a simple ping. By default, it performs a TCP connection check. It sends a SYN to the server’s port and expects a SYN-ACK. If it receives that within the configured timeout (default is 2 seconds), the server is considered healthy. If it gets a RST or no response, it’s marked unhealthy. This is usually sufficient for basic health checks, but you can configure more advanced checks using tcp-check if needed, though that starts to blur the lines back towards application-level awareness.
When you’re using balance roundrobin in TCP mode, HAProxy simply keeps a counter for each server in the backend. When a new connection comes in, it sends it to the server with the lowest current connection count. Once that connection is established, HAProxy essentially proxies the raw TCP packets between the client and the chosen server. It doesn’t inspect, modify, or even know what data is flowing through.
The next step is often to explore HAProxy’s more advanced TCP balancing algorithms like leastconn, which sends new connections to the server with the fewest active connections, or to implement more sophisticated health checks.