Building a Minimal HAProxy Load Balancer on Rocky Linux 10
A practical guide to building a small, hardened HAProxy load balancer on Rocky Linux 10 using Layer 4 TCP load balancing, TLS passthrough, multiple frontends and a separate DMZ interface. HAProxy is one of those tools that can look intimidating when you first open the configuration file. In reality, a basic Layer 4 load balancer can be surprisingly small and lightweight. I recently built a small, production-style HAProxy load balancer on a minimal Rocky Linux 10 installation. The server fronts several applications, including an application hosted in a DMZ, and passes HTTPS traffic through to the backend servers without terminating TLS. That last point is important. The HAProxy server doesn’t hold any certificates and doesn’t decrypt the application traffic. It simply accepts the TCP connection, selects a healthy backend and forwards the encrypted connection to it. TLS remains between the client and the application server. This guide documents the build from start to finish, including the networking, HAProxy configuration, SELinux, firewalld, logging, Active Directory integration, hardening, testing and — perhaps most importantly — the problems I encountered along the way. Everything in this article uses deliberately generic hostnames, IP addresses and account names. Replace them with values appropriate for your own environment. What we’re building The finished system is a single Rocky Linux 10 VM running HAProxy. Each application has its own frontend IP address and its own backend pool. HAProxy operates in TCP mode, so the encrypted HTTPS traffic is passed through without HAProxy terminating TLS. The certificates remain installed on the application servers. The basic design looks like this: The three application pools are: Pool Frontend Backends Production app 10.0.3.10:443 app-prod-01 (10.0.0.108), app-prod-02 (10.0.0.109) Test / API 10.0.3.11:443 app-test-01 (10.0.0.115), app-test-02 (10.0.0.124) DMZ web 10.0.16.251:443 web-dmz-01 (10.0.16.30), web-dmz-02 (10.0.16.31) Placeholder values Throughout this article I’ll use the following example values: Item Value Load balancer hostname lb01.corp.example.com Local fallback account localadmin AD domain corp.example.com Internal network 10.0.0.0/22 Internal gateway 10.0.3.254 DNS/NTP 10.0.0.11, 10.0.0.10 DMZ network 10.0.16.0/24 Admin workstation subnet 10.0.52.0/24 These are examples only. Don’t copy them into a production environment without adapting them to your own network. Why use TLS passthrough? There are two common ways of putting HAProxy in front of an HTTPS application. The first is TLS termination. HAProxy receives the HTTPS connection, decrypts it and then establishes another connection to the backend. The second is TLS passthrough. With passthrough, HAProxy doesn’t need to know anything about the application’s certificates. It operates at Layer 4 and simply proxies the TCP connection. For this particular deployment, TLS passthrough made sense for several reasons. No certificates on the load balancer The certificates remain on the application servers. That means there is another machine that doesn’t need certificate renewal, private-key protection or certificate deployment. Very low resource requirements HAProxy isn’t performing TLS encryption and decryption for client connections. It’s primarily moving TCP traffic between sockets. This makes it possible to run the load balancer on a very small VM. The downside: source IP visibility There is an important trade-off. From the backend server’s perspective, the TCP connection originates from the HAProxy server rather than directly from the client. HAProxy knows the original client address and can record it in its own logs, but the backend application won’t automatically see the client’s real source IP. If the backend needs that information, there are alternatives such as the PROXY protocol, provided the backend supports it, or terminating TLS on HAProxy instead. VM sizing The VM I used was deliberately small: For this type of workload, there’s little benefit in throwing a large amount of hardware at HAProxy. In my testing, HAProxy itself used only around 13 MB of RAM. Obviously, real resource requirements depend on connection counts, traffic levels, logging and configuration. Once you’re dealing with very large numbers of concurrent connections, memory and file descriptors become increasingly important, while network throughput can become the limiting factor. For this type of deployment, however, adding a second load balancer for availability is much more useful than simply making the first VM larger. One important virtualisation consideration Rocky Linux 10 and other RHEL 10-based distributions have newer x86-64 CPU requirements than some older Linux releases. If you’re installing the VM on an older hypervisor or with a deliberately old virtual CPU model, the installer may complain about unsupported CPU features. If that happens, expose a newer CPU model to the VM. On VMware, using the host CPU where appropriate is one option. Step 1 — Install Rocky Linux 10 Install Rocky Linux 10 using the Minimal Install profile. The goal here is to keep the server small. There is no reason to install a graphical desktop or a collection of applications that the load balancer doesn’t need. Once the operating system is installed, install the packages required for HAProxy and administration: If you’re not running VMware, open-vm-tools isn’t required. Enable the services: Check the HAProxy version: The build documented here used HAProxy 3.0.5 from AppStream. Your version may differ depending on the Rocky Linux repositories available at the time you build the server. Step 2 — Configure the network This server has two network interfaces: The internal interface carries the normal server traffic and hosts the production and test frontend addresses. The DMZ interface carries the DMZ frontend. The important thing here is that the DMZ interface must not become a second default route. Internal interface In this example, ens33 has two IP addresses: Configure the NetworkManager connection: Check that both addresses are actually present: Check the address before using it Before assigning an additional address to a server, make sure another device isn’t already using it. For example: No response doesn’t guarantee an address is unused, but it is a useful first check. Also check your DHCP scopes and IP address management system. An address that doesn’t answer an ARP request today can still be part of a DHCP scope and become a problem later. Configure the DMZ interface The second interface in this example is ens35. The DMZ network is:
Building a Minimal HAProxy Load Balancer on Rocky Linux 10 Read More »


