Stalwart on Coolify
How I configured Stalwart Mail Server inside Coolify's Docker environment
Self-hosting an email server is a common milestone for system administrators and hobbyists. When deploying Stalwart within a Coolify environment on a Virtual Private Server (VPS), the installation process can present several undocumented challenges. This guide does not replace the official Stalwart installation documentation, which is highly comprehensive. Instead, it documents the specific integration issues encountered when running Stalwart in a Coolify-managed Docker environment, alongside practical solutions to resolve them.
The configurations described here were tested on a Contabo VPS utilizing Cloudflare DNS. While specific hosting providers may vary, the core Coolify and Docker configuration principles remain applicable.
Docker Compose Configuration i used on my VPS running coolify
The following Docker Compose configuration serves as a functional baseline for the deployment:
version: '3.9'
services:
stalwart:
image: 'stalwartlabs/stalwart:v0.16'
container_name: stalwart
restart: unless-stopped
dns:
- 1.1.1.1
- 8.8.8.8
environment:
STALWART_PUBLIC_URL: 'https://mail.yourdomain.com'
STALWART_RECOVERY_ADMIN: 'admin:ChangeThisPassword'
volumes:
- 'stalwart-etc:/etc/stalwart'
- 'stalwart-data:/var/lib/stalwart'
ports:
- '8080:8080'
- '25:25'
- '587:587'
- '465:465'
- '143:143'
- '993:993'
- '110:110'
- '995:995'
- '4190:4190'
networks:
- stalwart-net
networks:
stalwart-net:
driver: bridge
volumes:
stalwart-etc: null
stalwart-data: null A notable omission in this configuration is port 443, which is addressed in the subsequent section.
Network Integration and the Port 443 Conflict
During the creation of a Docker Compose service in Coolify, the option “Connect to Predefined Network” is enabled by default. This option links the container to Coolify’s shared proxy network to facilitate reverse proxying. Depending on the Coolify version and installation settings, this proxy is typically managed by Traefik (older installations) or Caddy (modern installations).
For a standard Stalwart deployment, this option should remain disabled. (Alternatively, in older versions of Coolify, some guides recommended connecting to the predefined network to proxy the HTTP interface. Refer to this guide by Aldert Vaandering for further context on that method.)
Coolify’s proxy binds to ports 80 and 443 on the host system to route incoming HTTP/HTTPS web traffic. If Stalwart is configured to expose port 443 on the host while the proxy network is active, a port collision occurs. This results in the following deployment error:
Error response from daemon: driver failed programming external connectivity
on endpoint stalwart: Bind for 0.0.0.0:443 failed: port is already allocated Because the proxy (such as Caddy or Traefik) binds to port 443 on the host interface, Stalwart cannot bind to it directly.
To resolve this, do not expose port 443 in the Stalwart configuration. Instead, mail protocols (SMTP on ports 25, 465, 587; IMAP on ports 143, 993) bypass the HTTP proxy entirely. The primary architectural patterns to obtain and configure TLS certificates under this restriction from the docs from my understanding is as follows.
TLS Certificate Acquisition via DNS-01 Challenge
Acquiring TLS certificates automatically requires understanding the Automated Certificate Management Environment (ACME) protocol, which verifies domain ownership before issuing Let’s Encrypt certificates.
The default challenge method, TLS-ALPN-01, requires Let’s Encrypt to establish a connection to the server on port 443. Because the proxy binds to port 443, Stalwart cannot answer this challenge.
The DNS-01 challenge offers an alternative validation method. Rather than connecting to the server directly, Let’s Encrypt verifies a specific TXT record in the domain’s DNS zone. Stalwart automates this record creation using a Cloudflare API token, enabling certificate issuance without exposing port 443.
To configure this in the Stalwart administrative interface:
Navigate to Network → DNS → DNS Providers and configure Cloudflare with an API token. This token should have scoped permissions, specifically Zone → DNS → Edit for the target domain. For security reasons, do not use the Global API Key.
Navigate to TLS → ACME Providers. Stalwart typically creates a default provider pointing to
https://acme-v02.api.letsencrypt.org/directoryduring initial setup. Edit this entry to ensure:- The challenge type is set to DNS-01.
- The provider points to the Cloudflare DNS configuration.
Navigate to TLS → Certificates. Within a few minutes, a wildcard certificate for
*.yourdomain.comshould generate, covering the necessary mail subdomains.
A successfully populated Account URI field, such as https://acme-v02.api.letsencrypt.org/acme/acct/XXXXX, indicates that Stalwart has registered with Let’s Encrypt.
DNS Records Configuration
When Cloudflare is configured as the DNS provider, Stalwart attempts to automate the creation of the necessary DNS records. Post-installation, the following records should be visible in the Cloudflare dashboard:
| Type | Name | Purpose |
|---|---|---|
| A | mail | Resolves to the server IP address |
| MX | @ | Directs incoming mail to the mail server |
| TXT | @ | SPF record (typically v=spf1 a -all) |
| TXT | _dmarc | DMARC policy |
| TXT | _smtp._tls | MTA-STS TLS reporting |
| TXT | v1-ed25519-... | DKIM public key (ed25519 algorithm) |
| TXT | v1-rsa-... | DKIM public key (RSA algorithm) |
| CNAME | autoconfig | E-mail client autoconfiguration for Thunderbird and Outlook |
| CNAME | autodiscover | E-mail client autoconfiguration for Outlook |
| CNAME | mta-sts | MTA-STS policy endpoint |
| SRV | _submissions._tcp | Port 465 service discovery |
| SRV | _imaps._tcp | Port 993 service discovery |
It is essential that all mail-related A records, such as mail, smtp, and imap, are set to DNS only (bypassing the Cloudflare proxy). Cloudflare does not proxy standard mail protocols like SMTP or IMAP.
The full zone file configuration can be inspected in the Stalwart interface under Management → Domains → [your domain] → View DNS Zone file.
DKIM Configuration
DomainKeys Identified Mail (DKIM) provides cryptographic verification for outgoing email, which prevents spoofing and improves deliverability.
Enabling the option to generate email signing keys during the setup wizard prompts Stalwart to create both ed25519 and RSA DKIM key pairs. The system publishes the corresponding public keys to the Cloudflare DNS zone automatically.
To verify successful configuration:
- Confirm that the DKIM TXT records are present in the Cloudflare DNS settings.
- Send a test message to an external analysis service, such as mail-tester.com, to verify the DKIM signature headers.
Establishing the Port 587 Listener
By default, Stalwart configures listeners for port 465 (implicit TLS) and port 25 (inbound SMTP). It does not automatically configure port 587 (explicit TLS/STARTTLS). Because many mail clients attempt to connect via port 587 first during autoconfiguration, this omission can cause outbound message transmission to fail.
To configure the port 587 listener:
- Navigate to Settings → Network → Listeners in the administrative interface.
- Apply the following settings to create the listener:
- Listener ID:
submission - Protocol: SMTP
- Bind address:
[::]:587 - Enable TLS: Enabled
- Implicit TLS: Disabled (ensuring STARTTLS configuration)
- Listener ID:
- Save the changes and restart the container.
Once configured, the listeners table under Settings → Network → Listeners should resemble the following layout:
| Name | Protocol | Bind addresses | Implicit TLS |
|---|---|---|---|
submission | SMTP | [::]:587 | ❌ |
http | HTTP | [::]:8080 | ❌ |
https | HTTP | [::]:443 | ✔ |
sieve | ManageSieve | [::]:4190 | ❌ |
pop3s | POP3 | [::]:995 | ✔ |
imaps | IMAP4 | [::]:993 | ✔ |
submissions | SMTP | [::]:465 | ✔ |
smtp | SMTP | [::]:25 | ❌ |
Client Verification via Thunderbird
Mozilla Thunderbird is a useful tool for verifying the installation due to its support for autoconfiguration. If the autoconfig DNS records are set correctly, the client will resolve the server configuration automatically.
The resolved settings should reflect the following:
- Incoming Connection:
mail.yourdomain.com:993(SSL/TLS) - Outgoing Connection:
mail.yourdomain.com:465(SSL/TLS) or587(STARTTLS)
If the autoconfiguration succeeds but mail delivery fails, the underlying issue is typically routing or firewall restrictions.
Troubleshooting Outbound Mail Queue
If outbound mail is sent without immediate client errors but fails to reach the destination, check the outbound mail queue via the Stalwart administrative interface under MTA → Queue. Reviewing the specific delivery error associated with a queued message can identify the root cause.
A common issue on new VPS instances is the blocking of outbound port 25. While incoming traffic on port 25 is typically open, outbound traffic to external mail servers on the same port is frequently blocked by hosting providers as an anti-spam measure.
For Contabo instances, review the provider dashboard firewall or security group settings to ensure outbound TCP traffic on port 25 is permitted. Other hosting providers may require contacting support directly to request the removal of this restriction.
Additional diagnostics can be performed using the MXToolbox SMTP Test by querying smtp:mail.yourdomain.com. This test verifies port 25 accessibility, TLS functionality, banner correctness, and ensures the mail server is not configured as an open relay.
For real-time diagnostic logs, the Telemetry → Live Telemetry view in the Stalwart dashboard displays active connection attempts.
Resolving Reverse DNS (PTR Record) Mismatches
External testing tools may issue a warning stating: “Reverse DNS does not match SMTP Banner.” This occurs when the mail server announces its hostname as mail.yourdomain.com during the SMTP handshake, but a reverse DNS lookup (PTR) on the server’s IP address returns the provider’s default hostname.
While this mismatch may not prevent transmission, it negatively impacts sender reputation and deliverability. The issue can be resolved by creating a PTR record pointing to mail.yourdomain.com in the VPS provider’s administration panel.
Further Configuration
Once the core services are verified, refer to the official Stalwart documentation for advanced configurations:
- Security Hardening: Disabling unencrypted administrative endpoints after HTTPS verification.
- DNS Specification Details: Exhaustive reference for mail-related DNS record attributes.
- DKIM Signature Customization: Setting up key rotation schemes.
- Spam Filter Optimization: Configuring Stalwart’s native spam detection engine.
- MTA-STS Implementation: Enforcing TLS for inbound SMTP traffic.
Deploying a self-hosted mail server involves significant complexity. However, Stalwart automates many of the initial requirements such as certificate acquisition and DKIM key generation. The remaining deployment challenges generally relate to environment-specific networking, hosting provider firewall policies, and reverse DNS routing.