Prerequisites & VPS Selection
💡 Tip: HAProxy is extremely efficient. A single core can handle 20,000+ concurrent connections. Scale up for SSL termination or many backends.
Install HAProxy
Install HAProxy from the official repository for the latest stable version:
# Update system packages
sudo apt update && sudo apt upgrade -y
# Add HAProxy official repository (for latest version)
sudo apt install -y software-properties-common
sudo add-apt-repository ppa:vbernat/haproxy-2.8 -y
sudo apt update
# Install HAProxy
sudo apt install haproxy -y
# Verify installation
haproxy -v
# Check service status
sudo systemctl status haproxyFor RHEL/AlmaLinux/Rocky Linux:
# Install HAProxy
sudo dnf install haproxy -y
# Start and enable
sudo systemctl start haproxy
sudo systemctl enable haproxyBasic Configuration
HAProxy configuration is in /etc/haproxy/haproxy.cfg. Let's understand the structure:
# Backup original config
sudo cp /etc/haproxy/haproxy.cfg /etc/haproxy/haproxy.cfg.backup
# Edit configuration
sudo nano /etc/haproxy/haproxy.cfg#---------------------------------------------------------------------
# Global settings
#---------------------------------------------------------------------
global
log /dev/log local0
log /dev/log local1 notice
chroot /var/lib/haproxy
stats socket /run/haproxy/admin.sock mode 660 level admin
stats timeout 30s
user haproxy
group haproxy
daemon
# SSL settings
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
#---------------------------------------------------------------------
# Default settings
#---------------------------------------------------------------------
defaults
log global
mode http
option httplog
option dontlognull
timeout connect 5000
timeout client 50000
timeout server 50000
errorfile 400 /etc/haproxy/errors/400.http
errorfile 403 /etc/haproxy/errors/403.http
errorfile 408 /etc/haproxy/errors/408.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.httpLoad Balancing Configuration
Configure frontend (incoming) and backend (servers) for load balancing:
#---------------------------------------------------------------------
# Frontend: Accept incoming connections
#---------------------------------------------------------------------
frontend http_front
bind *:80
# Default backend
default_backend http_back
# Route based on domain (optional)
acl is_api hdr(host) -i api.example.com
acl is_app hdr(host) -i app.example.com
use_backend api_servers if is_api
use_backend app_servers if is_app
#---------------------------------------------------------------------
# Backend: Web servers pool
#---------------------------------------------------------------------
backend http_back
balance roundrobin
option httpchk GET /health
http-check expect status 200
# Backend servers
server web1 192.168.1.10:80 check
server web2 192.168.1.11:80 check
server web3 192.168.1.12:80 check backup
backend api_servers
balance leastconn
option httpchk GET /api/health
server api1 192.168.1.20:3000 check
server api2 192.168.1.21:3000 check
backend app_servers
balance roundrobin
server app1 192.168.1.30:8080 check
server app2 192.168.1.31:8080 checkLoad Balancing Algorithms
roundrobin
Distributes requests evenly across servers in order. Best for similar server capacities.
leastconn
Sends to server with fewest connections. Best for long-running connections.
source
Hashes client IP for consistent routing. Simple sticky sessions.
uri
Hashes URI for consistent routing. Good for caching layers.
Health Checks
Configure health checks to automatically remove unhealthy servers:
backend web_servers
balance roundrobin
# HTTP health check
option httpchk GET /health HTTP/1.1\r\nHost:\ localhost
http-check expect status 200
# Health check intervals
default-server inter 3s fall 3 rise 2
# Server definitions with health check options
server web1 192.168.1.10:80 check inter 2s fall 3 rise 2 weight 100
server web2 192.168.1.11:80 check inter 2s fall 3 rise 2 weight 100
server web3 192.168.1.12:80 check inter 2s fall 3 rise 2 weight 50
# TCP health check (for non-HTTP services)
backend mysql_servers
mode tcp
balance roundrobin
option mysql-check user haproxy
server mysql1 192.168.1.50:3306 check
server mysql2 192.168.1.51:3306 check backupHealth Check Parameters:
- • inter: Interval between checks (default 2s)
- • fall: Failed checks before marking down (default 3)
- • rise: Successful checks before marking up (default 2)
- • weight: Server weight for load distribution (1-256)
SSL Termination
Configure HAProxy to handle SSL certificates:
# Install Certbot
sudo apt install certbot -y
# Get certificate (standalone mode)
sudo certbot certonly --standalone -d example.com -d www.example.com
# Create combined PEM file for HAProxy
sudo cat /etc/letsencrypt/live/example.com/fullchain.pem \
/etc/letsencrypt/live/example.com/privkey.pem \
| sudo tee /etc/haproxy/certs/example.com.pem
# Set permissions
sudo chmod 600 /etc/haproxy/certs/example.com.pem#---------------------------------------------------------------------
# HTTPS Frontend with SSL termination
#---------------------------------------------------------------------
frontend https_front
bind *:443 ssl crt /etc/haproxy/certs/example.com.pem alpn h2,http/1.1
# Security headers
http-response set-header Strict-Transport-Security "max-age=31536000; includeSubDomains"
http-response set-header X-Frame-Options "SAMEORIGIN"
http-response set-header X-Content-Type-Options "nosniff"
default_backend http_back
# Redirect HTTP to HTTPS
frontend http_front
bind *:80
# ACME challenge for Let's Encrypt renewal
acl is_acme path_beg /.well-known/acme-challenge/
use_backend acme_backend if is_acme
# Redirect all other traffic to HTTPS
redirect scheme https code 301 if !is_acme
backend acme_backend
server acme 127.0.0.1:8888Auto-Renew SSL with HAProxy
sudo nano /etc/letsencrypt/renewal-hooks/post/haproxy.sh#!/bin/bash
DOMAIN="example.com"
cat /etc/letsencrypt/live/$DOMAIN/fullchain.pem \
/etc/letsencrypt/live/$DOMAIN/privkey.pem \
> /etc/haproxy/certs/$DOMAIN.pem
systemctl reload haproxysudo chmod +x /etc/letsencrypt/renewal-hooks/post/haproxy.shSticky Sessions
Ensure users stay on the same backend server:
backend app_servers
balance roundrobin
# Cookie-based persistence
cookie SERVERID insert indirect nocache
server app1 192.168.1.30:8080 check cookie app1
server app2 192.168.1.31:8080 check cookie app2
server app3 192.168.1.32:8080 check cookie app3backend app_servers
# Use source IP for persistence
balance source
hash-type consistent
server app1 192.168.1.30:8080 check
server app2 192.168.1.31:8080 checkbackend app_servers
balance roundrobin
# Stick table for session persistence
stick-table type ip size 200k expire 30m
stick on src
server app1 192.168.1.30:8080 check
server app2 192.168.1.31:8080 checkRate Limiting
Protect your servers from abuse with rate limiting:
frontend http_front
bind *:80
# Stick table for rate limiting
stick-table type ip size 100k expire 30s store http_req_rate(10s)
# Track requests per IP
http-request track-sc0 src
# Deny if more than 100 requests in 10 seconds
http-request deny deny_status 429 if { sc_http_req_rate(0) gt 100 }
# Add rate limit headers
http-response set-header X-RateLimit-Limit 100
http-response set-header X-RateLimit-Remaining %[sc_http_req_rate(0),sub(100)]
default_backend http_back
# Connection rate limiting
frontend http_front_connlimit
bind *:80
# Limit concurrent connections per IP
stick-table type ip size 100k expire 30s store conn_cur
http-request track-sc0 src
http-request deny deny_status 429 if { sc_conn_cur(0) gt 50 }Stats Dashboard
Enable the built-in statistics dashboard:
#---------------------------------------------------------------------
# Statistics Dashboard
#---------------------------------------------------------------------
frontend stats
bind *:8404
mode http
stats enable
stats uri /stats
stats refresh 10s
stats admin if LOCALHOST
# Authentication (strongly recommended)
stats auth admin:your_secure_password
# Hide HAProxy version
stats hide-version# Validate configuration
sudo haproxy -c -f /etc/haproxy/haproxy.cfg
# Restart service
sudo systemctl restart haproxy✅ Success! Access stats at http://YOUR_IP:8404/stats
⚠️ Security: Restrict stats access to trusted IPs or use a firewall. Never expose stats publicly without authentication.
Logging Configuration
sudo nano /etc/rsyslog.d/49-haproxy.conf# Create an additional socket in haproxy's chroot
$AddUnixListenSocket /var/lib/haproxy/dev/log
# Send HAProxy messages to a dedicated logfile
if $programname startswith 'haproxy' then /var/log/haproxy.log
&stop# Create log socket directory
sudo mkdir -p /var/lib/haproxy/dev
# Restart rsyslog
sudo systemctl restart rsyslog
# View logs
sudo tail -f /var/log/haproxy.logCustom Log Format
defaults
# Custom log format with timing info
log-format "%ci:%cp [%tr] %ft %b/%s %TR/%Tw/%Tc/%Tr/%Ta %ST %B %CC %CS %tsc %ac/%fc/%bc/%sc/%rc %sq/%bq %hr %hs %{+Q}r"
# Separate log for errors
option log-separate-errorsHigh Availability Setup
Set up HAProxy with Keepalived for automatic failover:
sudo apt install keepalived -yvrrp_script chk_haproxy {
script "killall -0 haproxy"
interval 2
weight 2
}
vrrp_instance VI_1 {
state MASTER
interface eth0
virtual_router_id 51
priority 101
advert_int 1
authentication {
auth_type PASS
auth_pass your_secret_password
}
virtual_ipaddress {
192.168.1.100/24
}
track_script {
chk_haproxy
}
}vrrp_script chk_haproxy {
script "killall -0 haproxy"
interval 2
weight 2
}
vrrp_instance VI_1 {
state BACKUP
interface eth0
virtual_router_id 51
priority 100
advert_int 1
authentication {
auth_type PASS
auth_pass your_secret_password
}
virtual_ipaddress {
192.168.1.100/24
}
track_script {
chk_haproxy
}
}sudo systemctl start keepalived
sudo systemctl enable keepalived
# Check virtual IP
ip addr show eth0Troubleshooting
Validate Configuration
# Check syntax
sudo haproxy -c -f /etc/haproxy/haproxy.cfg
# Check with verbose output
sudo haproxy -c -V -f /etc/haproxy/haproxy.cfgDebug Mode
# Stop service first
sudo systemctl stop haproxy
# Run in foreground with debug
sudo haproxy -d -f /etc/haproxy/haproxy.cfgCommon Issues
# Check if HAProxy is listening
sudo ss -tlnp | grep haproxy
# Check for port conflicts
sudo lsof -i :80
sudo lsof -i :443# Test backend directly
curl -v http://192.168.1.10:80/health
# Check HAProxy logs
sudo tail -100 /var/log/haproxy.log | grep -i errorRuntime Commands
# Show server status
echo "show stat" | sudo socat stdio /run/haproxy/admin.sock
# Disable a server
echo "disable server http_back/web1" | sudo socat stdio /run/haproxy/admin.sock
# Enable a server
echo "enable server http_back/web1" | sudo socat stdio /run/haproxy/admin.sock
# Show current sessions
echo "show sess" | sudo socat stdio /run/haproxy/admin.sock
# Graceful reload
sudo systemctl reload haproxyCongratulations!
You've successfully deployed HAProxy load balancer on your RamNode VPS. Your traffic is now being distributed across backend servers.
Security Best Practices
- ✅ Use strong SSL ciphers - Configure TLS 1.2+ with modern cipher suites
- ✅ Protect stats page - Use authentication and IP restrictions
- ✅ Enable rate limiting - Protect against DDoS and abuse
- ✅ Keep HAProxy updated - Apply security patches promptly
- ✅ Use firewall rules - Only allow necessary ports
- ✅ Monitor logs - Watch for suspicious patterns
