Configuring the proxy
The proxy is what makes the source and the new deployment appear as a single server to clients. It terminates every protocol on the public ports, identifies the account behind each connection, and forwards the session to whichever server currently holds that account. This step writes its configuration but does not start it; like the previous steps, it has no effect on existing clients and carries no downtime.
This page explains the decisions behind it and the operational pieces that have to be put in place around it. A complete, annotated configuration is published for each source, and a ready-to-edit copy ships in the proxy repository. The general reference for every option is the proxy configuration section.
The configuration for two Stalwart deployments is published as the Upgrading Stalwart example, and a ready-to-edit copy ships as resources/dovecot-postfix-trust.conf. Its two destinations are named old and new, which are the destination names used throughout the rest of this guide.
The configuration for a Dovecot and Postfix source is published as the Migrating from Dovecot example, and a ready-to-edit copy ships as resources/dovecot-postfix-trust.conf. Its two destinations are named legacy (the source) and stalwart (the new deployment); wherever the rest of this guide names the destinations old and new, substitute legacy and stalwart.
Shape of the configuration
Section titled “Shape of the configuration”Three choices define the migration topology and are worth understanding before editing the file.
The default destination is the source. Routing is driven by a mapping table that lists, for each account, which server holds it, and any account absent from the table resolves to this default. At the start of the migration the table is empty and every account therefore resolves to the source, which is correct because that is where every account still lives.
The pass-through destination for raw SMTP is the new deployment. Port 25 carries no login to route on, so it is handed in its entirety to a single backend. Directing it to the new server is what allows split delivery to work, since the new server is the one configured to deliver locally or relay as appropriate. This is independent of the per-account routing that governs the interactive protocols.
How the proxy announces each connection to its backends, and what HTTP routing it needs, depends on the source:
Both backends are addressed with forwarding = "proxy" and proxy_protocol = true, so the proxy announces the real client to each Stalwart deployment over a PROXY protocol header, matching the trust configured on the servers. On the HTTP leg, forwarded = true records the client address in the Forwarded and X-Forwarded-For headers.
Because both deployments serve HTTP and JMAP, the configuration includes HTTP routing rules, described in detail in the example. In summary, the few login and discovery paths that are unique to each Stalwart version are pinned to the deployment that serves them, the shared OAuth token endpoint is split by the web client it came from, and every other request is routed by the account its credentials identify.
The new Stalwart backend is addressed with forwarding = "proxy" and proxy_protocol = true, and forwarded = true on its HTTP leg, so the proxy announces the real client to it over a PROXY protocol header. The source backend instead uses forwarding = "xclient", because Dovecot and Postfix learn the real client address from the XCLIENT command and the IMAP ID rather than from a PROXY header. This matches the XCLIENT trust configured on the source.
Dovecot and Postfix serve no HTTP or JMAP, so there are no per-source web paths to pin and no HTTP routing rules are needed for the source. All HTTP and JMAP belong to the new server, which serves the web interface once accounts begin to move; an HTTP listener routing entirely to the new deployment can be added when a single hostname for web access is wanted, and can be omitted until then.
Mapping file
Section titled “Mapping file”The mapping file is the live record of which accounts have been migrated. It is referenced from the configuration, and its initial contents depend on whether the source serves a web interface that has to be reachable from the start:
The file starts out containing only the two entries that let the web interfaces sign in, which map each version’s OAuth client to its deployment:
webadmin oldstalwart-webui newThe file starts out empty, because the source serves no web interface that needs routing. Every account therefore falls through to the default destination, the source, until it is migrated:
# identifier destinationNo account entries are added yet. Accounts are added to this file, or set through the management API, as they are migrated, which the migration step covers. Keeping the file under version control or a backup is worthwhile, because it is the authoritative list of what has moved.
Certificates and the public hostname
Section titled “Certificates and the public hostname”The proxy presents a certificate to clients for the public hostname they already use, so the certificate and key for that hostname are placed where the configuration expects them. This is the same certificate the source presents today; reusing it means clients see no change. The proxy terminates both implicit TLS and STARTTLS with it.
The public hostname itself is not repointed at the proxy yet. That repointing, whether through DNS or by moving the address, happens at the cutover, so that the switch from the source to the proxy is a single deliberate action.
Management endpoint
Section titled “Management endpoint”The proxy exposes a small management API, protected by a bearer token, for changing mappings, invalidating caches and disconnecting accounts. A token of at least the configured minimum length is generated and stored in the file the configuration names, and the endpoint is bound to a loopback or management-only address. The migration step uses this API to cut accounts over, so confirming the token works against the running proxy is part of the next stage.
Rollback
Section titled “Rollback”The proxy has only been configured, not started, and the public hostname still resolves to the source, so there is nothing serving clients to roll back. Abandoning the migration here means the configuration file is simply never used. The proxy is first put into the serving path in the next step, which is also where the rollback procedure for a live proxy is described.