How to write good status pages for load Balancers

Every now and then we are asked about special topics for which a lot of documentation and general advice can be found online. But sometimes there is simply a lack of guidance how to apply these things in the real world ...
Dealing with load balancers and the required status page in your web application is such a topic. In this article I will demonstrate the most simplistic approach for a status page.

What does a load balancer do?

So why would we need a load balancer and what does a load balancer do?
For most people it all started with a web application that got more popular over time and can no longer handle the concurrent load, aka: the number of web clients accessing the application at the same time. So instead of adding resources (CPU, RAM, etc.) to the existing server or buying a larger server, both would be considered scaling vertically, it is often less expensive and a lot more flexible for daily operations to distribute load between several smaller servers running the same web application. This is what a load balancer does and this approach is called scaling horizontally. Now patching, updating, or rebooting one server is possible without affecting the service as a whole, because other servers would still be available during maintenance work on that one server.

To tell if an instance (aka: a web application running on a server) is available, a load balancer needs to check for the status of that instance on a regular basis. This is called a health check and happens e.g. every 10, 30, or 60 seconds, depending on the load balancer and the application itself. Requests from clients are only forwarded to instances considered to be healthy. On the other hand, instances considered to be unhealthy (offline, not available, etc.) do not get any requests from clients until they show up as healthy again.

Getting started

So as software developers our task is to implement a status page that accepts request from the load balancer and reports the health status of our application? Sounds easy! Let us take a look at the challenges for our status page:

  1. It must report the availability of our application from a technical point of view. The application must be up and running, which means it must be ready to handle requests from web clients. Therefore all backend systems used by our application must be available, too. This includes e.g. databases and message brokers.
  2. It must not perform lengthy or performance intense operations. The idea is to report back to the load balancer as fast as possible to allow a quicker response to status changes and potentially even allow more checks in a given period of time.

With all this being said, we can start coding. As all code from our own internal projects is written in Go (Golang), we also stick to Go with our examples. We hope you get the general idea, no matter which programming language and environment you are on. To keep things simple we assume in the following examples that our application only relies on a single database.

First we setup the application with different routes. The entry point for the load balancer and the only relevant route here is /status:

mux = http.NewServeMux()
mux.HandleFunc("GET /status", func(w http.ResponseWriter, r *http.Request) { ...

Now we need a simple way to verify connectivity to our database. As stated at the beginning of this article, we take the most simplistic approach that we can think of. After going through the API, Ping() seems to be a good candidate for us. As stated here: https://pkg.go.dev/database/sql#DB.Ping

Ping verifies a connection to the database is still alive, establishing a connection if necessary.

So this is what we do in our code:

  if err := db.Ping(); err != nil {
    panic (fmt.Sprintf(“Status: service unavailable, %s”, err.Error()))
  }

In case Ping() returns an error, we simply panic(). This results in HTTP 503 ("status unavailable") being sent to the client, the response also contains the error message in the HTTP body. The load balancer does not read error messages in plain text, it simply looks for the HTTP error code. But if we run a monitoring solution that also checks the status page, the error message might help a little.

If we conclude the func at this point, HTTP 200 ("OK") is sent to to tell the load balancer that the application is available .

That’s it. We are done. If you followed the steps outlined here you have a status page that works with most load balancers and is easy to maintain.


One word of caution

There is one more thing I want to mention, because we see this once in a while:
Please keep in mind that the idea of a status page is to check for the availability of a web application from a technical point of view. Nothing more, nothing less.
Please do not ever ever mix up technical and functional concerns!
In other words: the status page is not a good place to test the correctness of your business logic — please keep this for your unit and/or functional tests! If you were not confident that the business logic works as expected, please don't deploy a new version of the software.