Deployment guide

Stalwart

Overview

Stalwart is an all-in-one, open-source mail and collaboration server. A single memory-safe Rust binary speaks SMTP, IMAP, JMAP and POP3. It runs a built-in spam and phishing filter, signs outgoing mail with DKIM, verifies SPF, DMARC and ARC on inbound mail, and runs Sieve server-side filtering scripts. A web administration console manages domains, accounts and queues.

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 SecureAnyCloud image ships Stalwart fully installed, with its web admin console fronted by nginx. On the first boot of every VM, a oneshot 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, separate from the operating system disk.

What you get:

  • Stalwart 0.16.8 installed from the official prebuilt Linux binary at /opt/stalwart/bin/stalwart, run as the stalwart system 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 on 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 and recorded in a root-only 0600 file, with no default or shared credential in the image
  • stalwart.service and nginx.service as enabled, active systemd units
  • 24/7 SecureAnyCloud support

Prerequisites

You need an active Azure subscription, an SSH key pair, and a VNet with a 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.

Configure these NSG inbound rules:

  • 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
  • 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 SecureAnyCloud, and select Create.

On the Basics tab, 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 select Review + create and Create.

Step 2 - Deploy from the Azure CLI

# Accept the Marketplace terms once per subscription (required before the first CLI deploy)
az vm image terms accept --urn sac1647283583153:stalwart-ubuntu-24-04:default:latest
az vm create \
  --resource-group <your-rg> \
  --name stalwart \
  --image sac1647283583153:stalwart-ubuntu-24-04:default:latest \
  --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. 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. 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. Stalwart serves HTTPS on 443 natively.

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. 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. It also 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 to use as an Azure Load Balancer health probe. The web admin at /admin/ also returns HTTP 200. It 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. It authenticates against /jmap/session (HTTP 200) and confirms that 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>/. Enter your administrator account name admin@example.org and continue. 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 > Accounts to see the mailboxes on your server. Provisioning creates a single administrator account, admin@example.org.

From here you create user accounts and mailboxes, groups, mailing lists and roles. Each account is a mailbox that 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 > Domains and create your real domain (for example mail.yourcompany.com).

Stalwart generates the DKIM keys for the new domain automatically. It manages the domain's 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. Other mail servers can then verify and deliver to you, and your outgoing mail is DKIM-signed and aligned with SPF and DMARC.

Step 12 - Create mailboxes and connect a mail client

Under Directory > 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. The appliance ships a self-signed certificate until you install a trusted one (Step 13), so your client may ask you 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. Direct outbound delivery to other mail servers on port 25 does not work until you do one of the following:

  • Request an outbound port 25 exception from Azure for your subscription.
  • 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. Point it at your provider's submission endpoint on port 587 and enter your provider credentials. SecureAnyCloud 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 holds your mailboxes, message blobs, accounts, DKIM keys and full-text index. It 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 > Accounts, 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 spam filtering settings.
  • 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 restart stalwart.service. Your data under /opt/stalwart/data is preserved.
  • Storage and backups. All Stalwart state lives under /opt/stalwart/data on 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

SecureAnyCloud provides 24/7 expert support for this image. Contact sales@secureanycloud.com.

Deploy on Azure

Launch Stalwart Mail Server on Ubuntu 24.04 LTS on Azure with 24/7 support from SecureAnyCloud.

View this image

Need Help?

Our support team is available 24/7.

sales@secureanycloud.com

Deploy on Azure