Kubernetes
Run Nixt DNA Server on Kubernetes with its Helm chart — the secrets it needs, the values to set, the first organisation, and upgrades.
The Helm chart runs Nixt DNA Server as groups of pods, each group a StatefulSet running some of the server’s roles. Every pod shares one PostgreSQL database and one S3-compatible bucket.
Before you start
You need:
- a Kubernetes cluster that can give a Service a public address (type
LoadBalancer), for the mail ports; - PostgreSQL 15 or later, reachable from the cluster;
- an S3-compatible bucket;
- the chart,
nixt-server, which comes with each release in the offline bundle.
1. Create the secrets
The chart reads three secrets. Create them in the namespace you install into.
| Secret | Key | What it holds |
|---|---|---|
nixt-store | url | The PostgreSQL connection URL. |
nixt-keys | kek | The key-encryption key: 32 random bytes as 64 hexadecimal characters. Keep a copy somewhere safe and apart from your backups: without it, no stored mail can be read. |
nixt-tls | tls.crt, tls.key | Only if you supply your own certificate (tls.kind: files). With the default, acme, each pod obtains its own. |
kubectl create secret generic nixt-store --from-literal=url='postgres://nixt:<password>@db.example.com/nixt'
kubectl create secret generic nixt-keys --from-literal=kek="$(openssl rand -hex 32)"
If the pods have no cloud identity that can reach the bucket, also create a secret with AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY, and name it in blobs.credentialsSecret.
2. Set the values
Write a values file with at least these:
hostname: mail.example.com
blobs:
bucket: example-mail
region: eu-central-1
tls:
contact: postmaster@example.com
| Value | Default | What it does |
|---|---|---|
hostname | mail.example.org | The server’s host name, which every certificate includes. |
roleGroups | edge (2 pods), storage (3), control (2) | Which roles each group runs and how many pods it has. edge receives and delivers mail; storage serves mailboxes and calendars; control takes submissions and runs the admin API. |
store.urlSecret | nixt-store | The secret holding the database URL. |
blobs.bucket, blobs.region, blobs.endpoint | empty | The bucket. Set endpoint for a store other than Amazon S3. |
keys.secret | nixt-keys | The secret holding the key-encryption key. |
tls.kind | acme | acme to obtain certificates automatically, or files to use tls.secret. |
tls.names | empty | More host names for the certificate, beyond hostname. |
mtaSts.mode | testing | Your MTA-STS policy. Move to enforce once TLS reports show nothing failing. |
listeners | one Service per listener | The Service type and port for each of mx, submission, submissions, imap, imaps, managesieve, jmap, admin, serve and acme. The admin API is ClusterIP, inside the cluster only. |
resolver.sidecar | true | Runs a validating DNS resolver beside each pod, which the server trusts for DNSSEC. |
resources | 500m CPU and 512 MiB requested; 2 CPU and 2 GiB limit | Per pod. |
podDisruptionBudget.maxUnavailable | 1 | Keeps a voluntary disruption from taking more than one pod at a time. |
3. Install
helm install mail ./nixt-server -f values.yaml
Helm prints which groups it made and what each needs before it is ready.
4. Create the first organisation
Run the admin command line in a control pod. With the release named mail, the first control pod is mail-nixt-server-control-0:
kubectl exec mail-nixt-server-control-0 -- /nixt-server admin post tenants name=example-org
kubectl exec mail-nixt-server-control-0 -- /nixt-server admin post tenants/1/domains name=example.com
kubectl exec mail-nixt-server-control-0 -- /nixt-server admin post tenants/1/accounts address=admin@example.com "displayName=Administrator"
kubectl exec mail-nixt-server-control-0 -- /nixt-server admin put tenants/1/accounts/1/password "password=<a password of 14 or more characters>"
Each answer includes the new record’s id. The commands above assume the first organisation is 1 and its first account 1; use the ids the answers give. Domains and accounts explains each command.
5. Publish DNS
Print the records the domain needs:
kubectl exec mail-nixt-server-control-0 -- /nixt-server dns example.com
Point the domain’s MX record at the address of the mx Service, and the host names in tls.names at the serve Service, with acme on port 80, so that certificates can be obtained. kubectl get services lists each listener’s Service and its address. See DNS records.
Upgrades
Take a snapshot of the database, set upgrade.backup to its name, and run helm upgrade. Before any pod is replaced, a job of the new release prepares the database. If it refuses, Helm stops with nothing changed. See Upgrades.
Something unclear or out of date on this page? Tell us.