We are starting to configure the migration proxy for testing. Our legacy server has an SSL cert for every customer domain, I.E. “mail.customer.com” which points to the legacy server. Customers are using mail.customer.com in their clients.
How would I handle this with the migration proxy? We have 400+ certs for 400+ domains. I expected it to respond with the upstream certificate that the legacy mail server responds with. But it looks like I only get to use one certificate for the migration proxy. Am I reading that right in the docs?
Would we be able/need to load all 400+ certs into the proxy? How would this be handled with individual domain certs generated on an older Stalwart version moving to 0.16 or newer? That would seem to be a similar scenario to ours.
Any clarification would be appreciated.
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
This confirms Stalwart supports multiple certificates with SNI-based selection on listeners generally. Now let’s check the migration proxy docs specifically for inbound TLS.
This confirms multiple certificates are supported, selected per-connection via SNI. That directly answers the question.
The migration proxy does support multiple certificates, not just one. You declare each cert as a separate [tls.certificate.*] entry, and the proxy picks the right one per connection using SNI, so 400+ certs can be loaded side by side:
[tls.certificate.customer1]
cert = "/etc/proxy/tls/customer1.pem"
key = "/etc/proxy/tls/customer1-key.pem"
subjects = ["mail.customer1.com"]
[tls.certificate.customer2]
cert = "/etc/proxy/tls/customer2.pem"
key = "/etc/proxy/tls/customer2-key.pem"
subjects = ["mail.customer2.com"]
default = true
Selection at handshake time works from most to least specific: exact SNI match, then parent-domain match, then whichever entry is marked default (a catch-all). If you configure only one certificate, that one is used for every connection regardless of SNI, which is likely what you were seeing when testing with a single entry — that’s an artifact of the example config, not a hard limit of one cert overall.
So for your setup you would load all 400+ per-domain certs as separate named entries under [tls.certificate.*], each with its own subjects list (or let the proxy read SAN/CN automatically), and set one of them (or a wildcard) as default for any name that doesn’t match. The same mechanism applies whether the backend is a legacy Dovecot/Postfix stack or an older Stalwart 0.15-and-below deployment being migrated to 0.16+; the proxy’s inbound TLS termination is independent of what certificate the backend itself was using.
Reference: Configuration | Stalwart
This is an automated reply from the Stalwart Help Bot. Other community members may follow up if this answer is incomplete or wrong.
You’re not limited to one certificate; the example in that section just shows a single entry. Declare one [tls.certificate.*] block per certificate and the proxy picks between them per connection from the SNI name the client sends: exact match first, then the parent domain, then whichever entry is marked default = true. The subjects key is optional, so if the PEM already has the right SAN you can leave it out and let the proxy read the names from the certificate itself. 400+ entries is fine, it’s a hash lookup at handshake time, and in practice you’d generate that block with a small script over your certificate directory rather than write it by hand.
What won’t happen is the proxy relaying the upstream certificate. On IMAP, POP3, submission, ManageSieve and HTTP the proxy terminates TLS itself, so the certificate the client sees is always one of the proxy’s, never the legacy server’s. Port 25 is the exception: SMTP pass-through is bridged as an opaque byte stream, so with a plain listener the backend’s own certificate is what the peer negotiates with.
The part you should look into is renewal. The proxy has no ACME client, it only reads PEM files off disk, so whatever issues those 400 certs today keeps doing it and just needs to write the files where the proxy can read them. New or renewed certificates are picked up with a POST /config/reload against the management API, no restart and no dropped connections, since certificates are in the reloadable set (listeners and the admin listener are not). See Management API | Stalwart
For the Stalwart-to-Stalwart case it’s the same answer with one detail. ACME certificates issued by the server live in its data store, not as files, so they aren’t directly reusable by the proxy. Once the proxy owns ports 443 and 25/465/993, HTTP-01 and TLS-ALPN-01 challenges land on the proxy rather than the server, so I’d issue the proxy’s certificates with an external ACME client using DNS-01, drop the PEMs on disk and reload.