Issues getting TLS cert

Issue Description

I’m setting up Stalwart with hostname mail.domain.tld and email domain domain.tld, using automatic DNS management via deSEC. Note that deSEC requires a minimum TTL of 3600 seconds, so I had to configure the TTL accordingly.

The goal is to have Stalwart automatically obtain TLS certificates via ACME for the mail hostname as well as the standard auto-config subdomains (autodiscover, autoconfig, mta-sts, ua-auto-config).

I’ve encountered several issues across both dns-01 and tls-alpn-01 challenge types, and ultimately ended up in a state where only some subdomains have valid certificates.

Expected Behavior

Using dns-01 with deSEC integration, Stalwart should be able to create the required _acme-challenge TXT records without conflicts (e.g., by updating existing records instead of attempting to create duplicates).

Using tls-alpn-01, Stalwart should successfully obtain certificates for all configured hostnames, including the standard subdomains (autodiscover, autoconfig, mta-sts, ua-auto-config, mail) without requiring me to manually add each one as an “Additional Hostname”.

All configured subdomains should end up with valid Let’s Encrypt certificates.

Actual Behavior

dns-01 with deSEC:
The challenge fails with a race condition / duplicate-record error. The WebUI reports:

DNS error: Failed to create DNS record: API error: BadRequest
{"non_field_errors":["Another RRset with the same subdomain and type exists for this domain. (Try modifying it.)"]}

The log output:

stalwart  | 2026-05-17T14:36:15Z WARN No TLS certificates available (tls.no-certificates-available) total = 0
stalwart  | 2026-05-17T14:36:28Z INFO ACME authentication started (acme.auth-start) hostname = "domain.tld", type = "dns-01", url = "https://acme-v02.api.letsencrypt.org/acme/new-order"
stalwart  | 2026-05-17T14:36:28Z INFO ACME authentication started (acme.auth-start) hostname = "domain.tld", type = "dns-01", url = "https://acme-v02.api.letsencrypt.org/acme/new-order"
stalwart  | 2026-05-17T14:36:45Z WARN No TLS certificates available (tls.no-certificates-available) total = 0

It looks like Stalwart attempts to create a new TXT record for _acme-challenge instead of replacing/modifying an existing one. Possibly also a race condition when multiple orders run concurrently.

tls-alpn-01:
Initial attempts failed with:

Authentication failed: "Status: invalid; Challenge type: tls-alpn-01,
error: urn:ietf:params:acme:error:unauthorized:
Cannot negotiate ALPN protocol \"acme-tls/1\" for tls-alpn-01 challenge"

Adding mail and mta-sts as additional hostnames under the domain section will create two certs.

stalwart  | 2026-05-17T15:29:07Z INFO ACME order ready (acme.order-ready) url = "https://acme-v02.api.letsencrypt.org/acme/new-order", hostname = ["mail.domain.tld", "mta-sts.domain.tld"]
stalwart  | 2026-05-17T15:29:09Z INFO ACME order valid (acme.order-valid) url = "https://acme-v02.api.letsencrypt.org/acme/new-order", hostname = ["mail.domain.tld", "mta-sts.domain.tld"]
stalwart  | 2026-05-17T15:29:32Z WARN Multiple TLS certificates available (tls.multiple-certificates-available) total = 2

Reproduction Steps

  1. Configure Stalwart with hostname mail.domain.tld and email domain domain.tld.
  2. Enable automatic DNS with deSEC (TTL set to 3600).
  3. Configure ACME with dns-01 challenge → observe duplicate TXT record errors.
  4. Switch to tls-alpn-01 and create A/AAAA records for mail.domain.tld → observe “Cannot negotiate ALPN protocol” errors for all standard subdomains.
  5. Add mail as “Additional Hostname” in the WebUI → certificate is issued successfully for mail.domain.tld.
  6. Add mta-sts as “Additional Hostname” → certificate is issued successfully.
  7. Other subdomains (autodiscover, autoconfig, ua-auto-config) remain on self-signed certificates.

Stalwart Version

v0.16.x

Installation Method

Docker

Database Backend

RocksDB

Blob Storage

RocksDB

Search Engine

Internal

Directory Backend

Internal

Additional Context

I tried the setup with different domains, deSEC accounts and different server providers (like Hetzner and Ionos). The necessary ports are unblocked by the providers.

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

Let me research the ACME configuration and TLS certificate setup in Stalwart to provide accurate guidance.

Here are clear explanations and workarounds for each issue you’re hitting:


Issue 1: DNS-01 “duplicate record” error with deSEC

This is a known limitation of the standard Dns01 challenge type: Stalwart tries to create a new _acme-challenge TXT record on every run, and deSEC (like many providers) rejects a create if one already exists.

The fix: Switch the challenge type from Dns01 to DnsPersist01 in your AcmeProvider settings.

From the docs:

DNS-PERSIST-01 is a variant of the DNS-01 challenge that keeps the validation TXT record in place after the initial issuance so that subsequent renewals do not have to republish it; use it with DNS providers where propagation is slow or where keeping the record simplifies automation.

This resolves both the duplicate-record error and the propagation timing issue caused by deSEC’s 3600-second minimum TTL. With DnsPersist01, the record is written once and reused, so deSEC’s “modify, don’t create” constraint is respected on subsequent renewals.


Issue 2: tls-alpn-01 “Cannot negotiate ALPN protocol” for subdomains

TLS-ALPN-01 works by having Let’s Encrypt connect to each hostname being validated on port 443. For subdomains like autodiscover.domain.tld, autoconfig.domain.tld, etc., Let’s Encrypt must be able to reach that specific hostname at your server’s IP on port 443. If those subdomains don’t have A/AAAA records pointing to your server (or the DNS docs show them as CNAMEs pointing to mail.domain.tld, which resolves correctly), the ACME server can’t complete the TLS-ALPN handshake for them.

The docs note: “The domain must resolve publicly to the host serving the challenge response” and that the ACME server connects to the hostname under validation. If Let’s Encrypt can’t reach autodiscover.domain.tld:443 directly and negotiate acme-tls/1, that subdomain fails.


Issue 3: Subdomains needing explicit subjectAlternativeNames

For tls-alpn-01, you do need to list additional hostnames in subjectAlternativeNames on the Domain object:

Enter hostnames only (e.g. mta-sts, autoconfig), the domain is appended automatically.

There is no mechanism with tls-alpn-01 to implicitly obtain certs for all standard auto-config subdomains without listing them. The alternative that avoids this entirely is to use Dns01/DnsPersist01, which supports wildcard certificates (*.domain.tld), covering all subdomains in a single certificate without needing to list each one.


Recommended approach for your setup

Given deSEC’s constraints and the number of subdomains involved, the cleanest solution is:

  1. Switch to DnsPersist01 as the challenge type.
  2. Leave subjectAlternativeNames empty on your domain – the docs say leaving it empty requests a wildcard certificate where the provider supports it (*.domain.tld via DNS-01/DnsPersist01).
  3. This gives you one wildcard cert covering mail., autoconfig., autodiscover., mta-sts., etc. without needing to enumerate them.

Relevant docs: TLS Certificates | ACME Configuration | Challenge Types

This is an automated reply from the Stalwart Help Bot. Other community members may follow up if this answer is incomplete or wrong. If you found it useful, mention @helpbot in any reply to ask follow-up questions and the bot will respond again.

@helpbot I tried to use dns-persist-01 but let’s encrypt currently does not support this as the stalwart logs showed me.

For tls-alpn-01: It exists an A and AAAA record for mail.domain.tld pointing to the server.

These subdomains exists as CNAMES created from stalwart with the first try automatic dns. They are correctly resolveable.

That’s not true. The logs are showing me the attempts for these subdomains, just no success message.

This has been fixed in v0.16.6 which will be released this week.