Linux / MacOS
Stalwart ships with an installation script that downloads the latest release, creates a dedicated service account, installs the binary under the standard Unix paths, writes a service unit, and starts the daemon. Root access on the target machine and outgoing HTTPS connectivity are required for the steps below.
Run the installer
Open a terminal on the target host and fetch the installation script:
$ curl --proto '=https' --tlsv1.2 -sSf https://get.stalw.art/install.sh -o install.sh
Execute the script as root:
$ sudo sh install.sh
No arguments are required. The script follows the Filesystem Hierarchy Standard and places the binary under /usr/local/bin/stalwart, the configuration file under /etc/stalwart/config.json, environment variables under /etc/stalwart/stalwart.env, application data under /var/lib/stalwart/ and log files under /var/log/stalwart/.
A dedicated stalwart service account is created if it does not already exist. The script then writes the appropriate service unit (systemd, SysV init.d, or launchd depending on the operating system), enables the service at boot, and starts it immediately.
When the standard FHS paths cannot be used (for example on a host that mounts /opt on a separate volume), a single prefix argument relocates the whole installation under that directory:
$ sudo sh install.sh /opt/stalwart
The default build includes support for SQLite, PostgreSQL, MySQL, RocksDB, S3, Azure Blob Storage, Redis, and NATS. For installations that use FoundationDB as the main data store, see FoundationDB under Additional topics below.
Retrieve the administrator credentials
Immediately after the first start, Stalwart runs in bootstrap mode. A temporary administrator account is generated with a random password and printed to standard error. Bootstrap mode is a transient phase intended only to reach the setup wizard, after which a permanent administrator account is provisioned.
The temporary password is shown exactly once at startup. Retrieve it from the service manager's log facility.
On Linux with systemd
$ sudo journalctl -u stalwart -n 200 | grep -A8 'bootstrap mode'
On Linux with SysV init
Depending on the distribution's syslog daemon, the output lands in /var/log/syslog or /var/log/messages:
$ sudo grep -A8 'bootstrap mode' /var/log/syslog 2>/dev/null \
|| sudo grep -A8 'bootstrap mode' /var/log/messages
On MacOS
$ sudo log show --predicate 'process == "stalwart"' --last 5m
The block to look for looks like this:
════════════════════════════════════════════════════════════
🔑 Stalwart bootstrap mode - temporary administrator account
username: admin
password: XXXXXXXXXXXXXXXX
Use these credentials to complete the initial setup at the
/admin web UI. Once setup is done, Stalwart will provision a
permanent administrator and this temporary account will no
longer apply.
════════════════════════════════════════════════════════════
Copy the 16-character password from the password: line. This is the only time the value appears in the logs.
To avoid relying on a log-extracted temporary password, a fixed credential can be set in advance. Edit /etc/stalwart/stalwart.env and uncomment the STALWART_RECOVERY_ADMIN line, setting the desired username and password. On the next start, the administrator credentials will match the configured value and no temporary password will be generated.
Open the setup wizard
With the bootstrap credentials in hand, open a web browser and navigate to:
http://<hostname>:8080/admin
Replace <hostname> with the hostname or IP address of the machine running Stalwart. On the machine itself, http://127.0.0.1:8080/admin works for local access. Sign in using admin as the username and the temporary password retrieved in the previous step; the setup wizard appears automatically.
The wizard presents five screens plus a final confirmation. Every field has a sensible default, so most installations only need to enter their hostname and domain and click through the rest. Deployments that prefer to skip the wizard entirely and drive the same configuration from the CLI should consult Non-interactive setup under Additional topics below.
Step 1 of 5: Server identity
Two required fields plus two optional toggles.
-
Server hostname:
The public hostname this Stalwart server answers to, for example
mail.example.com. This value appears in SMTP greetings, outgoing message headers, and TLS certificate requests. It must be a valid domain name resolving via public DNS to this server's IP address. For test or internal deployments, Stalwart accepts the RFC 6761 reserved TLDs (.test,.local,.localhost,.invalid,.example); add the hostname to/etc/hostsor to the private DNS zone that serves the deployment.By default, this hostname also becomes the base URL published in OAuth, OIDC, and JMAP discovery documents, served over HTTPS on port
443. Reverse-proxy deployments, custom HTTPS ports, and path-prefixed mounts should setSTALWART_PUBLIC_URLto override the published base URL; see Reverse proxy. -
Default email domain:
The primary domain this installation will serve, for example
example.com. Must be a valid domain name. Additional domains can be added at any time after setup. For test deployments, use a.testdomain (e.g.example.test). -
Automatically obtain TLS certificate:
Leave enabled to have Stalwart request a free TLS certificate for the server hostname from Let's Encrypt using the ACME protocol. This is the recommended option for most deployments, because mail and web clients can then connect securely on the very first start without any additional steps. Disable the checkbox only if a certificate will be uploaded manually later, or if TLS is terminated by a reverse proxy in front of Stalwart.
-
Generate email signing keys:
Leave enabled to have Stalwart generate DKIM signing keys for the default domain. DKIM cryptographically signs outgoing messages and significantly improves the chances that they are accepted by remote servers rather than classified as spam. Disable only when DKIM keys are managed externally.
When the automatically obtain TLS certificate option is enabled and an automatic DNS provider is selected in Step 5, Stalwart validates certificate requests with the DNS-01 challenge, which does not require any ports to be reachable from the internet. When DNS management is left manual in Step 5, Stalwart falls back to the TLS-ALPN-01 challenge, which Let's Encrypt validates by connecting directly to the server on TCP port 443. In that case, before restarting the server after the wizard, make sure that the machine is reachable from the public internet on port 443, that no reverse proxy or load balancer is intercepting the connection, and that the server hostname entered above resolves to this machine in public DNS. See ACME challenge types for the full picture.
Step 2 of 5: Storage
Storage is split into four independent stores. All four default to RocksDB on local disk, which is the recommended configuration for single-node installations and requires no action. Each store can be moved to an external backend later through the WebUI without reinstalling.
-
Main data storage:
Where structured data lives: email metadata, calendar events, address book entries, mailbox state, and every server settings object. The default is RocksDB. PostgreSQL, MySQL, SQLite, and FoundationDB are available for larger deployments or when a specific database is mandated by operations policy.
-
Attachment and file storage:
Where the raw bytes of email messages, attachments, and other large files are stored. Leave set to Use Data Store to keep blobs alongside metadata; switch to a dedicated backend such as S3 or Azure Blob Storage for deployments with many attachments or when long-term object storage is preferable.
-
Full-text search index:
Where the full-text search index is kept so that users can search across message bodies and attachments. Leave set to Use Data Store for single-node deployments; point to a dedicated search backend (Elasticsearch or Meilisearch) when the search workload is large enough to warrant it.
-
Cache and temporary data:
Where short-lived state lives: session caches, rate-limit counters, authentication tokens. Leave set to Use Data Store for a single node; point to Redis for faster lookups and especially for multi-node deployments where cache state must be shared across the cluster.
Step 3 of 5: Account directory
Stalwart can source user accounts from its own internal directory or delegate authentication to an external identity provider.
- Directory type:
Leave set to Use the Internal Directory for the simplest installation: accounts are created and managed through the WebUI, and the wizard generates an administrator credential on the final screen. Select an external directory (OIDC, LDAP, or SQL) for single sign-on or for integration with an existing identity system.
The choice here affects the final screen. With the internal directory, a generated administrator credential is printed when the wizard finishes. With an external directory, no credential is generated; the administrator must be promoted after the first sign-in (see External directory promotion below for the procedure).
Step 4 of 5: Logging
Log destination. Where the server writes log messages, traces, and diagnostic events.
- Log File writes rotating log files to disk under the data directory. This is appropriate whenever the server has persistent disk and rotation is handled directly by Stalwart.
- Console writes log lines to standard output. Select this for containerised deployments so the orchestrator's log driver (Docker, Kubernetes, and so on) can capture them.
Additional destinations (OpenTelemetry, journald, remote webhooks) can be added after setup through the Tracer object.
Docker users should select Console here. A file-based logger inside a container writes to an ephemeral filesystem that is lost on every container restart; the Console logger emits structured log lines to standard output, which Docker captures through its log driver.
Step 5 of 5: Automatic DNS management
The DNS records a mail server needs (SPF, DKIM, DMARC, MX, MTA-STS, autoconfig, and so on) can either be published manually by the operator or generated and maintained by Stalwart on the operator's behalf.
DNS server type. Leave set to Manual DNS Server Management to keep full control of the DNS records. Select a supported provider (Cloudflare, AWS Route 53, Google Cloud DNS, etc, or a BIND-compatible server via RFC 2136) to have Stalwart publish and update the zone entries automatically through the provider's API. See DNS provider integration for the supported providers and the credentials each one needs. The choice is not final; provider integration can be enabled, disabled, or changed later.
Retrieving the DNS zone file
When manual management is kept, Stalwart still generates the full set of records that must be published and makes them available as a ready-to-import zone file.
From the WebUI, open the Domain list (found in the WebUI under Management › Domains › Domains), click the three-dot menu next to the relevant domain, and select View DNS Zone file. The returned zone file can be pasted into the DNS provider's control panel or imported as a zone template where the provider supports it. See Setting up DNS for a per-record explanation of what each entry does.
The same records can also be retrieved from the command line; see Retrieving DNS records via CLI under Additional topics below.
Setup complete
The final screen confirms that the server has been configured. What happens next depends on the directory chosen in Step 3.
Internal directory. The wizard prints an email address and a randomly generated password for the new administrator account, for example:
onMjJfFMO83tQcCX
Write both down. The password is shown only on this screen and cannot be retrieved from the server logs afterwards. The credentials are used to sign in to the WebUI once the server has been restarted (see the platform-specific Restart section below).
External directory (OIDC / LDAP / SQL). No administrator credential is generated here, because an account from the external directory does not exist locally until it has authenticated at least once. The administrator must be promoted manually after the first sign-in; see External directory promotion under Additional topics below for the procedure.
Restart service
Once the wizard has been completed, restart Stalwart so that the new configuration takes effect. The exact command depends on the service manager that the installer wrote a unit for at install time.
On Linux with systemd (the default on most modern distributions):
$ sudo systemctl restart stalwart
On Linux with SysV init:
$ sudo service stalwart restart
On macOS (launchd):
$ sudo launchctl kickstart -k system/stalwart.mail
Continue setup
After the restart, sign in at https://<hostname>/admin with the administrator credentials printed on the final wizard screen, where <hostname> is the value entered in Step 1. If TLS or DNS for that hostname is not in place yet, sign in at http://<host>:8080/admin instead and switch to the HTTPS URL once the production listener is reachable.
- Deployments using an external directory (OIDC, LDAP, or SQL) must promote the administrator account after first sign-in. See External directory promotion under Additional topics below.
- Deployments behind a reverse proxy should follow the Reverse proxy guidance under Troubleshooting below before signing in.
- If the new HTTPS URL does not load, see HTTPS not yet reachable under Troubleshooting below.
Next steps
With the internal directory, additional user accounts can be added through the WebUI's Account Manager, or directly by creating Account objects through the WebUI administration console or the CLI. See Individual accounts for the full field reference. With an external directory (OIDC, LDAP, or SQL), accounts continue to be provisioned and authenticated through the identity provider; any Stalwart-specific overrides are set on the local Account record that is created on first sign-in.
Diagnostic output is written to the log destination configured in Step 4 of the wizard, and can also be viewed live from the WebUI under Telemetry.
Once https://<hostname>/admin is reachable and a permanent administrator is in use, disable the HTTP configuration listener so that http://<host>:8080/admin no longer accepts sign-ins. Remove the STALWART_RECOVERY_ADMIN environment variable, or delete the HTTP listener through the WebUI, then restart the service.
If something does not behave as expected, consult Troubleshooting below. Further questions can be raised on the community forum.
Before exposing the server to the public internet, review the securing your server checklist.
Setup demonstration
Troubleshooting
Certificate warning on first sign-in
When automatically obtain TLS certificate was enabled in Step 1 of the wizard, the certificate from Let's Encrypt can take a minute or two to be issued after the restart. While the request is still in flight, the browser may show a warning because the server is presenting a self-signed fallback certificate. Accepting that warning for the initial sign-in is safe; the warning disappears once the ACME certificate has been installed and the page is reloaded. When the certificate option was disabled, the self-signed fallback remains in place until a certificate is uploaded or a reverse proxy terminates TLS in front of the server.
HTTPS not yet reachable
If the configured hostname does not yet resolve in DNS, port 443 is not reachable from the browser, or the deployment cannot present a trusted certificate for any other reason, sign in at http://<host>:8080/admin for as long as STALWART_RECOVERY_ADMIN is set in the service environment. Once HTTPS is in place, switch to https://<hostname>/admin and disable the HTTP listener as described in Next steps.
If the HTTP listener has already been disabled and the deployment is locked out (for example because the configured hostname is no longer reachable, the external OIDC provider is down, or the administrator account was deleted), start the server in recovery mode (STALWART_RECOVERY_MODE=1). Recovery mode re-enables the HTTP listener on port 8080 with the path-relative issuer used during bootstrap and accepts the credentials in STALWART_RECOVERY_ADMIN, regardless of the directory state. Restart without the flag once normal sign-in is restored.
Reverse proxy
When Stalwart sits behind a reverse proxy (NGINX, Traefik, Caddy, HAProxy, or similar) and the wizard or first sign-in does not behave as expected, the issue is almost always that the URL Stalwart publishes in its OAuth and JMAP discovery documents does not match the URL clients reach the proxy on. Set STALWART_PUBLIC_URL to the public-facing base URL (scheme, host, port if non-443, path if mounted under a prefix) and restart the server. For example, STALWART_PUBLIC_URL=https://mail.example.com:8443 for a proxy listening on port 8443, or https://example.com/mail for a path-prefixed mount. The full reverse-proxy model (HTTP vs HTTPS upstream, Proxy Protocol, and per-product configuration for NGINX / Caddy / Traefik / HAProxy) is documented under Reverse proxy › Overview.
Further diagnostics
For issues that go beyond initial setup (email delivery, DMARC, authentication, and so on), consult the general troubleshooting guide or the FAQ.
Additional topics
External directory promotion
When the wizard is completed with an external directory (OIDC, LDAP, or SQL), no administrator credential is generated on the final screen, because an account from the external directory does not exist locally until it has authenticated at least once. The administrator must be promoted manually after the first sign-in using the existing recovery admin credentials. The procedure, carried out after the platform-specific restart, is:
- Keep
STALWART_RECOVERY_ADMIN=<username>:<password>set in the service environment. The variable is honoured while the server is running normally, so this does not require recovery mode. - Have the account that will become the administrator sign in once through the WebUI. Any successful authentication against the directory provisions the account locally in Stalwart.
- Sign in as the recovery administrator in a separate browser session and assign the appropriate administrator role to the newly provisioned account, either through the WebUI or through the CLI.
- Sign in as the promoted account to confirm that the assignment took effect.
- Remove
STALWART_RECOVERY_ADMINfrom the service environment and restart the server so that the backdoor credential is no longer active.
FoundationDB
A separate build with FoundationDB support is available for installations that use FoundationDB as the main data store. The default build does not include FoundationDB because multiple FoundationDB client versions exist and a single default would break deployments that rely on a different client.
Linux / macOS. Run the installation script with the --fdb flag:
$ sudo sh install.sh --fdb
Docker. No pre-built FoundationDB image is published. To produce a FoundationDB-enabled image, use the Dockerfile.fdb file available in the stalwartlabs/stalwart repository as a starting point.
Retrieving DNS records via CLI
When manual DNS management is in effect, the zone file generated by Stalwart can also be fetched from the command line. List the configured domains and read the dnsZoneFile field on the desired one:
$ stalwart-cli query domain
$ stalwart-cli get domain <id> --fields dnsZoneFile
The returned zone file can be pasted into the DNS provider's control panel or imported as a zone template where the provider supports it.
Non-interactive setup
Deployments that prefer command-line or infrastructure-as-code workflows can skip the wizard entirely and drive the same configuration from the CLI; see bootstrap mode and declarative deployments.