ACME New Order & Rate Limits

Your question

Question rather than an issue because I may just not understand this flow.

Ref: Rate Limits - Let's Encrypt

I already know I am at my 7 day rate limit on the “same set of identifiers”.

Even though you are limited to 5 certs in 7 days against “identical identifiers” (e.g. the same 5 SAN hosts you start with on the same “registered domain”) if you add more SAN to the same cert, you are treated as a new order, subject to the 50 new orders in 7 days limit instead.

Therefore I am reasoning by adding mx1, mx2 and mx3 to my SAN list, the system should request a new cert with 8 SAN entries, and LE should grant it, as you’ve bypassed the “same identifiers” and moved back to “new orders” limits.

So I go to the domain settings, and I add “mx1”, “mx2”, and “mx3” under “additional hostnames”, which I believe should request 8 SAN in a cert.

I then issue a scheduled event for ACME renewal, and the following happens:


2026-05-30T03:42:26Z INFO ACME authentication started (acme.auth-start) hostname = "mx1.example.ca", type = "tls-alpn-01", url = "https://acme-v02.api.letsencrypt.org/acme/new-order"
2026-05-30T03:42:26Z INFO ACME authentication started (acme.auth-start) hostname = "mx2.example.ca", type = "tls-alpn-01", url = "https://acme-v02.api.letsencrypt.org/acme/new-order"

2026-05-30T03:42:28Z INFO ACME authentication pending (acme.auth-pending) hostname = "mx1.example.ca", url = "https://acme-v02.api.letsencrypt.org/acme/new-order", total = 0
2026-05-30T03:42:28Z INFO ACME authentication pending (acme.auth-pending) hostname = "mx2.example.ca", url = "https://acme-v02.api.letsencrypt.org/acme/new-order", total = 0
2026-05-30T03:42:30Z INFO ACME authentication pending (acme.auth-pending) hostname = "mx3.example.ca", url = "https://acme-v02.api.letsencrypt.org/acme/new-order", total = 0

2026-05-30T03:42:32Z INFO ACME authentication pending (acme.auth-pending) hostname = "mx2.example.ca", url = "https://acme-v02.api.letsencrypt.org/acme/new-order", total = 1
2026-05-30T03:42:33Z INFO ACME authentication pending (acme.auth-pending) hostname = "mx1.example.ca", url = "https://acme-v02.api.letsencrypt.org/acme/new-order", total = 1
2026-05-30T03:42:34Z INFO ACME authentication pending (acme.auth-pending) hostname = "mx3.example.ca", url = "https://acme-v02.api.letsencrypt.org/acme/new-order", total = 1

This repeats every 30 seconds until I hit the next rate limit which is “Authorization Failures per Identifier per Account”. At this point the task scheduler will report a failure with the “retry in x seconds” 10 times until it too fails and quits.

It looks to me like the system is trying to order 3 new certificates with one hostname each, rather than re-ordering the existing certificate with the 8 SANs.

Am I misunderstanding the flow, or is there a genuine issue with adding additional hostnames to SAN, and is currently being rate limited exposing this issue where it may not have occurred otherwise.

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 look into how Stalwart handles ACME certificate ordering and multiple hostnames/SANs.

The code confirms what is happening and clears up the confusion. Here is what the logs actually show and why:

The acme.auth-start logs showing individual hostnames are normal and expected – they do NOT mean Stalwart is ordering 3 separate single-SAN certificates. The ACME protocol requires Stalwart to prove control of each hostname in the order individually, and it does so in parallel. Each authorize() call corresponds to one domain authorization challenge within the same multi-SAN order.

Looking at the source code in crates/common/src/network/acme/order.rs:

  • new_order(domains) is called once with the full list of all domains.
  • The authorizations list returned by the ACME server contains one authorization URL per domain/SAN.
  • Stalwart then fires those off in parallel with try_join_all(auth_futures), which is why you see three acme.auth-start lines at the same time, each with its own hostname.

So the log line url = "https://acme-v02.api.letsencrypt.org/acme/new-order" is the ACME directory URL logged as context on every event – it is not saying “this hostname triggered its own new-order request.” There is only one order.

The actual problem is that the acme.auth-pending loop repeats because the TLS-ALPN-01 challenge for the new hostnames (mx1, mx2, mx3) is failing validation – Let’s Encrypt cannot reach those hostnames on port 443 to validate them. With tls-alpn-01, every hostname in the SANs list must be reachable by LE on port 443 at the IP address that DNS resolves for that hostname. If mx1.example.ca, mx2.example.ca, mx3.example.ca do not resolve to your server’s public IP (or port 443 is blocked), the auth will stay pending until LE gives up and marks it invalid.

What to check:

  1. Do mx1/mx2/mx3.example.ca have A/AAAA records pointing to your server?
  2. Is port 443 open and routed to Stalwart on those addresses?
  3. Alternatively, if you cannot expose those hostnames over TLS-ALPN-01, consider switching to dns-01 challenge type (with automatic DNS management) or http-01 for the ACME provider, which may be more practical for MX hostnames.

The docs on certificate management and the SAN list are at: TLS certificates | Stalwart and Domain | Stalwart

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

Resolved. I knew it was me. It was a reach issue due to a load balancer config, since these were going to specific nodes.

While confirmed it is not ordering 3 new certs, it did order one new cert with the 3 new SAN in it. Where I expected to have one cert with 8 SAN, I have 1x5 and 1x3 now. If that’s the intended outcome, then it’s functional.

How do you order a new certificate or force a renewal after adding a new SAN to a domain, @Disrupt ?

Management:Tasks:Scheduled

Create Task

Task Type - Perform ACME certificate renewal for a domain