Load balancer
Why Akadata ships two load balancers
A load balancer distributes traffic across multiple backend servers so no single machine is overwhelmed, and a failure on one server does not take the service down. Akadata Linux ships HAProxy for Layer 7 (HTTP/TCP content switching) and ipvsadm for Layer 4 (kernel‑level packet forwarding). They complement each other rather than compete. Use HAProxy when you need TLS termination, header inspection, or path‑based routing. Use ipvsadm when you need raw throughput measured in millions of packets per second.
ldirectord, the health‑check daemon for LVS, is a candidate for a future stage4 package. Until it ships, health checks for ipvsadm are configured with external scripts (see below).
Layer 7
HAProxy — TCP and HTTP reverse proxy
HAProxy operates at the application layer. It terminates client connections, inspects headers, routes requests based on hostname or path, and opens new connections to backend servers. The configuration lives at /etc/haproxy/haproxy.cfg.
Quick start
rc-service haproxy start
rc-update add haproxyFrontend / backend pattern
Every HAProxy configuration has at least one frontend (where clients connect) and one backend (where requests are forwarded).
global
daemon
maxconn 4096
defaults
mode http
timeout connect 5s
timeout client 50s
timeout server 50s
frontend http-in
bind :80
default_backend web-servers
backend web-servers
balance roundrobin
server web1 10.0.0.1:80 check
server web2 10.0.0.2:80 check
server web3 10.0.0.3:80 checkHost‑based routing
frontend http-in
bind :80
acl is_app hdr(Host) -i app.akadata.ltd
acl is_api hdr(Host) -i api.akadata.ltd
use_backend app-servers if is_app
use_backend api-servers if is_api
default_backend fallbackPath‑based routing
acl is_static path_beg /static/
use_backend static-servers if is_static
acl is_upload path_beg /upload/
use_backend upload-servers if is_uploadTLS termination
frontend https-in
bind :443 ssl crt /etc/ssl/akadata/akadata.ltd.pem
default_backend web-serversCombine the certificate and private key into one .pem file. HAProxy handles the TLS handshake; traffic to the backend is plain HTTP unless you configure backend SSL.
TCP mode
frontend postgres-in
mode tcp
bind :5432
default_backend postgres-servers
backend postgres-servers
mode tcp
balance leastconn
server pg1 10.0.0.10:5432 check
server pg2 10.0.0.11:5432 checkUse mode tcp when HAProxy should not inspect the payload: database connections, raw TLS passthrough, or protocols it does not understand.
Health checks
backend web-servers
option httpchk GET /health HTTP/1.1\r\nHost: localhost
server web1 10.0.0.1:80 check inter 3s fall 2 rise 2
server web2 10.0.0.2:80 check inter 3s fall 2 rise 2inter 3s checks every three seconds. fall 2 marks the server down after two failed checks. rise 2 brings it back after two successful checks.
Stats dashboard
listen stats
bind :8404
mode http
stats enable
stats uri /
stats realm HAProxy\ Statistics
stats auth admin:<strong-random-password>Browse to http://your-server:8404/ for a live dashboard of frontends, backends, and per‑server metrics. Replace <strong-random-password> with a value generated by openssl rand -base64 32. Never publish a real credential in configuration files or documentation.
Reload without dropping connections
rc-service haproxy reloadMan pages: man haproxy. Full reference: docs.haproxy.org.
Layer 4
ipvsadm — the kernel load balancer
IPVS (IP Virtual Server) is built into the Linux kernel. The director receives all traffic for a VIP (virtual IP) and forwards packets to a pool of real servers. Because the forwarding happens in the kernel, not in userspace, throughput is limited by the NIC, not by CPU copy loops. A single director can saturate 10 Gbps with modest hardware.
ipvsadm operates in three modes. DR (direct server return) is the fastest and the one you should reach for first. NAT is simpler to configure but the director handles return traffic. Tunnel (IPIP) reaches across L2 boundaries.
DR mode
Direct Server Return
In DR mode the director receives the packet, leaves the source and destination IPs unchanged, and forwards it to the real server by rewriting only the destination MAC address. The real server has the VIP bound to its loopback interface so it accepts the packet. When it replies, the reply goes directly to the client. The director never sees the return traffic.
This is the fastest mode. The director handles only the incoming direction. Throughput is limited by the NIC line rate, not by CPU.
Director setup
# Assign the VIP to the director's public interface
ip addr add 198.51.100.10/32 dev eth0
# Add the virtual service and real servers (DR = -g for gateway)
ipvsadm -A -t 198.51.100.10:80 -s wrr
ipvsadm -a -t 198.51.100.10:80 -r 10.0.0.1:80 -g -w 1
ipvsadm -a -t 198.51.100.10:80 -r 10.0.0.2:80 -g -w 1
ipvsadm -a -t 198.51.100.10:80 -r 10.0.0.3:80 -g -w 2
# Verify
ipvsadm -L -nThe -g flag means gateway mode (DR). The -w sets the weight: real server 3 gets twice the traffic of servers 1 and 2.
Real server configuration — the /32 loopback trick
Every real server must have the VIP bound to its loopback interface as a /32 address. This makes the kernel accept packets destined for the VIP without responding to ARP requests for it, because ARP is answered by the director alone.
Linux
# Add the VIP to loopback
ip addr add 198.51.100.10/32 dev lo
# Suppress ARP responses for the VIP
sysctl -w net.ipv4.conf.all.arp_ignore=1
sysctl -w net.ipv4.conf.all.arp_announce=2
# Make it persistent across reboots
echo 'net.ipv4.conf.all.arp_ignore = 1' >> /etc/sysctl.d/99-vip.conf
echo 'net.ipv4.conf.all.arp_announce = 2' >> /etc/sysctl.d/99-vip.conf
# Bring the VIP up at boot via the local service
printf 'ip addr add 198.51.100.10/32 dev lo\n' > /etc/local.d/vip.start
chmod +x /etc/local.d/vip.start
rc-update add local defaultarp_ignore=1 answers ARP only if the target IP is on the incoming interface, which it never is for loopback. arp_announce=2 always uses the best local address when sending ARP, preventing the VIP from leaking into ARP broadcasts on the physical NIC.
Linux — arptables alternative
# Drop ARP replies that carry the VIP on the physical interface
arptables -A OUTPUT -d 198.51.100.10 -j DROP
arptables -A INPUT -d 198.51.100.10 -j DROParptables is stricter: it drops ARP packets containing the VIP regardless of the kernel's ARP policy. On nftables-based systems this is provided by arptables-nft; the rules are identical. Use it when arp_ignore and arp_announce are not sufficient, for example when multiple VIPs are in play and you need per‑address rules.
FreeBSD
# Add the VIP to loopback
ifconfig lo0 alias 198.51.100.10/32
# Suppress ARP
sysctl net.link.ether.inet.proxyall=0
echo 'net.link.ether.inet.proxyall=0' >> /etc/sysctl.confproxyall=0 prevents the kernel from proxying ARP for addresses on other interfaces.
OpenBSD / NetBSD
# Add the VIP to loopback
ifconfig lo0 inet alias 198.51.100.10/32
# Prevent ARP leakage
sysctl net.inet.ip.redirect=0
echo 'net.inet.ip.redirect=0' >> /etc/sysctl.confmacOS
# Add the VIP to loopback
sudo ifconfig lo0 alias 198.51.100.10/32
# macOS does not need explicit ARP suppression because
# loopback aliases are not advertised via ARP.macOS handles loopback aliases correctly by default. No sysctl changes are needed; the alias is known only to the local TCP/IP stack.
Windows
# Add the VIP to the loopback adapter
netsh interface ipv4 add address "Loopback" 198.51.100.10 255.255.255.255
# Use weak-host receive on the loopback so it accepts the VIP,
# and keep the physical interface as strong-host.
netsh interface ipv4 set interface "Loopback" weakhostreceive=enabled
netsh interface ipv4 set interface "Loopback" weakhostsend=enabled
netsh interface ipv4 set interface "Ethernet" weakhostreceive=disabled
# Drop source-routed packets (defense in depth)
netsh int ipv4 set global sourceroutingbehavior=drop
# The VIP is bound exclusively to the loopback adapter above,
# so the physical interface never answers ARP for it.On Windows, the loopback adapter typically has a name like Loopback Pseudo-Interface 1. Run netsh interface ipv4 show interfaces to find the correct index. Windows does not natively offer per‑address ARP suppression like Linux; the safest approach is to bind the VIP exclusively to the loopback adapter and let the director handle all ARP traffic.
NAT mode
In NAT mode the director rewrites the destination IP of every incoming packet from the VIP to the real server's address. The real server sees a normal connection from the client; the VIP does not need to be bound to its loopback. The real server sends its reply back to the director, which rewrites the source IP back to the VIP before forwarding to the client.
NAT is easier to configure than DR: no loopback tricks, no ARP suppression. But the director handles both directions of every connection, which limits throughput to the director's CPU and packet‑forwarding capacity.
# Director setup for NAT (-m = masquerade)
ipvsadm -A -t 198.51.100.10:80 -s wrr
ipvsadm -a -t 198.51.100.10:80 -r 10.0.0.1:80 -m -w 1
ipvsadm -a -t 198.51.100.10:80 -r 10.0.0.2:80 -m -w 1
# Enable IP forwarding (persist across reboots)
sysctl -w net.ipv4.ip_forward=1
echo 'net.ipv4.ip_forward=1' > /etc/sysctl.d/99-ipvsadm.conf
# NAT return traffic with nftables (matches the VPN firewall)
nft add table ip akadata_nat
nft add chain ip akadata_nat postrouting '{ type nat hook postrouting priority srcnat; policy accept; }'
nft add rule ip akadata_nat postrouting ip saddr 10.0.0.0/24 oifname "eth0" masqueradeReal servers in NAT mode use a normal default gateway pointing at the director. No special configuration is needed on them.
Tunnel mode (IPIP)
Tunnel mode encapsulates the original packet inside an IPIP header and sends it to the real server. The real server decapsulates it and replies directly to the client, like DR mode, but the director and real servers can be in different L2 domains, even different datacenters.
# Director (-i = IPIP tunnel)
ipvsadm -A -t 198.51.100.10:80 -s rr
ipvsadm -a -t 198.51.100.10:80 -r 203.0.113.1:80 -i
# Real server — enable IPIP tunnel, bind VIP to tunl0
modprobe ipip
# tunl0 is created automatically by the ipip module; configure it directly.
# (Do not 'ip tunnel add tunl0' — it already exists and would fail with EEXIST.)
ip link set tunl0 up
ip addr add 198.51.100.10/32 dev tunl0
# Allow decapsulated packets: disable reverse-path filtering on tunl0
sysctl -w net.ipv4.conf.tunl0.rp_filter=0
# Suppress ARP on the real server as with DR mode
sysctl -w net.ipv4.conf.all.arp_ignore=1
sysctl -w net.ipv4.conf.all.arp_announce=2Use tunnel mode when real servers are on different subnets or in different physical locations. The overhead of the IPIP header is 20 bytes per packet, which is negligible for most workloads.
Scheduling
Scheduling algorithms
ipvsadm offers ten scheduling algorithms. The -s flag sets the scheduler on the virtual service.
| Flag | Algorithm | Best for |
|---|---|---|
rr | Round Robin | Equal backends, stateless workloads |
wrr | Weighted Round Robin | Backends of different capacity: give bigger servers a higher weight |
lc | Least Connection | Long‑lived connections (SSH, WebSocket, database) |
wlc | Weighted Least Connection | Long‑lived connections with unequal backends (Akadata's default) |
sh | Source Hash | Session persistence: same client always hits the same real server |
dh | Destination Hash | Cache affinity: same destination always hits the same real server |
lblc | Locality‑Based Least Connection | Proxy caches where destination affinity matters |
lblcr | LBLCR with Replication | Proxy caches with hot‑standby failover |
sed | Shortest Expected Delay | Lowest expected wait time based on active connections and weight |
nq | Never Queue | Like SED but never queues to idle servers |
Advanced
Advanced ipvsadm
Connection persistence
# 300-second persistence - same client hits same real server for 5 minutes
ipvsadm -E -t 198.51.100.10:80 -s rr -p 300View the connection table
ipvsadm -L -n -cShows every active connection tracked by the director: protocol, state, source IP, VIP, real server. Essential for debugging sticky‑session issues or verifying that traffic is reaching the right backend.
Throughput statistics
ipvsadm -L -n --stats
ipvsadm -L -n --rate--stats shows total connections and bytes since the rule was created. --rate shows connections and bytes per second. Use this to monitor live throughput.
Zero‑downtime changes
# Add a real server — existing connections are unaffected
ipvsadm -a -t 198.51.100.10:80 -r 10.0.0.4:80 -g -w 1
# Change a real server's weight without dropping connections
ipvsadm -e -t 198.51.100.10:80 -r 10.0.0.1:80 -g -w 3
# Remove a real server — active connections to it will complete,
# new connections will not be sent to it
ipvsadm -d -t 198.51.100.10:80 -r 10.0.0.2:80ipvsadm edits are applied live. Connections already dispatched to a removed server will complete normally. New connections are dispatched only to the remaining servers.
Persisting rules across reboots
# Save current rules
ipvsadm-save -n > /etc/ipvsadm.rules
# Restore on boot — add to /etc/init.d/local or create a dedicated service
ipvsadm-restore < /etc/ipvsadm.rulesThe /etc/init.d/ipvsadm service automatically loads /etc/ipvsadm.rules if it exists. Save your rules there and the service will restore them on every boot.
Health checks
ldirectord (planned)
ldirectord is a daemon that monitors LVS real servers. It sends health checks (HTTP requests, TCP connects, or custom scripts) and, when a server fails, automatically removes it from the ipvsadm table. When the server recovers, ldirectord adds it back. It is a candidate for a future stage4 package; the configuration below is the upstream format it would use.
# Example configuration — /etc/ldirectord.cf
virtual = 198.51.100.10:80
real = 10.0.0.1:80 gate
real = 10.0.0.2:80 gate
real = 10.0.0.3:80 gate
scheduler = wrr
checktype = negotiate
request = "GET /health HTTP/1.1\r\nHost: localhost\r\n\r\n"
receive = "OK"
virtualhost = "akadata.ltd"Until ldirectord ships, the same effect comes from external health‑check scripts that call ipvsadm -d and ipvsadm -a when a server fails or recovers.
Decision
HAProxy or ipvsadm — which one?
| Criteria | HAProxy | ipvsadm DR | ipvsadm NAT |
|---|---|---|---|
| OSI layer | 7 (HTTP) / 4 (TCP) | 4 | 4 |
| Throughput ceiling | ~50 Gbps per instance | Line rate (kernel forwarding) | Director CPU‑bound (both directions) |
| TLS termination | Yes, native | No (pass‑through) | No (pass‑through) |
| Content switching | Host, path, header, cookie | No (L4 only) | No (L4 only) |
| Health checks | Built‑in (TCP, HTTP, script) | External (ldirectord or custom) | External (ldirectord or custom) |
| Real server config | None (it's a proxy) | Loopback VIP + ARP suppression | Default gateway → director |
| Best for | Web apps, APIs, TLS offload | High‑throughput HTTP, DNS, NTP, UDP services | Simple setups, small pools |
They are not exclusive. A common pattern: HAProxy handles TLS termination and routing at the edge, then forwards to an internal VIP managed by ipvsadm DR, which distributes across a large backend pool. HAProxy for intelligence, ipvsadm for scale.
References
Man pages and documentation
man ipvsadm— ipvsadm command referenceman haproxy— HAProxy configuration reference- docs.haproxy.org — HAProxy documentation
- linuxvirtualserver.org — Linux Virtual Server project
