Not able to retrieve certificates through Let's encrypt ACME (behind caddy) - worked around

Issue Description

TLS certificate not obtained when using HTTP-01 and only MX and A record for mail.domain.com and when only port 443 is behind reverse proxy (caddy) and forwarding to stalwart http:8080.

Three pending tasks are created for the same domain renewal (that’s probably a separate issue).

Expected Behavior

Stalwart should successfully obtain certificate for mail.domain.com because ACME challenge is visible in the reverse proxy log and visible also that request hits Stalwart http endpoint.

Actual Behavior

The ACME task requests multiple certificates (mail, autodiscover, autoconfig, mta-sts) and apparently those are created in a batch and therefore fail because autodiscover, autoconfig, mta-sts DNS record don’t even exist. So the certificate is not even requested.

I believe that there should be a separate option for domain TLS and server TLS certificate, because I only care about server certificate (mail.domain.com) and not about others (so that IMAPS, submission TLS) are using proper valid certificates so that mail clients don’t complain.

Reproduction Steps

  1. Setup stalwart so that it accepts 25, 465, 993 directly. https traffic is termianted by proxy (caddy) and proxied to stalwart:8080
  2. Try to obtain a certificate

Relevant Log Output

2026-06-12T13:13:18Z INFO ACME authentication started (acme.auth-start) hostname = “autoconfig.redacted.com”, type = “http-01”, url = “https://acme-staging-v02.api.letsencrypt.org/acme/new-order
2026-06-12T13:13:18Z INFO ACME authentication started (acme.auth-start) hostname = “ua-auto-config.redacted.com”, type = “http-01”, url = “https://acme-staging-v02.api.letsencrypt.org/acme/new-order
2026-06-12T13:13:18Z INFO ACME authentication started (acme.auth-start) hostname = “autodiscover.redacted.com”, type = “http-01”, url = “https://acme-staging-v02.api.letsencrypt.org/acme/new-order
2026-06-12T13:13:18Z INFO ACME authentication started (acme.auth-start) hostname = “mta-sts.redacted.com”, type = “http-01”, url = “https://acme-staging-v02.api.letsencrypt.org/acme/new-order
2026-06-12T13:13:30Z DEBUG HTTP connection ended (http.connection-end) listenerId = “http”, localPort = 8080, remoteIp = 172.18.0.7, remotePort = 38360, elapsed = 208478ms
2026-06-12T13:13:35Z DEBUG HTTP connection ended (http.connection-end) listenerId = “http”, localPort = 8080, remoteIp = 172.18.0.7, remotePort = 51310, elapsed = 154797ms
2026-06-12T13:13:48Z WARN No TLS certificates available (tls.no-certificates-available) total = 0

Stalwart Version

v0.16.x

Installation Method

Docker

Database Backend

RocksDB

Blob Storage

RocksDB

Search Engine

Internal

Directory Backend

Internal

I have reviewed the documentation and FAQ and confirm that my issue is NOT addressed there.

on

I have searched this support forum (open and closed topics) and confirm this is not a duplicate.

on

I understand that topics in this category are triaged by a bot first but a human reply will follow up. If I’d prefer a human-only reply, I’ll add the no-ai tag to my topic.

on

Good, I now have a solid understanding of the issue and the solution. Let me summarize the findings:

The root cause here is well-understood: when a domain is configured for automatic certificate management and subjectAlternativeNames is left empty, Stalwart uses a default SAN set derived from the domain, which includes autodiscover.*, autoconfig.*, mta-sts.*, etc. Since those names have no DNS records and aren’t reachable over HTTP, the HTTP-01 challenge fails for the whole order.

Here are the two fixes:


Fix 1 (Recommended): Explicitly set subjectAlternativeNames to only mail.yourdomain.com

On your Domain object, under certificateManagement (Automatic mode), populate the subjectAlternativeNames list with only the hostnames you actually have DNS records for – in your case, just mail.yourdomain.com. This prevents Stalwart from including autodiscover.*, autoconfig.*, mta-sts.* in the certificate order.

From the docs:

subjectAlternativeNames lists additional host names to include alongside the domain name itself. Leaving the list empty requests a wildcard certificate where the provider supports it, or uses the default SAN set derived from the Domain.

You can set this in the WebUI under Settings › Domains › [your domain] › Certificate Management.


Fix 2: Note on HTTP-01 through a reverse proxy

The HTTP-01 challenge requires that port 80 (not 443) is publicly reachable and resolves to each hostname being validated. Since your setup only proxies port 443 to Stalwart’s :8080, and the names like autodiscover.* have no DNS records at all, HTTP-01 will always fail for those names regardless of proxy config.

If you only want mail.yourdomain.com covered, Fix 1 above resolves the problem cleanly. If you later need more names, you’d need DNS records for them and ensure port 80 forwards .well-known/acme-challenge/ requests through to Stalwart, or switch to DNS-01.


Relevant docs:

This is an automated reply from the Stalwart Help Bot. Other community members may follow up if this answer is incomplete or wrong.

I also started writing a forum post, but then changed it to issue, so here it is anyway:

I believe this to be a bug in 0.16.8 (that is what I’m using).

Stalwart will not retrieve certificate for mail.domain.com automatically (that’s the hostname used) even though I have configured ACME (Let’s Encrypt) provider with HTTP-01 challenge, which should work, as Caddy is redirecting http requests to ./well-known to https and Let’s Encrypt is requesting them.

My DNS records had just MX records for domain.com and A records for mail.domain.com. I noticed that acme client was requesting certificate for the default SANs as well and I assume the request failed because the alternate SANs (mta-sts, autodiscover, autoconfig) did not exist.

I then create autodiscover and autoconfig CNAMEs in DNS and added those two to SANS under the domain configuration domain.com. Now Stalwart requested the certificate only for autoconfig.domain.com with subject alternate names autoconfig.domain.com and autodiscover.domain.com. So no certificate for mail.domain.com. Why?

The process of re-requesting certificates is quite convoluted: switch to manual, save, switch to automatic, fill in the details, save. Restarting the container does not help.

So, I [found out later]( Refresh certificates manually ) on I can go to Tasks and issue AcmeRenewal for a domain through the gui or through CLI

The log contains a warning: 2026-06-12T14:02:01Z WARN Multiple TLS certificates available (tls.multiple-certificates-available) total = 2

I then added mail to the SANs on the domain and issued renewal and got all three.

I then added production ACME provider and changed domain configuration to use the production provider. Triggered AcmeRenewal task. Done :oncoming_fist:.

Questions:

Whis does Stalwart log a warning that multiple certificates exist?

Why does it not request certificate for the mail.domain.com automatically (without autodiscover and autoconfig gymnastics)? Is it expected that those CNAMEs are present for every installation?

I believe documentation should be at least updated to list the requirements (unless I’ve misread it). I also believe that server certificate should be requested and there should be a way to manage that (not only through the domain TLS settings).

Dear bot, Fix1 did not work for me (I’ve tested this path as well) - no certificates were requested in this case, possibly due to a bug that duplicate entry is found?

Fix2: it is incorrect that port 80 needs to be exposed to stalwart - Let’s Encrypt will follow the redirect to 443 and reverse proxy will pass it through to Stalwart. All of the alternative names must of course point to the same docker instance.

This happens when a host is requested that is not on the SAN list, or no SNI host is given in the request, AND a default certificate is not selected in the server settings, so it doesn’t know which one to respond with.

The bot will not respond to prompts after the first post.

Good to know. So I just configured the default certificate for the server and in the process found out that 5 (five!) of them exist, all with the same subject alternative names.

Now what? Should I delete all but one?

It is still happening today even though I defined a default cert yesterday.