Stalwart Mail Server on Ubuntu 24.04 on Azure User Guide
Overview
Stalwart is the all in one open source mail and collaboration server. A single memory safe Rust binary speaks SMTP, IMAP, JMAP and POP3, runs a built in spam and phishing filter, signs outgoing mail with DKIM and verifies SPF, DMARC and ARC on inbound mail, runs Sieve server side filtering scripts, and serves a web administration console for managing domains, accounts and queues. Because Stalwart keeps its settings, accounts and message store in an embedded RocksDB datastore, there is no separate database to run and far fewer daemons to patch than the traditional Postfix, Dovecot, SpamAssassin and OpenDKIM stack.
The cloudimg image ships Stalwart fully installed with its web admin console fronted by nginx, so a complete private mail platform is running within minutes of launch. On the first boot of every VM a one shot service provisions the server, generates a unique administrator password, regenerates the TLS private key, and writes the login to a root only file. The RocksDB datastore lives on a dedicated Azure data disk mounted at /opt/stalwart/data, kept separate from the operating system disk.
What is included:
- Stalwart 0.16.8 installed from the official prebuilt Linux binary at
/opt/stalwart/bin/stalwart, run as thestalwartsystem service - The embedded RocksDB datastore (no external database required) on a dedicated Azure data disk at
/opt/stalwart/data - The web admin console and JMAP interface served on port 80 through nginx (the Stalwart HTTP listener is bound to the loopback address
127.0.0.1:8080) - HTTPS 443 served natively by Stalwart, with mail listeners on SMTP 25, submission over STARTTLS 587, submission over TLS 465 and IMAP over TLS 993
- A per instance self signed TLS certificate for the mail listeners, generated on first boot
- A unique administrator account generated on first boot, recorded in a root only
0600file, with no default or shared credential in the image stalwart.serviceandnginx.serviceas systemd units, enabled and active- 24/7 cloudimg support
Prerequisites
An active Azure subscription, an SSH key pair, and a VNet plus subnet in the target region. Standard_B2s (2 vCPU / 4 GiB RAM) is a reasonable starting point for a small mailbox count; size up for busier mail. NSG inbound rules: allow 22/tcp from your management network for SSH, 80/tcp and 443/tcp for the web admin and HTTPS, 25/tcp for inbound mail, 465/tcp and 587/tcp for mail submission, and 993/tcp for IMAP.
Receiving mail on a real domain also needs public DNS records (MX, SPF, DKIM and DMARC) that point at your VM. Stalwart generates the exact records for you in the admin console, as shown later in this guide.
Step 1 - Deploy from the Azure Marketplace
Sign in to the Azure Portal, choose Create a resource, search the Marketplace for Stalwart by cloudimg, and select Create. On Basics pick your subscription, resource group, region and size; under Administrator account choose SSH public key and paste your key; under Inbound port rules allow SSH (22) and HTTP (80). After creation, add inbound rules for 443, 25, 465, 587 and 993. Review the dedicated data disk on the Disks tab, then Review + create -> Create.
Step 2 - Deploy from the Azure CLI
az vm create \
--resource-group <your-rg> \
--name stalwart \
--image <marketplace-image-urn> \
--size Standard_B2s \
--admin-username azureuser \
--ssh-key-values ~/.ssh/id_ed25519.pub \
--vnet-name <your-vnet> --subnet <your-subnet> \
--public-ip-sku Standard
Then open the mail and web ports in the network security group:
az network nsg rule create --resource-group <your-rg> --nsg-name <your-nsg> \
--name allow-mail --priority 1010 --access Allow --protocol Tcp --direction Inbound \
--destination-port-ranges 80 443 25 465 587 993
Step 3 - Connect to your VM
ssh azureuser@<vm-public-ip>
Stalwart and nginx are already installed and running. The first boot provisioning has generated your unique administrator login.
Step 4 - Confirm the services are running
systemctl is-active stalwart.service nginx.service
sudo ss -tlnp | grep -E ':25|:465|:587|:993|:443|:8080|:80'
Both services report active. nginx listens on port 80 and fronts the web admin, while the Stalwart HTTP listener is bound only to the loopback address 127.0.0.1:8080. The mail listeners are bound on SMTP 25, submission 465 and 587, and IMAP over TLS 993, with HTTPS on 443 served natively by Stalwart.

Step 5 - Retrieve your admin login
The administrator password is generated uniquely on the first boot of your VM and written to a root only file:
sudo cat /root/stalwart-credentials.txt
This file contains stalwart.admin.user (the administrator account, admin@example.org), stalwart.admin.pass and the web admin URL. The file is mode 0600 and owned by root, so only an administrator can read it. Store the password somewhere safe. The binary reports version 0.16.8, and the RocksDB datastore that holds your mail lives on the dedicated Azure data disk mounted at /opt/stalwart/data.

Step 6 - Confirm the health endpoint and web admin
nginx serves an unauthenticated health endpoint for load balancers and probes, and serves the web admin single page application on port 80:
curl -s -o /dev/null -w 'healthz -> HTTP %{http_code}\n' http://localhost/healthz/live
curl -s -o /dev/null -w 'admin -> HTTP %{http_code}\n' http://localhost/admin/
/healthz/live returns HTTP 200 without authentication, so it is safe for an Azure Load Balancer health probe. The web admin at /admin/ also returns HTTP 200 and loads the console in the browser, where you sign in.

Step 7 - Verify authentication from the command line
Stalwart's JMAP session endpoint authenticates the administrator account. The check below reads your per VM password from the credentials file so it is never printed, authenticates against /jmap/session (HTTP 200), and confirms a wrong password is rejected (HTTP 401):
PASS=$(sudo grep '^stalwart.admin.pass=' /root/stalwart-credentials.txt | cut -d= -f2-)
curl -s -o /dev/null -w 'correct password -> HTTP %{http_code}\n' -u "admin@example.org:$PASS" http://localhost/jmap/session
curl -s -o /dev/null -w 'wrong password -> HTTP %{http_code}\n' -u 'admin@example.org:definitely-wrong-pass' http://localhost/jmap/session
The correct per VM password prints HTTP 200 (authenticated); a wrong password prints HTTP 401 (rejected), which confirms the temporary provisioning back door has been removed.

Step 8 - Sign in to the web admin
Browse to http://<vm-public-ip>/ in your browser. Enter your administrator account name admin@example.org and continue, then enter the password from Step 5 on the sign in page and select Sign in. You land on the Stalwart admin console, where you manage domains, accounts, queues, reports and server settings.

Step 9 - Review your accounts and mailboxes
Open Directory then Accounts to see the mailboxes on your server. The provisioning creates the single administrator account admin@example.org. From here you create user accounts and mailboxes, groups, mailing lists and roles. Each account is a mailbox any IMAP or JMAP client can sign in to.

Step 10 - Add your real domain
The image is provisioned with the temporary domain example.org so the server is complete on first boot. To serve mail for your own domain, open Domains then Domains and create your real domain (for example mail.yourcompany.com). Stalwart generates the DKIM keys for the new domain automatically and manages its DNS, DKIM and TLS certificate settings from this view.

Step 11 - Publish your DNS records
Open the domain's actions menu and choose View Zone File to see the exact DNS records to publish at your DNS provider. Stalwart shows the DKIM public keys (both ed25519 and RSA), the SPF record, the MX record, the DMARC policy and the service SRV records. Publish these at your DNS host so other mail servers can verify and deliver to you, and so your outgoing mail is DKIM signed and SPF and DMARC aligned.

Step 12 - Create mailboxes and connect a mail client
Under Directory then Accounts, create a mailbox for each user on your domain and set a password. Then connect any standards compliant mail client (for example Thunderbird, Apple Mail or a JMAP client) using your VM's public IP or hostname:
- IMAP over TLS on port 993
- Submission over STARTTLS on port 587, or submission over implicit TLS on port 465
- JMAP over HTTPS on port 443
The client signs in with the mailbox address and password you set. Because the appliance ships a self signed certificate until you install a trusted one (Step 13), your client may prompt to trust the certificate on first connection.
Step 13 - Set your hostname and enable Let's Encrypt TLS
For production, set your server hostname and replace the self signed certificate with a trusted one. In the admin console, set the server hostname to a name that resolves to your VM, then enable Stalwart's built in ACME support so it obtains and renews a Let's Encrypt certificate automatically. Point a DNS A record at your VM's public IP first, so the ACME HTTP challenge on port 80 can validate. Once ACME is enabled, HTTPS on 443 and the mail listeners on 465, 587 and 993 present the trusted certificate.
Step 14 - Sending mail: outbound port 25 on Azure
Important: Azure blocks OUTBOUND port 25 by default for most subscriptions. Receiving mail and IMAP access work out of the box, but direct outbound delivery to other mail servers on port 25 will not, until you either:
- request an outbound port 25 exception from Azure for your subscription, or
- relay outbound mail through a smart host on port 587, such as SendGrid or Amazon SES.
To relay through a smart host, add an outbound relay host in the Stalwart admin console pointing at your provider's submission endpoint on port 587 with your provider credentials. cloudimg support can walk you through either option. Inbound mail, IMAP and JMAP are unaffected.
Step 15 - Confirm data lives on the dedicated disk
Stalwart's RocksDB datastore, which holds your mailboxes, message blobs, accounts, DKIM keys and full text index, is stored under /opt/stalwart/data on the dedicated Azure data disk, so it survives OS changes and can be resized independently:
findmnt /opt/stalwart/data
The mount is backed by a separate Azure data disk captured into the image and re-provisioned on every VM.
Maintenance
- Password: the admin password is set on first boot. Change it in the web admin under Directory then Accounts, or by editing the administrator account.
- Spam and phishing filter: the built in filter is enabled by default. Tune its rules and thresholds in the admin console under the settings for spam filtering.
- Sieve filtering: create server side Sieve scripts for mailbox rules (sorting, forwarding, vacation replies) from the admin console.
- TLS and certificates: the appliance generates a self signed certificate per instance. For production, set a real hostname and enable Let's Encrypt ACME (Step 13) so the mail and HTTPS listeners present a trusted certificate.
- DKIM, SPF, DMARC and ARC: Stalwart signs outgoing mail with DKIM and verifies SPF, DMARC and ARC on inbound mail. Publish the DNS records from Step 11 so remote servers trust your mail and your domain is aligned.
- Queues and reports: monitor the mail queue and delivery reports from the admin console under Emails and Reports.
- Upgrades: the binary lives at
/opt/stalwart/bin/stalwart; replace it with a newer release and restartstalwart.service. Your data under/opt/stalwart/datais preserved. - Storage and backups: all Stalwart state lives under
/opt/stalwart/dataon the data disk; snapshot or back up that volume to protect your mailboxes, message store and DKIM keys. - Security patches: unattended-upgrades remains enabled so the OS continues to receive security updates automatically.
Support
cloudimg provides 24/7 expert support for this image. Contact support@cloudimg.co.uk.