Hi,
I am very new to all of this, so please bear with me.
I have a docker compose setup with 2 backend services and a haproxy.
The proxy uses the (experimental) ACME integration with letsencrypt.
The DNS is handled by a dynamic DNS setup (noip).
It all seems to work well, a certificate is requested and obtained. See logs here:
haproxy-1 | [NOTICE] (1) : Initializing new worker (8)
haproxy-1 | [NOTICE] (8) : config : No certificate available for 'mpin.ddns.net.pem', generating a temporary key pair before getting the ACME certificate
haproxy-1 | [NOTICE] (8) : Automatically setting global.maxconn to 262126.
haproxy-1 | [NOTICE] (1) : Loading success.
haproxy-1 | acme: mpin.ddns.net.pem: Starting update of the certificate.
haproxy-1 | -:- [26/Jun/2026:15:02:24.335] -/- 4/0/379/181/562 200 1325 - - ---- 0/0/0/0/0 0/0 {172.65.46.172} "GET ``https://acme-staging-v02.api.letsencrypt.org/directory`` HTTP/1.1" 0/0000000000000000/-/-/0 ``acme-staging-v02.api.letsencrypt.org/TLSv1.3/TLS_AES_256_GCM_SHA384
haproxy-1 | -:- [26/Jun/2026:15:02:24.898] -/- 2/0/0/181/183 200 309 - - ---- 0/0/0/0/0 0/0 {172.65.46.172} "HEAD ``https://acme-staging-v02.api.letsencrypt.org/acme/new-nonce`` HTTP/1.1" 0/0000000000000000/-/-/0 ``acme-staging-v02.api.letsencrypt.org/TLSv1.3/TLS_AES_256_GCM_SHA384
haproxy-1 | -:- [26/Jun/2026:15:02:25.080] -/- 3/0/0/185/187 200 720 - - ---- 0/0/0/0/0 0/0 {172.65.46.172} "POST ``https://acme-staging-v02.api.letsencrypt.org/acme/new-acct`` HTTP/1.1" 0/0000000000000000/-/-/0 ``acme-staging-v02.api.letsencrypt.org/TLSv1.3/TLS_AES_256_GCM_SHA384
haproxy-1 | -:- [26/Jun/2026:15:02:25.266] -/- 3/0/0/200/202 201 821 - - ---- 0/0/0/0/0 0/0 {172.65.46.172} "POST ``https://acme-staging-v02.api.letsencrypt.org/acme/new-order`` HTTP/1.1" 0/0000000000000000/-/-/0 ``acme-staging-v02.api.letsencrypt.org/TLSv1.3/TLS_AES_256_GCM_SHA384
haproxy-1 | -:- [26/Jun/2026:15:02:25.467] -/- 3/0/0/183/185 200 1453 - - ---- 0/0/0/0/0 0/0 {172.65.46.172} "POST ``https://acme-staging-v02.api.letsencrypt.org/acme/authz/306667734/2347829964`` HTTP/1.1" 0/0000000000000000/-/-/0 ``acme-staging-v02.api.letsencrypt.org/TLSv1.3/TLS_AES_256_GCM_SHA384
haproxy-1 | -:- [26/Jun/2026:15:02:25.652] -/- 3/0/0/190/191 200 838 - - ---- 0/0/0/0/0 0/0 {172.65.46.172} "POST ``https://acme-staging-v02.api.letsencrypt.org/acme/finalize/306667734/42414363574`` HTTP/1.1" 0/0000000000000000/-/-/0 ``acme-staging-v02.api.letsencrypt.org/TLSv1.3/TLS_AES_256_GCM_SHA384
haproxy-1 | -:- [26/Jun/2026:15:02:28.844] -/- 2/0/0/186/187 200 907 - - ---- 0/0/0/0/0 0/0 {172.65.46.172} "POST ``https://acme-staging-v02.api.letsencrypt.org/acme/order/306667734/42414363574`` HTTP/1.1" 0/0000000000000000/-/-/0 ``acme-staging-v02.api.letsencrypt.org/TLSv1.3/TLS_AES_256_GCM_SHA384
haproxy-1 | acme: mpin.ddns.net.pem: Successful update of the certificate.
haproxy-1 | -:- [26/Jun/2026:15:02:29.031] -/- 3/0/0/184/188 200 5742 - - ---- 0/0/0/0/0 0/0 {172.65.46.172} "POST ``https://acme-staging-v02.api.letsencrypt.org/acme/cert/2c19524b3e98ce2a0dbb5e20e18259e66d31`` HTTP/1.1" 0/0000000000000000/-/-/0 ``acme-staging-v02.api.letsencrypt.org/TLSv1.3/TLS_AES_256_GCM_SHA384
But the SSL handshake is not working correctly. The browser displays a message about an untrusted cert, and haproxy displays this error in the logs:
in/2: SSL handshake failure (error:0A000418:SSL routines::tlsv1 alert unknown ca)
I have no idea at all how to fix it. I thought the certificates obtained through the ACME client should be trusted?
Here is my configuration:
global
log stdout format raw local0
expose-experimental-directives
defaults
log global
mode http
timeout connect 5s
timeout client 50s
timeout server 50s
acme LE1
directory https://acme-staging-v02.api.letsencrypt.org/directory
contact simon@webtecc.com
map virt@acme
crt-store
load crt "mpin.ddns.net.pem" acme LE1 domains "mpin.ddns.net"
frontend in
bind *:80
bind *:443 ssl ciphersuites TLS_AES_256_GCM_SHA384:@SECLEVEL=0
ssl-f-use crt "mpin.ddns.net.pem"
option forwardfor
http-request redirect scheme https unless { ssl_fc }
http-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 allow if { path_beg /app/ }
http-request allow if { path_beg /auth/ }
use_backend app if { path_beg /app/ }
use_backend keycloak if { path_beg /auth/ }
default_backend app
backend app
server s1 mpin-app-1:8080 check
backend keycloak
server s2 mpin-keycloak-1:8080