Govern sends verification, password reset, magic link and email change notices through one SMTP server. Without one (NI_AUTH_SMTP_HOST empty), nothing is sent and the request that needs mail answers 503 email_unavailable.
Settings
| Variable | Meaning |
|---|---|
NI_AUTH_SMTP_HOST | Mail server host name. Empty: delivery is off (logged at start). localhost works only when a mail server listens on the Govern host or container |
NI_AUTH_SMTP_PORT | Port, default 587 |
NI_AUTH_SMTP_STARTTLS | true (the server's default): require STARTTLS before authenticating; a server without it is refused |
NI_AUTH_SMTP_USERNAME, NI_AUTH_SMTP_PASSWORD | SMTP credentials (PLAIN, only over TLS or to localhost). Keep them in your secret store |
NI_AUTH_SMTP_FROM | The From address; its domain must be one your provider lets you send from |
NI_AUTH_SMTP_INSECURE_SKIP_VERIFY | Never in production |
NI_AUTH_SIGN_IN_URL | Where the "Sign in" button of the email link pages points (https); empty: no button |
The Docker Compose stack points at its Mailpit container by default (NI_AUTH_SMTP_HOST=mailpit, port 1025, STARTTLS off), which captures mail for local development at http://localhost:8025. Set real values for any other use.
Example: Amazon SES
In the SES console of the region you send from, verify the domain of
NI_AUTH_SMTP_FROM(DKIM records; add SPF and DMARC for deliverability).Request production access: in the SES sandbox only verified addresses receive mail, and others are refused (
reason=rejected).Create SMTP credentials in the SES console. They are an SMTP user name and password, not IAM access keys.
Configure Govern:
NI_AUTH_SMTP_HOST=email-smtp.eu-west-1.amazonaws.com NI_AUTH_SMTP_PORT=587 NI_AUTH_SMTP_STARTTLS=true NI_AUTH_SMTP_USERNAME=<SES SMTP user name> NI_AUTH_SMTP_PASSWORD=<SES SMTP password> NI_AUTH_SMTP_FROM=no-reply@<verified domain>Restart, then probe as a super admin:
POST /api/v1/admin/email/probemust answerstatus: ok. Request a verification email for a test account.
Outbound TCP 587 to the SES endpoint must be allowed. In an air-gapped installation, point NI_AUTH_SMTP_HOST at your internal relay.
Knowing whether mail works
- Logs. Every failed send is logged at error level with
event=email_delivery_failed, areasonand a hint. Reasons:not_configured,connect,timeout,tls,auth,rejected(a5xx, for example an unverified sender),temporary(a4xx),circuit_open(sends paused after repeated failures),other. The recipient is logged by domain only. - Probe. The mail server is probed at start and every 5 minutes: connect, STARTTLS, authenticate, quit. Nothing is sent.
- Metrics on
/metrics:keystone_email_deliveries_total{kind="send|probe",result="sent|failed",reason}andkeystone_email_delivery_healthy(1 or 0). Alert onkeystone_email_delivery_healthy == 0for 10 minutes. - Readiness.
GET /readyzincludes"email": "ok" | "failing" | "unconfigured" | "unknown". It never makes the node unready. - Admin view.
GET /api/v1/admin/email/health(super admins) returns the status, host, port, STARTTLS, From, whether credentials are set, the last success and failure times, the last reason and the number of consecutive failures.POST /api/v1/admin/email/probeprobes now and returns the same object; use it after a configuration change. - Circuit breaker. Selfguard guards the mail server with the
smtpbreaker: an outage fails fast instead of holding requests, and a rejected recipient does not count as an outage. An open breaker raises adependency.breaker_openfinding; it closes by itself once a trial send succeeds.