HAProxy Configuration Basics: Load Balance Your Servers

Learn how to set up basic load balancing using the HAProxy configuration file.

If you’re new to using the HAProxy load balancer, you’ve come to the right place. In this blog post, you’ll learn how to configure HAProxy for basic load balancing. I am assuming that you’ve already installed the software. If not, there are several ways to do so:

Now that you have HAProxy installed, let’s see how to configure it. All settings are defined in the file /etc/haproxy/haproxy.cfg (or /etc/hapee-{version}/hapee-lb.cfg for HAProxy Enterprise). If you are using Docker, then this file is mounted as a volume into the container at the path /usr/local/etc/haproxy/haproxy.cfg.

Before learning how to use this file, let’s consider what we are trying to achieve. To answer that, we have to first ask, what is a load balancer?

What is a Load Balancer?

HAProxy is a load balancer. Not familiar with the term? A load balancer helps you handle more web traffic and avoid downtime. A load balancer receives traffic from the Internet (or from your internal network, if we’re talking about load balancing an internal service) and then forwards that traffic to your web server.

The benefits of using a load balancer are realized once you’ve deployed multiple web servers. The load balancer can then relay traffic to each of them, allowing you to grow your capacity to serve more clients without asking those clients to connect to each server directly. HAProxy receives the traffic and then balances the load across your servers. This technique hedges against any one of your servers failing since the load balancer can detect if a server becomes unresponsive and automatically stop sending traffic to it. You can use HAProxy to balance the traffic to any number of web applications using a single configuration file.

Caveat! To guarantee truly reliable service, you must run at least two instances of HAProxy in an active-active or active-standby setup. Learn how to do that with HAProxy Enterprise by reading the official docs. For now, we’ll stick to using a single instance of HAProxy.

When configuring HAProxy, you typically start with the following three goals:

  • Decide which IP addresses and ports HAProxy should bind to for receiving traffic;

  • Define pools of servers to which HAProxy will relay traffic;

  • Set rules for edge cases, such as when you want a client’s request to go somewhere other than the normal pool of servers.

Read More: What is Load Balancing?

Set the Listening IP Address and Port

There are lots of things you can do with HAProxy: enable TLS, set rate limits, cache responses, reject malicious requests, modify HTTP headers, handle CORS, authenticate users, and many other tasks, but let’s start simple.

To define the IP address and port at which HAProxy should receive traffic, add a frontend section to your haproxy.cfg file. Inside this section, add a bind line. A bind line sets the IP address and port to listen on.

Your configuration file now contains the following and nothing else:

defaults
mode http
timeout client 10s
timeout connect 5s
timeout server 10s
timeout http-request 10s
frontend myfrontend
bind 127.0.0.1:80

This example also includes a defaults section, which defines settings that are shared across all sections that follow. It sets timeouts for how long HAProxy should wait for a client to send data (timeout client), how long to wait when trying to connect to a backend server (timeout connect), how long to wait for the server to send back data (timeout server), and how long to wait for the client to send a complete HTTP request (timeout http-request). These are just good safety measures that will help avoid common problems.

The mode sets how HAProxy treats requests. It can be either http or tcp, with the former used when working with HTTP web applications. In this mode, HAProxy is able to inspect metadata of an HTTP request, such as headers, cookies, and the URL path, when deciding how to route the request.

Restart HAProxy:

$ sudo systemctl restart haproxy

The load balancer is now listening on the localhost address, 127.0.0.1, at port 80. If you make a request using curl, you’ll get back a response:

$ curl 127.0.0.1:80
<html><body><h1>503 Service Unavailable</h1>
No server is available to handle this request.
</body></html>

Granted, there’s no reply from a server because we haven’t configured any servers yet. Nevertheless, you can see that HAProxy is functional. The table below lists a few other ways that you could set the bind line:

Bind line

What it does

bind 0.0.0.0:80

Listen on all IP addresses assigned to this server at port 80.

bind :80

The same as specifying 0.0.0.0 for the address.

bind :80,:8080

Listen on ports 80 and 8080. (Do not add a space between ports)

bind :6379-6390

Listen on all the ports between 6379 and 6390.

Next, let’s add a pool of servers to route requests to.

Define a Backend

In HAProxy, a frontend receives traffic before dispatching it to a backend, which is a pool of web or application servers. One of the servers in the backend will receive the request, form a response, and then send the response back through HAProxy to the client.

First, let’s run a small web application. Open a new terminal window on your HAProxy server and run this command, which starts a Python-based web application that listens at port 8000:

$ python3 -m http.server 8000 --bind 127.0.0.1
Serving HTTP on 0.0.0.0 port 8000 (http://127.0.0.1:8000/) ...

In your HAProxy configuration, below the frontend section, add a backend section that contains a single server. Also, add a default_backend line to your frontend that points to this new backend. Your file now looks like this:

defaults
mode http
timeout client 10s
timeout connect 5s
timeout server 10s
timeout http-request 10s
frontend myfrontend
bind 127.0.0.1:80
default_backend myservers
backend myservers
server server1 127.0.0.1:8000

Make the same curl request as before and you’ll get a response from the Python application (truncated for space).

$ curl 127.0.0.1:80
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
<html>
...
</html>

The request went through HAProxy. To get a real feel for it, run more Python web applications on different ports and then add them to the backend:

frontend myfrontend
bind 127.0.0.1:80
default_backend myservers
backend myservers
server server1 127.0.0.1:8000
server server2 127.0.0.1:8001
server server3 127.0.0.1:8002

With each new request that you make to port 80, HAProxy receives it and selects a server to handle it. HAProxy rotates through the list of servers, relaying the next request to the next server in line. You are now balancing the load across these servers.

Set Rules for Edge Cases

Suppose you wanted to send all requests that arrive on port 80 to servers 1 and 2, but if a request arrives at port 81, it should go to server 3. Consider the following configuration that achieves that:

frontend myfrontend
bind 127.0.0.1:80,127.0.0.1:81
use_backend special if { dst_port 81 }
default_backend myservers
backend myservers
server server1 127.0.0.1:8000
server server2 127.0.0.1:8001
backend special
server server3 127.0.0.1:8002

Here, I’ve moved server3 into its own backend section named special. In the frontend, I’ve added a use_backend line that checks whether the destination port requested by the client is 81. If it is, then the request goes to the special backend. Because I want the frontend to listen on both ports 80 and 81 simultaneously, I’ve separated those addresses with a comma. I could also have written a second bind line, like this:

frontend myfrontend
bind 127.0.0.1:80
bind 127.0.0.1:81
use_backend special if { dst_port 81 }
default_backend myservers

Either syntax is acceptable and they have the same result.

The condition that checks the destination port is surrounded by curly braces. Such conditions in HAProxy are called ACLs. The table below lists examples of other ACLs that you might use to route traffic to different backends. Before you can use some of them, you will need to set mode http in your frontend and backend sections, which allows HAProxy to evaluate HTTP data in a message.

ACL

Description

if { path_beg /api/ }

Route API requests.

if { path_end .jpg .png }

Route requests for images.

if { hdr(host) -m dom example.local }

Routes requests for the domain example.local.

if { src 127.0.0.1/8 }

Route requests originating from the given IP address range.

if { method POST PUT }

Route POST and PUT requests.

if { url_param(region) europe }

Route requests that have a URL parameter named region that is set to europe.

Learn more about ACLs in our blog post, Introduction to HAProxy ACLs.

Conclusion

That should be enough to get started using HAProxy as a load balancer. Once you’ve got the hang of it, read up on some other common tasks, such as:

HAProxy Enterprise powers modern application delivery at any scale and in any environment, providing the utmost performance, observability, and security for your critical services. Organizations harness its cutting edge features and enterprise suite of add-ons, which are backed by authoritative, expert support and professional services. Ready to learn more? Sign up for a free trial.

Want to know when more content like this is published? Subscribe to our blog or follow us on Twitter. You can also join the conversation on Slack.

Subscribe to our blog. Get the latest release updates, tutorials, and deep-dives from HAProxy experts.