Stalwart
Stalwart is a mail and collaboration server supporting SMTP, IMAP, POP3, JMAP, WebDAV, CalDAV, and CardDAV. The HelmForge chart uses the official image and the current Stalwart 0.16 configuration model.
Key features
- Existing Secret/ConfigMap bootstrap configuration and full env
valueFrom - External Secrets Operator integration without literal credentials
- Single-node RocksDB/PVC default and fail-fast external-store HA mode
- StatefulSet identity, automatic HA PDB, anti-affinity, and topology spread
- Split HTTP and mail Services with custom listener ports and dual stack
- HTTPRoute, TCPRoute, and TLSRoute behind one disabled-by-default switch
- Optional official
stalwart-cli applyJob for an NDJSON management plan - Prometheus annotations, optional ServiceMonitor, and extra manifests for VictoriaMetrics
Install
helm repo add helmforge https://repo.helmforge.dev
helm repo update
helm upgrade --install stalwart helmforge/stalwart \
--namespace stalwart --create-namespace
The chart does not create a default password. Supply the recovery credential from an existing Secret and remove it after permanent administration is ready:
extraEnv:
- name: STALWART_RECOVERY_ADMIN
valueFrom:
secretKeyRef:
name: stalwart-recovery
key: credential
Configuration model
In Stalwart 0.16, config.json contains exactly one DataStore object. Blob, search and in-memory stores, the
Coordinator, listeners, telemetry, domains, and accounts are objects in that DataStore and are managed through the Web
UI or stalwart-cli.
Choose a generated DataStore or mount an existing ConfigMap/Secret. Credentials
inside upstream objects should use their EnvironmentVariable or File variants;
inject the referenced value through extraEnv, extraEnvFrom, or a Secret
mount. Secret values never need to enter the Helm release object.
HA and external stores
Multiple replicas require persistence.enabled=false and an external shared
DataStore. The chart rejects local RocksDB/SQLite and a local data PVC in that
mode. Use the CLI plan for shared BlobStore, SearchStore, InMemoryStore,
Coordinator, roles, and listeners.
| Concern | Common production choices |
|---|---|
| DataStore | PostgreSQL, MySQL/MariaDB, FoundationDB |
| BlobStore | S3-compatible, Azure, shared database |
| SearchStore | Meilisearch, Elasticsearch, shared database |
| InMemoryStore | Redis/Valkey, Redis Cluster or Sentinel |
| Coordinator | NATS, Redis, Redis Cluster/Sentinel, Kafka/Redpanda |
The coordinator topic is fixed to stwt.agora and must be dedicated to one
Stalwart cluster. Zenoh peer-to-peer needs a custom build and is not available
in the standard image. The current S3 object supports a custom endpoint and
region but does not expose a pathStyle property.
Declarative apply
The optional Job mounts an NDJSON plan from an existing ConfigMap or Secret,
runs apply --dry-run, then applies it with the pinned official CLI image.
Authentication comes from environment Secret refs. Changes can be activated
cluster-wide with Stalwart’s Action/ReloadSettings; a pod restart is normally
unnecessary. A generic rolloutTrigger remains available for exceptional use.
Gateway API
HTTPRoute targets HTTP/JMAP. TCPRoute exposes plaintext and STARTTLS mail protocols. TLSRoute passes implicit TLS through to Stalwart. STARTTLS cannot be SNI-routed as TLS because it begins in plaintext. The chart creates routes, not a shared Gateway, certificate, or DNS record. Controller support for TCPRoute and Experimental CRDs must be confirmed on the target cluster.
Ingress remains available for HTTP only and can coexist with Gateway API.
Health and graceful shutdown
The official /healthz/live and /healthz/ready endpoints run on the
management listener. Readiness proves DataStore initialization, not coordinator
membership, so HA acceptance must inspect coordinator behavior separately.
Stalwart drains on SIGTERM; tune terminationGracePeriodSeconds for long-lived
sessions.
Observability
Enable metrics in Stalwart’s database-backed settings, then scrape
/metrics/prometheus on the management port. Use service annotations,
ServiceMonitor, or extraManifests for VMServiceScrape/VMPodScrape without
coupling the chart to one monitoring operator.
Backup and upgrade
Back up each external backend with its native consistency and point-in-time
recovery features. stalwart-cli snapshot exports database-backed settings but
removes secrets by default; it is not a replacement for store backups.
Before upgrades, review upstream changes, capture a restorable backup, keep the CLI plan and credentials recoverable, and verify health, SMTP behavior, events, logs, and effective image digests after the StatefulSet rolls. StatefulSet updates have no Deployment-style surge.
Security scan
The chart runs as UID/GID 2000 with a read-only root filesystem, RuntimeDefault
seccomp, and all capabilities dropped except NET_BIND_SERVICE. The exception
is required because the official binary carries a file capability for standard
mail ports below 1024. Kubescape results are published in the chart README.
Values overview
| Value | Default | Purpose |
|---|---|---|
replicaCount |
1 |
StatefulSet replicas |
config.source |
generated |
Generated, existing ConfigMap, or existing Secret |
persistence.enabled |
true |
Per-pod local DataStore PVC |
extraEnv, extraEnvFrom |
[] |
Plain and Secret/ConfigMap-referenced environment |
extraPorts |
[] |
Additional named listeners |
service.http.type |
ClusterIP |
HTTP/JMAP Service exposure |
service.mail.type |
ClusterIP |
Mail protocol Service exposure |
gatewayAPI.enabled |
false |
Route master switch |
externalSecrets.enabled |
false |
ExternalSecret rendering |
applyJob.enabled |
false |
Declarative CLI plan Job |
monitoring.serviceMonitor.enabled |
false |
Prometheus Operator integration |
networkPolicy.enabled |
false |
NetworkPolicy rendering |
The complete validated contract is in
values.yaml
and
values.schema.json.