HAProxy (short for High Availability Proxy) is an open-source software that acts as a load balancer and proxy server for TCP and HTTP-based applications. It is widely used in both small and large-scale production environments to improve performance, reliability, and scalability of web and application services.
Any L7 load balancer (reverse http proxy) nowadays is used for SSL/TLS termination and very often with combination with ACME (Automatic Certificate Management Environment).
How ACME works? Below is the simplified process ...
- Account Setup
- Your ACME client (like Certbot, acme.sh, or HAProxy’s built-in ACME support) registers with the CA.
- Domain Validation
- The CA challenges the client to prove it controls the domain (HTTP-01, DNS-01, or TLS-ALPN-01 challenge).
- Example:
- For HTTP-01, the client places a special token on your web server, and the CA checks it.
- For DNS-01, the client places a special token on your DNS server, and the CA checks it.
- acme.sh creates a TXT record value that must be placed under
- _acme-challenge.uw.cz
- Certificate Issuance
- Once validated, the CA issues an SSL/TLS certificate automatically.
- Renewal
- The client renews certificates before they expire, often without human involvement.
I use DNS-01 CA challenge, therefore integration with DNS provider is necessary. I use Active24.cz DNS provider.
For my personal load-balancer I use VM with 2 vCPUs, 2 GB RAM, 10 GB vSSD, 1x vNIC, Linux OS - Debian 13.0
If you are interested how to install and configure above solution, keep reading.
HAproxy Basic Concept
Before installation and configuration we should be familiar with HAproxy's basic concepts and terminology. Here's a breakdown of its core concepts.
HAProxy (High Availability Proxy) is an open-source software solution designed to provide high availability, load balancing, and proxying for TCP and HTTP-based applications. It acts as an intermediary, sitting between the clients and the backend servers, to efficiently distribute incoming network traffic.
Key Concepts
Frontends: These define how HAProxy listens for incoming client requests. A frontend specifies the IP addresses and ports that HAProxy binds to. It also includes rules for processing and routing requests, such as SSL termination and access control lists (ACLs).
Backends: A backend is a group of servers to which a frontend forwards requests. It defines the pool of servers, their IP addresses, and ports. The backend also specifies the load balancing algorithm that HAProxy will use to distribute traffic among the servers.
Load Balancing Algorithms: These are the rules HAProxy uses to decide which server in a backend should receive a new request. Common algorithms include:
Round Robin: Distributes requests sequentially to each server in the pool. This is the default algorithm and works well when all servers have similar capabilities.
Least Connections: Routes new connections to the server with the fewest active connections. This is useful when the time it takes to process a request varies between servers.
Source: Ensures a client is always directed to the same server based on their source IP address. This is crucial for applications that require session persistence.
Core Features
High Availability: As its name suggests, HAProxy is built to ensure web services are always accessible. It achieves this by distributing traffic across multiple servers, preventing a single point of failure. If one server goes down, HAProxy automatically redirects traffic to the remaining healthy servers.
Health Checks: HAProxy continuously monitors the health of the backend servers. If a server becomes unresponsive, HAProxy automatically removes it from the pool and stops sending traffic to it. It will add the server back once it's healthy again.
SSL Offloading: This feature allows HAProxy to handle the SSL/TLS encryption and decryption process. By offloading this computationally intensive task from the backend servers, HAProxy improves overall performance and simplifies SSL certificate management by centralizing it.
HAProxy Installation
Official HAproxy documentation is available at https://docs.haproxy.org/
Install Debian
Debian installation is out of scope. After standard Debian minimal installation, login as root and update the system ...
# Apt sources are in file /etc/apt/sources.list
apt update && apt upgrade -y
When system and packages are up to date, install dependencies ...
apt install -y curl git apt-transport-https ca-certificates gnupg lsb-release
Install Docker
Check installed docker compose version
root@mailcow:~#docker compose versionDocker Compose version v2.39.1 root@mailcow:~#
Prepare Directories to store files
TLS Certificates with ZeroSSL and Active24 DNS provider
We will leverage acme.sh to automatically issue & renew the free certificates and we will use DNS-01 challenge, therefore integration with DNS provider is necessary.
Create DNS provider credentials
Your DNS provider must be supported by acme.sh. See supported providers at https://github.com/acmesh-official/acme.sh/wiki/dnsapi. I use Active24 which is supported.
Create an .env file with your Active24 credentials:
Create docker volume
docker volume create haproxy_certs
This volume will be used for acme.sh and SSL/TLS certificate files are stored there.
Register and Generate Certificate Manualy (One Time just to be sure it works)
Since v3, acme.sh uses ZeroSSL as the default Certificate Authority (CA). See. https://github.com/acmesh-official/acme.sh/wiki/ZeroSSL.com-CA. Account registration (one-time) is required before one can issue new certs. This must be done on web https://zerossl.com/. Try to issue one certificate over web to be sure everything works. After registration, e-mail verification, and validation that certificates can be created, you can continue.
HAproxy docker-compose Deployment
Above section was just test that certificates can be successfully issued with neilpang/acme.sh container. In this section we will configure everything in docker compose file.
Create HAProxy config file ...
log stdout format raw local0
maxconn 2000
tune.ssl.default-dh-param 2048
defaults
log global
option httplog
option dontlognull
timeout connect 5s
timeout client 50s
timeout server 50s
retries 3
frontend http-in
bind *:80
mode http
redirect scheme https code 301 if !{ ssl_fc }
frontend https-in
bind *:443 ssl crt /usr/local/etc/haproxy/certs/uw.cz_ecc/uw.cz.pem
mode http
option forwardfor
default_backend web-backend
backend web-backend
mode http
option httpchk GET /
balance roundrobin
server web1 10.200.2.3:443 verify none check
Create docker compose file
certs:
driver: local
proxy:
external: false
haproxy:
image: haproxy:latest
container_name: haproxy
- proxy
ports:
- "80:80"
- "443:443"
- certs:/usr/local/etc/haproxy/certs:ro
restart: unless-stopped
acme:
image: neilpang/acme.sh
container_name: acme
- proxy
volumes:
- certs:/acme.sh
environment:
- Active24_ApiKey=\${Active24_ApiKey} # Active24 API Key
- Active24_ApiSecret=\${Active24_ApiSecret} # Active24 API Secret
command: daemon --foreground
restart: unless-stopped
Run Docker Stack (HAProxy with ACME)
Register account at ZeroSSL - Initial setup (one-time)
docker exec -it acme acme.sh --set-default-ca --server zerossl
# Register account
docker exec -it acme acme.sh --register-account -m david.pasek@gmail.com --server zerossl
Issue cert with automated renewal and reload hook
# Issue cert with reload hook
docker exec -it acme acme.sh --issue \
--dns dns_active24 \
-d uw.cz -d '*.uw.cz' \
--key-file /acme.sh/uw.cz_ecc/uw.cz.key \
--fullchain-file /acme.sh/uw.cz_ecc/fullchain.cer \
--reloadcmd "sh -c 'cat /acme.sh/uw.cz_ecc/uw.cz.key /acme.sh/uw.cz_ecc/fullchain.cer > /acme.sh/uw.cz_ecc/uw.cz.pem && chmod 644 /acme.sh/uw.cz_ecc/uw.cz.pem && docker kill -s HUP haproxy'"
How renewal works automatically?
- acme.sh daemon wakes up daily.
- If a certificate is within 30 days of expiration, it renews it.
- After renewal, it executes the --reloadcmd to update HAProxy without downtime.
Your HAProxy should be up and running now at (10.200.2.4)
Your backend HTTP server (10.200.2.3) should be accessible over HTTPS.
In case of troubles, use troubleshooting tips on section below.
HAproxy docker-compose Troubleshooting
Troubleshoot acme.sh
you can login into acme container for troubleshooting ...
docker logs acme
docker exec -it acme sh -c "/bin/sh"
docker run --rm -it -v haproxy_certs:/acme.sh busybox cat /acme.sh/uw.cz_ecc/uw.cz.pem
docker run --rm -it -v haproxy_certs:/acme.sh busybox ls -l /acme.sh/uw.cz_ecc/uw.cz.pem
We can check generated files by following commands ...
docker run --rm -it -v haproxy_certs:/acme.sh busybox cat /acme.sh/uw.cz_ecc/uw.cz.pem
docker run --rm -it -v haproxy_certs:/acme.sh busybox ls -l /acme.sh/uw.cz_ecc/uw.cz.pem
Certificate verification in haproxy_certs docker volume
We can verify that the cert is really ZeroSSL with following command ...
docker run --rm -v haproxy_certs:/acme.sh alpine \
sh -c "apk add --no-cache openssl >/dev/null && \
openssl x509 -in /acme.sh/uw.cz_ecc/fullchain.cer -noout -issuer -subject"
root@lb:/opt/haproxy#docker run --rm -v haproxy_certs:/acme.sh alpine sh -c "apk add --no-cache openssl >/dev/null && \ openssl x509 -in /acme.sh/uw.cz_ecc/fullchain.cer -noout -issuer -subject"issuer=C=AT, O=ZeroSSL, CN=ZeroSSL ECC Domain Secure Site CA subject=CN=uw.cz root@lb:/opt/haproxy#
List all certificates acme.sh manages
docker exec -it acme acme.sh --list
root@lb:/opt/haproxy#docker exec -it acme acme.sh --listMain_Domain KeyLength SAN_Domains CA Created Renew uw.cz "ec-256" *.uw.cz ZeroSSL.com 2025-09-13T18:06:21Z 2025-11-11T18:06:21Z root@lb:/opt/haproxy#
Troubleshoot haproxy
you can login into acme container for troubleshooting ...
docker logs haproxy
docker exec -it haproxy sh -c "/bin/sh"
Stop and start again HAProxy and ACME.SH
HAProxy acme.sh certificate renewal
Automatic certificate renewal
Docker compose and steps above provide automatic renewal
docker exec -it acme acme.sh --install-cert -d uw.cz \
--cert-file /acme.sh/uw.cz_ecc/uw.cz.cer \
--key-file /acme.sh/uw.cz_ecc/uw.cz.key \
--fullchain-file /acme.sh/uw.cz_ecc/fullchain.cer \
--reloadcmd "cat /acme.sh/uw.cz_ecc/uw.cz.key /acme.sh/uw.cz_ecc/fullchain.cer > /acme.sh/uw.cz_ecc/uw.cz.pem && chmod 644 /acme.sh/uw.cz_ecc/uw.cz.pem"
Manual certificate renewal
Explanation:
- --renew -d uw.cz → Attempts to renew the certificate for uw.cz
- --force → Forces the renewal even if it’s not close to expiration
- docker compose down → Shutdown the stack
- docker compose up -d → Start the stack in background (as daemon)
Automated restart of haproxy container
Create a small script at /opt/haproxy/restart-weekly.sh
# Weekly restart of selected HAProxy containers
docker compose restart haproxy >> /var/log/haproxy-weekly-restart.log 2>&1
Make the script executable
chmod +x /opt/haproxy/restart-weekly.sh
Schedule the script in cron
Edit the root crontab:
crontab -e
...and add following line to run it every Saturday at 3:00 AM:
0 3 * * 0 /opt/haproxy/restart-weekly.sh
No comments:
Post a Comment