How to selfhost Stalwart mail server using docker-compose behind Caddy v2.8.4
- Rajasekhar Gundala
- 29 Jul, 2024
Stalwart is an open-source mail server solution with JMAP, IMAP4, POP3 & SMTP support; a wide range of modern features. It is written in Rust and aims to be secure, fast, robust and scalable.
Today I am going to show you how to deploy Stalwart mail server using docker compose behind Caddy proxy.
Lets start with the actual deployment…
Prerequisite
Please make sure you should fulfill the below requirements before proceeding to the actual deployment.
- Ubuntu latest server (any Linux flavor works but I am using Ubuntu here).
- Docker and Docker Compose installed on the Ubuntu Server.
Introduction to Stalwart Mail Server
Stalwart is an open-source mail server solution with JMAP, IMAP4, POP3 & SMTP support; a wide range of modern features. It is written in Rust and aims to be secure, fast, robust and scalable.
Unlike any other solution on the market, Stalwart redefines the email server landscape. Traditionally, operating a mail server required piecing together multiple components—such as MTA, message store, and spam filter—each developed over 20 years ago with their own configuration formats.
Stalwart streamlines this process into a single, efficient binary with a unified configuration. Furthermore, it is crafted in Rust, a language celebrated for its memory safety, ensuring a robust and secure email infrastructure.
Secure & Modern All-in-One Mail Server (IMAP, JMAP, POP3, SMTP).
Stalwart Mail Server Core Features
Stalwart is an integrated email infrastructure for secure and efficient messaging. It has the following key features.
- JMAP Core and JMAP Mail full compliance.
- JMAP for Sieve Scripts extension for managing Sieve scripts.
- JMAP for WebSocket, JMAP Blob Management and JMAP for Quotas extensions.
- IMAP4rev2 and IMAP4rev1 server with support for numerous extensions.
- POP3 server with extensions, STLS and SASL support.
- ManageSieve server for managing Sieve scripts.
- Built-in DMARC, DKIM, SPF and ARC support for message authentication.
- Strong transport security through DANE, MTA-STS and SMTP TLS reporting.
- Inbound throttling and filtering with granular configuration rules, sieve scripting, MTA hooks and milter integration.
- Distributed virtual queues with delayed delivery, priority delivery, quotas, routing rules and throttling support.
- Envelope rewriting and message modification.
- Comprehensive set of filtering rules on par with popular solutions.
- Statistical spam classifier with automatic training capabilities.
- DNS Blocklists (DNSBLs) checking of IP addresses, domains, and hashes.
- Collaborative digest-based spam filtering with Pyzor.
- Phishing protection against homographic URL attacks, sender spoofing and other techniques.
- Trusted reply tracking to recognize and prioritize genuine e-mail replies.
- Sender reputation monitoring by IP address, ASN, domain and email address.
- Greylisting to temporarily defer unknown senders.
- Spam traps to set up decoy email addresses that catch and analyze spam.
- Pluggable storage backends with RocksDB, FoundationDB, PostgreSQL, mySQL, SQLite, S3-Compatible, Redis and ElasticSearch support.
- Clustering support with node autodiscovery and partition-tolerant failure detection.
- Built-in, LDAP or SQL authentication backend support.
- Full-text search available in 17 languages.
- Sieve scripting language with support for all registered extensions.
- Email aliases, mailing lists, subaddressing and catch-all addresses support.
- Automatic account configuration and discovery with autoconfig and autodiscover.
- Integration with OpenTelemetry to enable monitoring, tracing, and performance analysis.
- Webhooks for event-driven automation.
- Disk quotas.
- Account, domain, group and mailing list management.
- SMTP queue management for messages and outbound DMARC and TLS reports.
- Report visualization interface for received DMARC, TLS-RPT and Failure (ARF) reports.
- Configuration of every aspect of the mail server.
- Log viewer with search and filtering capabilities.
- Self-service portal for password reset and encryption-at-rest key management.
- Encryption at rest with S/MIME or OpenPGP.
- Automatic TLS certificate provisioning with ACME using TLS-ALPN-01, DNS-01 or HTTP-01 challenges.
- OAuth 2.0 authorization code and device authorization flows.
- Two-factor authentication with Time-based One-Time Passwords.
- Application passwords (App Passwords).
- Automated blocking of hosts that cause multiple authentication errors.
- Access Control Lists.
- Rate limiting.
- Security audited.
- Memory safe.
Prepare Stalwart Mail Server Environment
To proceed further make sure that the port 25 is open on the server
Your ISP or hosting provider won’t block incoming connection to port 25, means you can receive emails from other mail servers. However, many ISP/hosting providers block outgoing connection to port 25 of other mail servers, means you can’t send emails.
Run the following command on your server where you are going to deploy Stalwart Mail Server
to check if port 25 (outbound) is blocked.
telnet gmail-smtp-in.l.google.com 25
You would see below message, which indicates a connection is successfully established.
Trying your-server-ip-address...
Connected to gmail-smtp-in.l.google.com.
Escape character is '^]'.
220 mx.google.com ESMTP 7fbef4ecsi44cewcfewdfew.135 - gsmtp
Tip
quit
and press Enter to close the connection.Proceed further after confirming the port 25 is not blocked
Let’s start creating a local folder stalwart
to persist Stalwart docker container data, /opt/stalwart-mail
for disaster recovery purpose.
Create folder in /opt
directory to persistent Stalwart data folder, /opt/stalwart-mail
cd /opt
sudo mkdir -p stalwart
Caddyfile – Stalwart Mail Server
The Caddyfile is a convenient Caddy configuration format for humans.
Caddyfile is easy to write, easy to understand, and expressive enough for most use cases.
Production-ready Caddyfile for Jekyll as Static File Server. You can use it for other static site generators as well, like Hugo.
Learn more about Caddyfile here to get familiar with it.
Use the below commands to create the Caddy
configuration file,
cd /opt
sudo touch Caddyfile
Open Caddyfile
with nano editor using sudo nano Caddyfile
Copy and paste the below code in the Caddyfile configuration file.
{
email you@example.com
default_sni stalwart
cert_issuer acme
# Production acme directory
acme_ca https://acme-v02.api.letsencrypt.org/directory
# Staging acme directory
#acme_ca https://acme-staging-v02.api.letsencrypt.org/directory
servers {
metrics
protocol h1 h2c h3
strict_sni_host on
trusted_proxies cloudflare {
interval 12h
timeout 15s
}
}
admin 0.0.0.0:2019
}
mail.example.com {
log {
output file /var/log/caddy/jekyll.log {
roll_size 20mb
roll_keep 2
roll_keep_for 6h
}
format console
level error
}
reverse_proxy stalwart:8080 {
transport http {
keepalive 300s
}
}
}
Please go to Caddy Post to get more insight to deploy it in the docker swarm cluster.
Prepare Docker Compose File
Now it’s time to create a docker compose file that contains the Caddy Reverse Proxy
service and Stalwart
service in /opt
directory. docker compose
is the configuration file in .yml
(Yet Another Markup Language).
Create a docker network caddy
, we will use it to expose application containers or internal services to outside world.
docker network create caddy
Use the below commands to create the configuration file,
cd /opt
sudo touch docker-compose.yml
Open docker-compose.yml
with nano editor using sudo nano docker-compose.yml
Copy and paste the below code in the docker-compose.yml configuration file.
Here is the full docker-compose
file.
services:
caddy:
image: rajaseg/caddy
restart: unless-stopped
container_name: caddy
ports:
- "80:80"
- "443:443"
- "2019:2019"
networks:
- caddy
volumes:
- ./Caddyfile:/etc/caddy/Caddyfile
- ./caddydata:/data
- ./caddyconfig:/config
- ./caddylogs:/var/log/caddy
- ./stalwart:/opt/stalwart-mail
stalwart:
image: stalwartlabs/mail-server:latest
container_name: stalwart
restart: unless-stopped
depends_on:
caddy:
condition: service_started
volumes:
- ./stalwart:/opt/stalwart-mail
ports:
- "4190:4190"
- "993:993"
- "143:143"
- "465:465"
- "587:587"
- "25:25"
- "8443:443"
networks:
- caddy
stdin_open: true
tty: true
volumes:
caddydata:
caddyconfig:
caddylogs:
stalwart:
beszel:
networks:
caddy:
external: true
Lets discuss about some of the configuration options above.
depends_on
depends_on expresses startup and shutdown dependencies between services.
short syntax
The short syntax variant only specifies service names of the dependencies. Service dependencies cause the following behaviors:
- Compose creates services in dependency order. In the above scenario,
caddy
is created beforestalwart
. - Compose removes services in dependency order. In the above scenario,
stalwart
is removed beforecaddy
. restart_policy
is to configure whether and how to restart containers when they exit.condition
: One ofnone
,on-failure
orany
. Default is any*.*
Compose guarantees dependency services have been started before starting a dependent service. Compose waits for dependency services to be ready before starting a dependent service.
Deploy the Docker Compose
Now it’s time to deploy our docker-compose
file above using the below command
docker compose up -d
Tip
Log in to Stalwart Mail Server Web Interface
In order to obtain the administrator account and password details execute the below command.
docker logs stalwart-container-id
Tip
docker ps
and press Enter to get the container id's
of the running services.After getting the Stalwart container id, use below command to get the log in details
docker logs stalwart-container-id
✅ Configuration file written to /opt/stalwart-mail/etc/config.toml
🔑 Your administrator account is 'admin' with password 'password'.
With this information, you can log in to the web interface at mail.example.com
.
Warning
mail.example.com
) in your DNS Management Application.Please find below images for your reference.
Below screen shots are for reference purpose.
I hope you enjoyed this post. Please share your thoughts or feedback on it by commenting.
Stay tuned for other deployments 🙂