HAProxy is famous as an HTTP load balancer, but it’s equally good at layer 4 (TCP). In TCP mode it forwards raw connections without understanding the protocol inside. That’s ideal for databases, Redis, mail, game servers, MQTT, or TLS traffic you don’t want to decrypt. This guide covers TCP-mode setups with health checks, SNI-based routing and the PROXY protocol.
For HTTP load balancing, see HAProxy load balancer.
Install
sudo apt install haproxy # Debian/Ubuntu
sudo dnf install haproxy # RHEL family
haproxy -v
Config: /etc/haproxy/haproxy.cfg. Validate before reloading:
sudo haproxy -c -f /etc/haproxy/haproxy.cfg && sudo systemctl reload haproxy
Basic TCP proxy
Forward port 5432 on this host to a PostgreSQL server on a private network:
defaults
mode tcp
log global
option tcplog
timeout connect 5s
timeout client 1h
timeout server 1h
frontend pg_in
bind 10.0.0.5:5432
default_backend pg_servers
backend pg_servers
server db1 10.0.0.12:5432 check
Long timeout client and timeout server values matter for database connections, which can sit idle in connection pools. HTTP-style short timeouts would cut them.
Load balancing with health checks
backend redis_replicas
balance roundrobin
option tcp-check
tcp-check connect
server r1 10.0.0.21:6379 check inter 2s fall 3 rise 2
server r2 10.0.0.22:6379 check inter 2s fall 3 rise 2
checkenables health checks.interis the interval,fall/riseare the failures and successes needed to change state.balance roundrobin,leastconn(good for long-lived connections) orsource(sticky by client IP).
Protocol-aware checks
Simple TCP checks only prove the port is open. HAProxy can do better.
Redis: only send traffic to the master:
backend redis_master
option tcp-check
tcp-check send PING\r\n
tcp-check expect string +PONG
tcp-check send info\ replication\r\n
tcp-check expect string role:master
tcp-check send QUIT\r\n
server r1 10.0.0.21:6379 check
server r2 10.0.0.22:6379 check
MySQL: option mysql-check user haproxy (create a passwordless haproxy user allowed only from the HAProxy IP, with no privileges).
PostgreSQL: option pgsql-check user haproxy. For primary/replica awareness, an HTTP check against Patroni’s REST API (option httpchk GET /primary on port 8008) is the common pattern.
See database replication and PgBouncer.
SNI routing: TLS passthrough to different backends
Route TLS connections by hostname without decrypting them. Each backend keeps its own certificates:
frontend tls_in
mode tcp
bind :443
tcp-request inspect-delay 5s
tcp-request content accept if { req_ssl_hello_type 1 }
use_backend app1 if { req_ssl_sni -i app1.example.com }
use_backend app2 if { req_ssl_sni -i app2.example.com }
default_backend app1
backend app1
mode tcp
server s1 10.0.0.31:443 check
backend app2
mode tcp
server s2 10.0.0.32:443 check
This is useful when several services share one public IP and port 443, but must terminate TLS themselves (for example a mail web UI, or apps managing their own certificates).
Preserving the client IP: PROXY protocol
In TCP mode, backends see HAProxy’s IP as the client. The PROXY protocol prepends the original client address to the connection:
backend web
mode tcp
server s1 10.0.0.31:443 check send-proxy-v2
The backend must expect it, or it sees garbage. In Nginx:
server {
listen 443 ssl proxy_protocol;
set_real_ip_from 10.0.0.5;
real_ip_header proxy_protocol;
}
Postfix, Dovecot, many game servers and HAProxy itself support it too.
Stats and monitoring
listen stats
mode http
bind 127.0.0.1:8404
stats enable
stats uri /stats
stats refresh 10s
Open it through an SSH tunnel (ssh -L 8404:127.0.0.1:8404 host). It shows backend states, connections and errors. HAProxy also exposes Prometheus metrics. See Prometheus and Grafana.
High availability for HAProxy itself
One HAProxy is a single point of failure. Run two, with a floating IP managed by keepalived (VRRP). See keepalived floating IP.
Security
- Bind frontends to private addresses where only internal clients connect (
bind 10.0.0.5:5432), and firewall them. - Never expose database frontends publicly. See check open ports.
- Use
maxconnlimits per frontend or server to protect backends.
Quick reference
| Task | Config |
|---|---|
| TCP mode | mode tcp |
| Long-lived connections | timeout client/server 1h |
| Health check | server … check inter 2s fall 3 rise 2 |
| Least connections | balance leastconn |
| SNI routing | req_ssl_sni + tcp-request inspect-delay |
| Client IP to backend | send-proxy-v2 + backend proxy_protocol |
| Validate | haproxy -c -f /etc/haproxy/haproxy.cfg |
Related: Nginx load balancing and port forwarding.