New reverse proxy
I’ve finally gotten around to doing something about my old main reverse proxy in the home lab. Don’t get me wrong: It’s been working fine, but in the years that have passed, things have really improved around automated certificate renewal using Let’s Encrypt: Adding a new service used to be a multi-step process that required me to sit down and remember how things were hooked up, since I had invariably forgotten how my system was configured between these occasions. Also, I had kept it too simple: The config was literally manually managed. I do have machine backups so I would be able to roll back bad changes, but with the old setup I couldn’t be sure to remember whether I had added manual tweaks somewhere, other than by trawling through my /etc and /usr/local directories.
Now, to add an HTTPS-protected service I only have to set up new DNS records, add the new (sub-)domain to my Ansible inventory file, and run the playbook. This will automatically trigger a request for the new certificate and ensure it’s included in the automatic renewal process. Then all that’s left is to tell HAProxy where to direct the traffic.
I’ve published a skeleton version of the complete playbook, role, inventory and helper script on my git forge, and in a simple environment it should be enough to update a few variables in the inventory file for the solution to be immediately useful.
About the role
A few years ago, the HAProxy team wrote a blog post about how to automate certificate management via Let’s Encrypt with HAProxy and the acme.sh script. One of the great pointers in it was how to make (semi-recent) versions of HAProxy be able to start without already having a certificate in place.
Of course the article itself discusses the steps required for this integration to work, and so it makes perfect sense for it to describe the process in terms of manual step-by-step instructions with examples of script output and what to look for.
What I have done is to reimplement the gist of that blog post in Ansible, currently ready for deployment on a Ubuntu 26.04 server, but it shouldn’t be hard to tweak the playbook for use with other operating systems that support recent versions of HAProxy.
What the playbook will take care of for you:
- Install HAProxy and its prerequisites
- Install
socatfor later validation of certificate status - Clone the latest version of
acme.shand install it - Deploy a base HAProxy configuration that’s ready for integration with
acme.sh - Request or if necessary renew any certificates listed in the inventory’s
haproxy_listener_domainsvariable - Install these into HAProxy
- Validate that the certificates are marked as
Usedby HAProxy, and that their expiry date is at least a week into the future (which should guarantee that they have been newly generated or renewed). - Install a systemd unit and timer for renewal of all certificates
Using this Ansible play should be an easy introduction to HAProxy, with some of the initial sharp edges sanded down. Update the template in roles/haproxy-server/templates/haproxy.cfg.j2 with routing logic and backend definitions to allow traffic to reach web services within your network.