Hey,

This is a quick post for those who might need to perform HTTP health checks against a running HAProxy instance.

From my perspective, three ways of doing it:

  • using the monitor-uri directive (you should use this one);
  • using a custom HTTP file with errorfile directive;
  • using a lua script.

While the first and seconds should work in any HAProxy installation, the third requires lua, which your HAProxy binary might have support or not.

ps.: In the past, I wrote about how to install haproxy with Lua on MacOS.

The gist of it can be summarized in a single haproxy.conf file that contains three frontends - each representing a way of doing it.

#	This configuration demonstrates how several frontends
#	can be created in different ways to indicate
#	via HTTP that the HAProxy instance is running.
#
#	The usefulness of these arise in scenarios like
#	AWS NLB where no TCP health check can be specified
#	and you can't modify the health check headers.
global
	lua-load	./script.lua


defaults
	mode		http
	timeout		client 10000
	timeout		server 10000
	timeout		connect 1000


#	The first way of doing it is via lua scripting:
#	a script (which registers a service) is loaded
#	and then when a request is made to a frontend
#	with the directive `use-service`, the script
#	does the job of responding with 200OK.
frontend		status-lua
	bind		127.0.0.1:8000
	http-request	use-service lua.status_service


#	The second way is using `monitor-uri` (the
#	proper way of doing since you can make the
#	monitor fail in other parts of the configuration
#	and there's no lua involved).
frontend		status-monitor
	bind		127.0.0.1:8001
	monitor-uri	/


#
#	The third way is using a "hack": setting errorfile
#	to respond with a 200OK when a 503 happens, we're
#	able to indicate that HAProxy is active.
frontend		status-errorfile
	bind		127.0.0.1:8002
	http-request	set-log-level silent
	errorfile	503 ./200.http

With that, we now need to tailor a script.lua file that defines the status_service that is used by the first frontend:

-- `core` is a static class provided by haproxy containing all
-- the haproxy methods we can use.


-- `register_init` registers a function to be executed after
-- configuration parsing.
core.register_init(function ()
	core.log(core.info, "script loaded: case-200-ok")
end)

-- `register_service` registers a lua function to be executed
-- as a service. Once it's been properly registered, the service
-- can be referenced in the haproxy configuration as `lua.<svc_name>`.
--
-- Depending on the mode (tcp or http), the applet is either an
-- AppletHTTP or AppletTCP class instance.
core.register_service("status_service", "http", function (applet)
	local response = "OK"

	applet:set_status(200)
	applet:start_response()
	applet:send(response)
end)

And then, for the last frontend, tailor a 200.http file:

HTTP/1.0 200 OK
Cache-Control: no-cache
Connection: close
Content-Type: text/plain
User-Agent: *
Allow: /

ps.: given that we’re defining the whole HTTP message, it must end with carriege return and line feed (\r\n).

That’s it!

Load haproxy.conf and you’re good to go.

If you have any doubts or spotted any mistakes, please let me know! I’m cirowrc on Twitter.

Have a good one!

finis