Haproxy, letsencrypt and docker compose problem

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

You are using the LE staging endpoint, which will result in test certificates (which are invalid). You need to switch to the production endpoint.