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:
Install HAProxy on Debian or Ubuntu using the system’s package manager.
Run the Enterprise version of HAProxy in AWS or Azure. (This option is not free, but it will give you access to features like the HAProxy Enterprise Bot Management Module and the next-generation HAProxy Enterprise WAF).
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 |
| Listen on all IP addresses assigned to this server at port 80. |
| The same as specifying 0.0.0.0 for the address. |
| Listen on ports 80 and 8080. (Do not add a space between ports) |
| 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 |
| Route API requests. |
| Route requests for images. |
| Routes requests for the domain example.local. |
| Route requests originating from the given IP address range. |
| Route POST and PUT requests. |
| 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.