SSL/TLS
Enable TLS by using the ACME protocol
This page applies to:
- HAProxy 3.2 and newer
- HAProxy Enterprise 3.2r1 and newer
Experimental feature
Enabling the ACME protocol to manage TLS certificates is currently experimental.
With the ACME (Automatic Certificate Management Environment) protocol, you can integrate with certificate issuers to automate the process of obtaining and renewing TLS certificates. The ACME protocol is an open standard that’s widely supported by both free and paid certificate issuers. By enabling ACME at the proxy layer, you can:
- Offload the integration details and avoid a custom implementation in your applications.
- Ensure a single tier for managing the certificate issuer’s validation of your domain and the process of certificate renewals.
- Maintain a consistent approach for certificate management across heterogeneous applications.
This feature works with a single load balancer, but not an active-active pair, due to the need to communicate with the certificate issuer’s servers.
Let’s Encrypt Jump to heading
In this section, learn how to configure the load balancer to use the ACME provider named Let’s Encrypt to get a TLS certificate.
HTTP-01 challenge Jump to heading
This section applies to:
- HAProxy 3.2 and newer
- HAProxy Enterprise 3.2r1 and newer
We’ll use an HTTP-01 challenge type. The challenge allows the Let’s Encrypt servers to verify that you control the domain for which you’re requesting a certificate. In this case, HAProxy hosts a file at a well-known URL path, which Let’s Encrypt verifies.
To get a certificate from Let’s Encrypt:
-
HAProxy 3.2 / HAProxy Enterprise 3.2: In version 3.2, you must generate a dummy TLS certificate. This certificate serves as a placeholder, allowing HAProxy to start and preventing an error that would happen if it tried to read a certificate that doesn’t yet exist on the filesystem. You don’t need to do this in later versions.
-
Generate a dummy TLS certificate file, which we’ll later overwrite with the Let’s Encrypt certificate. The load balancer needs a file on the filesystem at startup.
nixcd ~openssl req -x509 \-newkey rsa:2048 \-keyout example.key \-out example.crt \-days 365 \-nodes \-subj "/C=US/ST=Ohio/L=Columbus/O=MyCompany/CN=www.example.com"cat example.key example.crt > example.pemnixcd ~openssl req -x509 \-newkey rsa:2048 \-keyout example.key \-out example.crt \-days 365 \-nodes \-subj "/C=US/ST=Ohio/L=Columbus/O=MyCompany/CN=www.example.com"cat example.key example.crt > example.pem -
Copy the PEM file to the directory. For example:
nixsudo mkdir /etc/haproxy/sslsudo cp example.pem /etc/haproxy/sslnixsudo mkdir /etc/haproxy/sslsudo cp example.pem /etc/haproxy/ssl
-
-
HAProxy 3.3 / HAProxy Enterprise 3.3 and newer: Starting in version 3.3, HAProxy generates event notifications when it creates or updates a certificate from an ACME provider, and the HAProxy Data Plane API watches for these events. Together, they automate saving the certificates to the filesystem. Previously, you had to call the Runtime API function
dump ssl certto save the certificate from HAProxy’s runtime memory to a file.-
Find the path to the HAProxy master socket by calling
systemctl status haproxyto get the value of the-Sparameter.nixsystemctl status haproxynixsystemctl status haproxyoutputtext● haproxy.service - HAProxy Load BalancerLoaded: loaded (/usr/lib/systemd/system/haproxy.service; enabled; preset: enabled)Active: active (running) since Mon 2026-04-06 14:39:32 UTC; 33min agoDocs: man:haproxy(1)file:/usr/share/doc/haproxy/configuration.txt.gzMain PID: 2366 (haproxy)Status: "Ready."Tasks: 3 (limit: 9433)Memory: 69.2M (peak: 70.8M)CPU: 63msCGroup: /system.slice/haproxy.service├─2366 /usr/sbin/haproxy -Ws -f /etc/haproxy/haproxy.cfg -p /run/haproxy.pid -S /run/haproxy-master.sock└─2368 /usr/sbin/haproxy -Ws -f /etc/haproxy/haproxy.cfg -p /run/haproxy.pid -S /run/haproxy-master.sockoutputtext● haproxy.service - HAProxy Load BalancerLoaded: loaded (/usr/lib/systemd/system/haproxy.service; enabled; preset: enabled)Active: active (running) since Mon 2026-04-06 14:39:32 UTC; 33min agoDocs: man:haproxy(1)file:/usr/share/doc/haproxy/configuration.txt.gzMain PID: 2366 (haproxy)Status: "Ready."Tasks: 3 (limit: 9433)Memory: 69.2M (peak: 70.8M)CPU: 63msCGroup: /system.slice/haproxy.service├─2366 /usr/sbin/haproxy -Ws -f /etc/haproxy/haproxy.cfg -p /run/haproxy.pid -S /run/haproxy-master.sock└─2368 /usr/sbin/haproxy -Ws -f /etc/haproxy/haproxy.cfg -p /run/haproxy.pid -S /run/haproxy-master.sock -
Edit the HAProxy Data Plane API configuration file. On HAProxy, it’s located at
/etc/dataplaneapi/dataplaneapi.yml. On HAProxy Enterprise, it’s located at/etc/hapee-extras/dataplaneapi.yml. Under thehaproxysection, set themaster_runtimefield to the HAProxy master socket path. For example:dataplaneapi.ymlyamlhaproxy:master_runtime: /run/haproxy-master.sockdataplaneapi.ymlyamlhaproxy:master_runtime: /run/haproxy-master.sock
-
Edit your load balancer configuration accordingly:
-
In the
globalsection, addexpose-experimental-directivesandhttpclient.resolvers.prefer ipv4:haproxyglobal...expose-experimental-directiveshttpclient.resolvers.prefer ipv4haproxyglobal...expose-experimental-directiveshttpclient.resolvers.prefer ipv4 -
After the
globalsection, add anacmesection to register with Let’s Encrypt. In this example, we’re using the Let’s Encrypt staging server, which you can use for testing. Later, change it to the Let’s Encrypt production server.haproxyacme letsencrypt-stagingdirectory https://acme-staging-v02.api.letsencrypt.org/directorycontact admin@example.comchallenge HTTP-01keytype RSAbits 2048map virt@acmehaproxyacme letsencrypt-stagingdirectory https://acme-staging-v02.api.letsencrypt.org/directorycontact admin@example.comchallenge HTTP-01keytype RSAbits 2048map virt@acme -
After the
acmesection, add a crt-store section to define the location of your Let’s Encrypt issued certificate. You don’t have to use acrt-storesection. For small configurations, the arguments can all go onto thessl-f-useline. Theloaddirective will save the.pemcertificate, after it’s generated, to the directory/etc/haproxy/ssl. Change this line to use your domain.haproxycrt-store my_filescrt-base /etc/haproxy/sslkey-base /etc/haproxy/sslload crt "example.pem" acme letsencrypt-staging domains "www.example.com" alias "example"haproxycrt-store my_filescrt-base /etc/haproxy/sslkey-base /etc/haproxy/sslload crt "example.pem" acme letsencrypt-staging domains "www.example.com" alias "example" -
In your
frontendsection, respond to the Let’s Encrypt challenge via thehttp-request returndirective and use thessl-f-usedirective to serve the TLS certificate bundle.haproxyfrontend mysitebind :80bind :443 sslhttp-request return status 200 content-type text/plain lf-string "%[path,field(-1,/)].%[path,field(-1,/),map(virt@acme)]\n" if { path_beg '/.well-known/acme-challenge/' }http-request redirect scheme https unless { ssl_fc }ssl-f-use crt "@my_files/example"use_backend webservershaproxyfrontend mysitebind :80bind :443 sslhttp-request return status 200 content-type text/plain lf-string "%[path,field(-1,/)].%[path,field(-1,/),map(virt@acme)]\n" if { path_beg '/.well-known/acme-challenge/' }http-request redirect scheme https unless { ssl_fc }ssl-f-use crt "@my_files/example"use_backend webservers
-
-
Restart the load balancer.
nixsudo systemctl restart haproxynixsudo systemctl restart haproxynixsudo systemctl restart hapee-3.3-lbnixsudo systemctl restart hapee-3.3-lb -
HAProxy 3.2 / HAProxy Enterprise 3.2: Use the HAProxy Runtime API to initiate getting a certificate. In newer versions, this happens automatically when you restart the load balancer.
-
Call the Runtime API command acme renew to create a Let’s Encrypt certificate that replaces, in memory, the dummy TLS certificate file you created earlier.
nixecho "acme renew @my_files/example" | sudo socat stdio tcp4-connect:127.0.0.1:9999nixecho "acme renew @my_files/example" | sudo socat stdio tcp4-connect:127.0.0.1:9999 -
The new certificate exists only in the load balancer’s running memory. To save it to a file, call the Runtime API command dump ssl cert.
nixecho "dump ssl cert @my_files/example" | sudo socat stdio tcp4-connect:127.0.0.1:9999 | sudo tee /etc/haproxy/example.pemnixecho "dump ssl cert @my_files/example" | sudo socat stdio tcp4-connect:127.0.0.1:9999 | sudo tee /etc/haproxy/example.pem -
Use the Runtime API command acme status to see a list of running tasks.
nixecho "acme status" | sudo socat stdio tcp4-connect:127.0.0.1:9999nixecho "acme status" | sudo socat stdio tcp4-connect:127.0.0.1:9999outputtext# certificate section state expiration date (UTC) expires in scheduled date (UTC) scheduled in@my_files/example letsencrypt-staging Running 2026-09-30T20:37:44Z 364d 23h50m02s - -outputtext# certificate section state expiration date (UTC) expires in scheduled date (UTC) scheduled in@my_files/example letsencrypt-staging Running 2026-09-30T20:37:44Z 364d 23h50m02s - -
-
The TLS certificate should now be saved to the /etc/haproxy/ssl directory. It will be used when clients make requests to your domain.