Caddy Load Balancing Tutorial: A Step-by-Step Guide to Balanced Server Workloads

Introduction to Caddy Load Balancing

In this tutorial, you will learn how to use Caddy as a web load balancer. We will be using three servers. The first server, called lb01, is going to act as the load balancer. The other two servers are going to act as the web servers. They are named web01 and web02. By the way, if you’re familiar with Vagrant and want to follow along, you can download the Vagrantfile at the end of this article.

Setting Up Your Environment with Vagrant

Vagrant allows you to quickly create virtual machines, which can act as local test and development environments. The Vagrantfile attached to this video will create the three servers we’ll be working with. There’s also a cheat sheet you can download that lists all the commands and configurations used in this video.

Understanding and Implementing Caddy Load Balancing

We will be building a load balancer that will accept requests from clients, routes those requests to a web server for processing, and sends back the results to the clients.

If a web server isn’t available or is down, the load balancer will send the request to a web server that is up and accepting connections and sends the resulting response back to the client.

In short, a load balancer distributes incoming network traffic across multiple servers, ensuring that no single server is overloaded, and also ensuring that each client gets a response from a server to handle their request.

You will often hear the server components of this architecture called “back-end” servers. You may also hear the back-end servers called “upstreams” as in the Caddy documentation.

Caddy Load Balancing Polices

Random Load Balancing

There are several load balancing options, or load balancing policies, for Caddy. The first is “random”. This algorithm selects a backend, or upstream, server randomly for each incoming request. When all the backend servers have the same specifications, this should distribute traffic fairly evenly, or at least in theory it should. If you use Caddy for load balancing, but do not specify a load balancing policy, it will use “random” as it is the default load balancing policy for Caddy.

Round-Robin Load Balancing

Another Caddy load balancing policy is round-robin, and it ensures requests to the backend servers are distributed in a round-robin fashion. If you have two web servers like we do in this case, the first request will go to the first web server, the second request will go to the second server, the third request will go to the first server, and so on.

Least Connected Load Balancing

The next option is least_conn or least connected. This algorithm sends each new request to the backend server with the least number of active connections. When the processing power and specifications of the backend servers vary, this policy can be useful, as it can help balance the workload more evenly.

Load Balancing by IP Address

The next load balancing policy we are going to cover is called ip_hash. This policy tells Caddy to use a hash function to determine what server should be selected for a request based on the client’s IP address. This means that all requests from a given IP address will be routed to the same backend server, and thus makes user sessions sticky. Subsequent requests from a specific IP address, will always be routed to the same backend server. This can be useful for applications that require session persistence.  On the other hand, if you are using a round-robin or least-connected load balancing policy, a particular client’s request will not be tied to a particular backend server.

First Available Policy

The final load balancing policy we are going to cover is “first”. This policy selects the first available backend server from a list of defined backend servers, starting with the first one listed in the configuration, then proceeding down the list, until an available backend server processes the request.

This policy can be useful in situations where you want to prioritize certain backend servers over others, such as when you have a primary backend server that you want to direct traffic to before any secondary or backup servers.

Other Load Balancing Policies

There are other policies such as uri_hash, header, and cookie. You can explore these lesser used options on your own if you wish.

Install Caddy on the Web Servers

curl -sLf 'https://dl.cloudsmith.io/public/caddy/stable/gpg.key' | sudo gpg --dearmor -o /usr/share/keyrings/caddy-stable-archive-keyring.gpg

In essence, this command is used to securely add the GPG key for the Caddy stable repository to your system’s trusted key rings. This is a crucial step in securely setting up a third-party repository on a Debian or Ubuntu system, as it allows the package manager, APT, to verify the authenticity and integrity of packages downloaded from this repository.

curl -sLf 'https://dl.cloudsmith.io/public/caddy/stable/debian.deb.txt' | sudo tee /etc/apt/sources.list.d/caddy-stable.list

The above command downloads the repository information for the Caddy stable software repository and adds it to the APT sources list under /etc/apt/sources.list.d/. This allows APT to check this new repository for software packages, which is useful when you want to install or update the Caddy web server using APT.

Now that we’ve added the Caddy repository to our list of available package sources, let’s tell the system to refresh the list of available packages.

sudo apt update

Now we can install caddy:

sudo apt install -y caddy

Let’s quickly see if it’s running.

ss -ntlp

By the way, you might be familiar with another command that acts in an almost identical fashion… and that command is netstat.  At this point, netstat is considered a legacy command and is being replaced in favor of the ss command. So, if you’re on an older distro you can run “netstat -ntlp” or on the latest distros you can run “ss -ntlp”.

In any case, you will see that Caddy is listening on Port 80, which is the default port for HTTP traffic. By default, the document root is in /usr/share/caddy, and I’m going to replace the contents of the index.html file in that directory with the name of the server.

uname -n | sudo tee /usr/share/caddy/index.html

Now, when we make a connection through the load balancer, we’ll be able to tell which backend web server we’re connecting to.

Let’s go ahead and perform the same steps on web02.

curl -sLf 'https://dl.cloudsmith.io/public/caddy/stable/gpg.key' | sudo gpg --dearmor -o /usr/share/keyrings/caddy-stable-archive-keyring.gpg

curl -sLf 'https://dl.cloudsmith.io/public/caddy/stable/debian.deb.txt' | sudo tee /etc/apt/sources.list.d/caddy-stable.list

sudo apt update

sudo apt install -y caddy

uname -n | sudo tee /usr/share/caddy/index.html

Install Caddy on the Load Balancer Server

curl -sLf 'https://dl.cloudsmith.io/public/caddy/stable/gpg.key' | sudo gpg --dearmor -o /usr/share/keyrings/caddy-stable-archive-keyring.gpg 

curl -sLf 'https://dl.cloudsmith.io/public/caddy/stable/debian.deb.txt' | sudo tee /etc/apt/sources.list.d/caddy-stable.list 

sudo apt update

sudo apt install -y caddy

Edit the configuration file, which is /etc/caddy/Caddyfile.

sudo vi /etc/caddy/Caddyfile

Use these contents:

:80 {
     reverse_proxy 192.168.56.51 192.168.56.52
}

One of the things I love about Caddy is that its configuration can be very simple. It can do a lot with very little configuration because of its well-chosen default settings. This simple configuration says, “accept all incoming requests on port 80 and forward them to, or proxy them to, either 192.168.56.51 or 192.168.56.52.” Note that we didn’t specify a load balancing policy. That means Caddy will use the “random” load balancing policy.

Testing Different Load Balancing Policies

Now that we have our new configuration in place, let’s reload Caddy so that it reads that configuration.

sudo systemctl reload caddy

Let’s see what happens when we start making requests to the load balancer:

curl localhost

curl localhost

curl localhost

Sometimes it will use web01 as the backend server, and sometimes it will use web02.

Of course, clients will actually be connecting to the public IP address of the load balancer, so let’s demonstrate that as well.

curl 192.168.56.50

curl 192.168.56.50

curl 192.168.56.50

This default random policy may very well be all you need when implementing a load balancer. With Caddy, and one simple line of configuration, your job just might be done. But let’s say you want to use a round-robin policy. What would that look like? Well, first, make a backup of the current configuration.

sudo cp /etc/caddy/Caddyfile /etc/caddy/Caddyfile.random

Now edit the configuration.

sudo vi /etc/caddy/Caddyfile

Use these contents:

:80 {   
     reverse_proxy 192.168.56.51 192.168.56.52 {
           lb_policy round_robin
     }
}

We’ve added a stanza for the reverse_proxy directive. In that stanza, we’ve told it to use the lb_policy of “round_robin”.  Again, this is a very simple configuration, and it makes Caddy such a pleasure to use.

Now, tell Caddy to re-reread its configuration:

sudo systemctl reload caddy

Let’s see what happens when we start sending requests to the load balancer.

curl 192.168.56.50

curl 192.168.56.50

curl 192.168.56.50

curl 192.168.56.50

You will see that the load balancer is sending one request to one backend server, the next request to the next server in the list, and so. So, the round-robin policy is working as expected.

Let’s backup this configuration and try yet another load balancing policy.

sudo cp /etc/caddy/Caddyfile /etc/caddy/Caddyfile.rr

sudo vi /etc/caddy/Caddyfile

Now let’s use the ip_hash policy.

Use these contents:

:80 {
     reverse_proxy 192.168.56.51 192.168.56.52 {
       lb_policy ip_hash
     }
}

Because we’ve changed Caddy’s configuration, we need to let Caddy know to reread it.

sudo systemctl reload caddy

Let’s make our initial request to the load balancer.

curl 192.168.56.50

The load balancer will route our request to either web01 or web02.  Because we are using the ip_hash policy, all of our future requests from this IP address will be routed to the same backend server. Let’s test it out.

curl 192.168.56.50

Ok, great. Our next request was also sent to the same backend, or upstream server. Let’s do many requests and see if this changes.

curl 192.168.56.50

curl 192.168.56.50

curl 192.168.56.50

If you need to tie a particular session with one backend server, then use the ip_hash load balancing policy.

Another great thing about Caddy is that it can automatically handle the registration and renewal of SSL certificates for free. You can use Caddy as a load balancer and SSL terminator.  I’ll save that for another post.

SUMMARY

In this tutorial, you learned:

  • How to use Caddy as a web load balancer to distribute incoming network traffic across multiple servers.
  • What the most commonly used load balancing policies are for Caddy.
  • Why and when to use each of the policies.
  • And finally, how to implement each load balancing policy.

Caddy Load Balancing Resources Download

If you want a copy of all the commands and configurations used in this lesson, be sure to download the files below.

Download Your Caddy Cheat Sheet

Includes Vagrantfiles, example Caddyfiles, and installation shell script.

We respect your privacy.