Introduction
Load balancing distributes incoming traffic across multiple backend servers, improving performance and providing redundancy. This tutorial sets up HAProxy on a Hostxpeed VPS to balance traffic between two or more web servers (VPS or other instances).
When Do You Need a Load Balancer?
Single VPS becomes insufficient when: CPU consistently >80% during peak, you need zero-downtime updates (rolling deployments), you want high availability (if one VPS fails, traffic goes to another), or you have geographically diverse users. Load balancer adds cost (another VPS) but enables horizontal scaling. Start with HAProxy on small VPS (NVME-1 sufficient) balancing 2-10 backend servers.
Architecture Overview
Components: HAProxy VPS (public IP) as frontend. Backend web servers (private IPs) handle requests. Options: Layer 4 (TCP) load balancing (faster, less inspection) or Layer 7 (HTTP) with content switching (URL-based routing). For web apps, Layer 7 recommended. Session persistence (sticky sessions) via cookies if your app stores session locally (use Redis for shared sessions).
Step 1: Provision Backend Web Servers
Create two or more VPS (same region recommended). Install Nginx/Apache and your app. Ensure they have private network interfaces (Hostxpeed provides private networking - enable in dashboard). Assign private IPs (e.g., 10.0.0.2, 10.0.0.3). Test application directly via private IPs. Configure them identically (same app version, same config). Use rsync or Git for sync.
Step 2: Install HAProxy on Frontend VPS
On a separate small VPS (NVME-1 works): sudo apt install haproxy -y. Enable HAProxy: sudo systemctl enable haproxy. Configuration file: /etc/haproxy/haproxy.cfg. Backup default: sudo cp /etc/haproxy/haproxy.cfg /etc/haproxy/haproxy.cfg.bak. We'll replace with custom config.
Step 3: Basic HAProxy Configuration
Edit /etc/haproxy/haproxy.cfg: global section: log /dev/log local0, maxconn 4096, user haproxy, group haproxy. defaults: mode http, log global, option httplog, option dontlognull, retries 3, timeout connect 5000, timeout client 50000, timeout server 50000. frontend web_frontend: bind *:80, default_backend web_servers. backend web_servers: balance roundrobin, server web1 10.0.0.2:80 check, server web2 10.0.0.3:80 check. Test config: sudo haproxy -f /etc/haproxy/haproxy.cfg -c. Restart: sudo systemctl restart haproxy.
Step 4: Health Checks and Failover
Add check parameter ensures HAProxy only sends traffic to healthy servers. Customize health check: option httpchk GET /health, http-check expect status 200. On backend, create /health endpoint returning 200. If one backend fails, HAProxy sends all traffic to remaining. When failed server recovers, HAProxy automatically adds it back. Also add backup server: server web3 10.0.0.4:80 backup (used only if all primary fail).
Step 5: Load Balancing Algorithms
Roundrobin (default) - distributes equally. Leastconn - sends to server with fewest connections (good for long-lived connections like WebSockets). Source - hash client IP for sticky sessions (but not needed if using Redis sessions). For API servers, roundrobin works well. For media servers, leastconn. Change by editing backend: balance leastconn.
Step 6: SSL Termination on HAProxy
To handle HTTPS, terminate SSL at HAProxy. Obtain certificate: sudo certbot certonly --standalone -d example.com. Combine cert and key: cat /etc/letsencrypt/live/example.com/fullchain.pem /etc/letsencrypt/live/example.com/privkey.pem > /etc/haproxy/example.com.pem. Then frontend bind *:443 ssl crt /etc/haproxy/example.com.pem. Redirect HTTP to HTTPS: frontend http: bind *:80, redirect scheme https code 301. Backend communication can be HTTP (since inside private network) or also HTTPS.
Step 7: Session Persistence (Sticky Sessions)
If your app stores sessions locally on each backend (not recommended), enable cookie persistence: backend web_servers: cookie SERVERID insert indirect nocache, server web1 10.0.0.2:80 cookie web1, server web2 10.0.0.3:80 cookie web2. Client browser stores cookie, always goes to same backend. Better approach: use Redis for session storage (shared across backends). Then no sticky needed. For WordPress with Redis object cache, works fine.
Step 8: Monitoring HAProxy
Enable stats page: listen stats bind *:8404, stats enable, stats uri /haproxy, stats refresh 10s, stats auth admin:yourpassword. Access http://your_lb_ip:8404/haproxy to see server status, traffic, queue. Also integrate with Prometheus: haproxy_exporter. Set up alerts if any backend down (integrate with Netdata). Monitor queue length - if >0, increase backend capacity.
Step 9: Scaling Backends (Add/Remove Servers)
To add more backends: edit haproxy.cfg, add server web3 10.0.0.4:80 check. Reload without dropping connections: sudo haproxy -f /etc/haproxy/haproxy.cfg -sf $(pidof haproxy). This graceful reload. For auto-scaling (cloud), use dynamic servers via API or consul template. Hostxpeed doesn't auto-scale, but you can script: create new VPS via API, configure app via ansible, then reload HAProxy config.
Step 10: High Availability for HAProxy Itself
Single HAProxy is a single point of failure. Set up two HAProxy VPS with floating IP (Keepalived). Both run HAProxy, but only active node has floating IP. If primary fails, secondary takes IP. Detailed in HAProxy article (Article 19 in Category 1). Or use DNS round-robin with multiple HAProxy IPs (less reliable). For most, single HAProxy with fast recovery is sufficient.
Real-World Example: Scaling WordPress
Three backend VPS (NVME-2 each) running Nginx+PHP-FPM+Redis, one database VPS (NVME-3) running MySQL. HAProxy (NVME-1) distributes load. Each backend has identical WordPress files (shared via NFS or git pull). Session persistence not needed (Redis object cache handles). Database is single point of failure; add replication later. This setup handles 200k visits/day comfortably.
Performance Tuning HAProxy
Set maxconn in global: 100000. Increase ulimit: ulimit -n 65535. Tune timeouts: timeout client 30s (lower than 50s). Enable HTTP keep-alive: option http-keep-alive. For static assets, serve directly from HAProxy with cache? Not recommended; use CDN. Use nbproc 4 (or nbthread 4) for multicore. Test with ab or wrk. HAProxy overhead minimal (<5% CPU). Bottleneck usually backend.
Troubleshooting Common Load Balancer Issues
Backend marked down: check health endpoint, firewall allowing HAProxy IP, backend overloaded (increase check timeout). Uneven distribution: check algorithm, server weights. Sticky sessions not working: cookie missing, path issue. SSL errors: certificate chain incomplete. HAProxy logs: /var/log/haproxy.log (configure rsyslog). Use tcpdump on HAProxy to inspect traffic.
Conclusion
HAProxy load balancer enables horizontal scaling and high availability. Start with two backends, test failover, then expand as traffic grows. Use Hostxpeed private networking for secure, low-latency backend communication. Monitor stats page for health. Combine with auto-scaling scripts for cloud-native architecture. HAProxy can also load balance databases, Redis, and other TCP services using mode tcp.