Published by FR Studios

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:

  1. 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.

  2. Navigate to TLS → ACME Providers. Stalwart typically creates a default provider pointing to https://acme-v02.api.letsencrypt.org/directory during initial setup. Edit this entry to ensure:

    • The challenge type is set to DNS-01.
    • The provider points to the Cloudflare DNS configuration.
  3. Navigate to TLS → Certificates. Within a few minutes, a wildcard certificate for *.yourdomain.com should 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:

TypeNamePurpose
AmailResolves to the server IP address
MX@Directs incoming mail to the mail server
TXT@SPF record (typically v=spf1 a -all)
TXT_dmarcDMARC policy
TXT_smtp._tlsMTA-STS TLS reporting
TXTv1-ed25519-...DKIM public key (ed25519 algorithm)
TXTv1-rsa-...DKIM public key (RSA algorithm)
CNAMEautoconfigE-mail client autoconfiguration for Thunderbird and Outlook
CNAMEautodiscoverE-mail client autoconfiguration for Outlook
CNAMEmta-stsMTA-STS policy endpoint
SRV_submissions._tcpPort 465 service discovery
SRV_imaps._tcpPort 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:

  1. Confirm that the DKIM TXT records are present in the Cloudflare DNS settings.
  2. 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:

  1. Navigate to Settings → Network → Listeners in the administrative interface.
  2. 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)
  3. Save the changes and restart the container.

Once configured, the listeners table under Settings → Network → Listeners should resemble the following layout:

NameProtocolBind addressesImplicit TLS
submissionSMTP[::]:587
httpHTTP[::]:8080
httpsHTTP[::]:443
sieveManageSieve[::]:4190
pop3sPOP3[::]:995
imapsIMAP4[::]:993
submissionsSMTP[::]:465
smtpSMTP[::]: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) or 587 (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:

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.