How to Install and Configure HAProxy as a Load Balancer

Welcome to Fit Servers. If you are scaling your infrastructure, routing traffic efficiently is just as critical as the compute resources behind it. HAProxy remains the industry standard for high-performance TCP/HTTP load balancing.

It is lightweight, highly configurable, and capable of handling tens of thousands of concurrent connections.

In this technical guide, we will walk through the complete installation and configuration of HAProxy as a reverse proxy and load balancer on Ubuntu 24.04 LTS. We will cover backend setup, core routing, SSL termination, and enabling the statistics dashboard.

Architecture Overview

Before installing anything, it is important to understand where HAProxy sits in the network. The client does not directly connect to the backend servers. Instead, HAProxy receives the request, selects a healthy backend, and forwards the traffic.

For this tutorial, we will set up a standard three-node architecture using RFC 5737 documentation IP addresses.

Internet HTTP :80 / HTTPS :443 Load Balancer Node (HAProxy Server) 192.0.2.10 HTTP :80 HTTP :80 Bare-Metal Node 1 192.0.2.20 (Nginx) Bare-Metal Node 2 192.0.2.21 (Nginx)

Note: Replace the example IP addresses (192.0.2.x) with your actual network IPs.

Prerequisites

  • Three servers running Ubuntu 24.04 LTS.
  • Root or sudo privileges on all nodes.
  • Basic familiarity with the Linux command line and nano or vim.

Step 1: Prepare the Backend Web Servers

Before configuring the load balancer, we need backend servers to route traffic to. We will use Nginx for this example. Run these steps on both Backend 1 and Backend 2.

1. Install Nginx:

Bash
sudo apt update
sudo apt install nginx -y

2. Create a distinct index page for each server:

This allows us to visually verify that HAProxy is distributing traffic.

On Backend 1 (192.0.2.20):

Bash
echo "<h1>Welcome to Backend Server 1</h1>" | sudo tee /var/www/html/index.nginx-debian.html

On Backend 2 (192.0.2.21):

Bash
echo "<h1>Welcome to Backend Server 2</h1>" | sudo tee /var/www/html/index.nginx-debian.html

3. Secure the Backend Firewalls:

Your backend servers should only accept web traffic directly from the HAProxy load balancer. Block direct outside access to port 80.

Bash
# Allow SSH so you don't lock yourself out
sudo ufw allow OpenSSH
# Restrict HTTP traffic to ONLY the HAProxy IP
sudo ufw allow from 192.0.2.10 to any port 80
sudo ufw deny 80/tcp
sudo ufw enable

4. Ensure Nginx is running:

Bash
sudo systemctl enable --now nginx

Step 2: Install HAProxy on the Load Balancer

Switch to your Load Balancer node (192.0.2.10). Ubuntu 24.04 repositories include a stable version of HAProxy (2.8+), which is perfectly suitable for production environments and uses the modern configuration syntax.

1. Install HAProxy:

Bash
sudo apt update
sudo apt install haproxy -y

2. Verify the installation:

Bash
haproxy -v

Step 3: Configure HAProxy

The main configuration file is located at /etc/haproxy/haproxy.cfg. Before making changes, back up the default configuration.

Bash
sudo cp /etc/haproxy/haproxy.cfg /etc/haproxy/haproxy.cfg.bak

Open the configuration file in your text editor:

Bash
sudo nano /etc/haproxy/haproxy.cfg

Clear the existing content and paste the following Production-Ready Configuration block. This consolidated configuration includes modern health check syntax, security timeouts to prevent Slowloris attacks, and passes the true client IP to your backends.

HAProxy Config
global
    log /dev/log local0
    log /dev/log local1 notice
    maxconn 50000  # Tune based on server RAM (~17KB per connection)
    chroot /var/lib/haproxy
    user haproxy
    group haproxy
    daemon
    
    # Modern SSL tuning for 2026 standards
    ssl-default-bind-ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256
    ssl-default-bind-ciphersuites TLS_AES_128_GCM_SHA256:TLS_AES_256_GCM_SHA384
    ssl-default-bind-options ssl-min-ver TLSv1.2 no-tls-tickets

defaults
    log     global
    mode    http
    option  httplog
    option  dontlognull
    option  forwardfor       # Crucial: Passes real client IP to backends
    option  http-server-close
    timeout http-request 10s # Protects against Slowloris attacks
    timeout connect 5s
    timeout client 30s
    timeout server 30s
    errorfile 400 /etc/haproxy/errors/400.http
    errorfile 500 /etc/haproxy/errors/500.http
    errorfile 502 /etc/haproxy/errors/502.http
    errorfile 503 /etc/haproxy/errors/503.http
    errorfile 504 /etc/haproxy/errors/504.http

frontend http_front
    bind *:80
    # Optional: Redirect HTTP to HTTPS if you configure SSL later
    # http-request redirect scheme https unless { ssl_fc }
    default_backend web_servers

backend web_servers
    balance roundrobin
    
    # Modern HAProxy 2.8+ health check syntax
    option httpchk
    http-check send meth GET uri / ver HTTP/1.1
    http-check expect status 200

    # 'check' enables health monitoring. 'inter', 'fall', and 'rise' tune sensitivity.
    server web1 192.0.2.20:80 check inter 5s fall 3 rise 2
    server web2 192.0.2.21:80 check inter 5s fall 3 rise 2

# Secure Statistics Dashboard
listen stats
    bind *:8404
    mode http
    stats enable
    stats uri /stats
    stats refresh 10s
    stats auth admin:YourSecurePasswordHere
    # Optional: Restrict stats to a specific admin IP
    # http-request deny unless { src 198.51.100.50 }
Production Insight In this tutorial, we are checking the root directory (uri /) to see if the web server is awake. However, a web server can be running while the actual application behind it is broken. In a true production environment, configure your app to serve a dedicated /health endpoint that returns an HTTP 200 only when the database and app are fully functional, and point your http-check there.

Step 4: Validate and Restart HAProxy

Never restart a service without validating the configuration first. A syntax error will prevent HAProxy from starting, causing load balancer downtime.

1. Check configuration syntax:

Bash
sudo haproxy -c -f /etc/haproxy/haproxy.cfg

Expected output: Configuration file is valid

2. Restart and enable HAProxy:

Bash
sudo systemctl restart haproxy
sudo systemctl enable haproxy

Step 5: Configure the Load Balancer Firewall

Ensure your firewall allows traffic to the HAProxy frontend (Port 80) and the stats page (Port 8404). Run this on the HAProxy server:

Bash
sudo ufw allow 80/tcp
sudo ufw allow 8404/tcp
sudo ufw reload

Step 6: Test the Load Balancer

Now, we verify that HAProxy is correctly distributing traffic.

1. Test via Command Line:

From your local machine (or a separate test server), use curl to hit the Load Balancer's IP multiple times.

Bash
for i in {1..6}; do curl -s http://192.0.2.10; done

Expected Output:

Plaintext
<h1>Welcome to Backend Server 1</h1>
<h1>Welcome to Backend Server 2</h1>
<h1>Welcome to Backend Server 1</h1>
<h1>Welcome to Backend Server 2</h1>
<h1>Welcome to Backend Server 1</h1>
<h1>Welcome to Backend Server 2</h1>

The output should alternate perfectly between Server 1 and Server 2, confirming that the roundrobin algorithm is functioning.

2. Test the Stats Dashboard:

Open your web browser and navigate to: http://192.0.2.10:8404/stats

Log in using the credentials you set (admin / YourSecurePasswordHere). You will see a real-time dashboard showing queue sizes, response times, and the up/down status of your backend servers.

Step 7: Implementing SSL/TLS Termination (Optional but Recommended)

In a production environment, you should terminate SSL at the load balancer to offload cryptographic processing from your application servers.

1. Generate a combined PEM file:

HAProxy requires the certificate and private key to be combined into a single .pem file. If you are using Let's Encrypt, concatenate them:

Bash
sudo mkdir -p /etc/haproxy/certs
sudo cat /etc/letsencrypt/live/yourdomain.com/fullchain.pem /etc/letsencrypt/live/yourdomain.com/privkey.pem | sudo tee /etc/haproxy/certs/yourdomain.com.pem

(Ensure permissions are restricted: sudo chmod 600 /etc/haproxy/certs/yourdomain.com.pem)

2. Update the Frontend Configuration:

Modify your frontend http_front in /etc/haproxy/haproxy.cfg to bind to port 443 with SSL:

HAProxy Config
frontend http_front
    bind *:80
    bind *:443 ssl crt /etc/haproxy/certs/yourdomain.com.pem
    
    # Force HTTPS
    http-request redirect scheme https unless { ssl_fc }
    
    default_backend web_servers

3. Open port 443 and reload configuration:

Bash
sudo ufw allow 443/tcp
sudo systemctl reload haproxy

Step 8: Production Consideration - Avoiding a Single Point of Failure

A common mistake in new deployments is building the architecture we just created and stopping there. While this protects against an application node failure, HAProxy itself has now become a Single Point of Failure (SPOF).

If your primary HAProxy instance crashes, both backend servers can remain perfectly healthy, but users still won't be able to reach your site. For high-availability enterprise environments, you need two independent HAProxy nodes sharing a Virtual IP (VIP) using a tool like Keepalived:

Internet Public IP / VIP HAProxy Node 1 (Active) HAProxy Node 2 (Passive) Backend Server 1 Backend Server 2

Implementing this ensures that if HAProxy Node 1 goes offline, Node 2 instantly takes over the IP address, resulting in zero downtime.

Troubleshooting Common Issues

  • 503 Service Unavailable: HAProxy cannot reach the backend servers. Verify that the backend IPs are correct, Nginx is running on the backends, and the backend firewalls correctly allow traffic from the HAProxy IP.
  • Configuration fails to load: Always run sudo haproxy -c -f /etc/haproxy/haproxy.cfg after making changes. Check /var/log/syslog for specific syntax errors.
  • Backend stuck in "DOWN" state: Your health check URI might be returning a non-200 status code. Ensure http-check send is pointing to a valid endpoint that returns a 200 OK.

Need High-Performance Infrastructure?

Scaling a load balancer requires reliable, high-throughput networking. Whether you need a lightweight reverse proxy or a fully redundant multi-node cluster, our bare-metal servers provide the unmetered bandwidth and low latency required for mission-critical routing.

Explore Dedicated Servers