Docker and Docker Compose installed on the Ubuntu 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 is an integrated email infrastructure for secure and efficient messaging. It has the following key features.
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.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
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.
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:
caddy
is created before stalwart
.stalwart
is removed before caddy
.restart_policy
is to configure whether and how to restart containers when they exit. condition
: One of none
, on-failure
or any
. 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.
Now it’s time to deploy our docker-compose
file above using the below command
docker compose up -d
Tip
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.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 🙂