Custom email templates
Customize verification, recovery, and notification email content
Kratos's courier ships built-in plain-text and HTML templates for: verification, recovery, and notification emails. Customize per-deployment.
Where they live
Templates are mounted into the Kratos container at /etc/config/kratos/courier/template/:
/etc/config/kratos/courier/template/
├── verification/code/valid/
│ ├── email.body.gotmpl
│ └── email.subject.gotmpl
├── recovery/code/valid/
│ ├── email.body.gotmpl
│ └── email.subject.gotmpl
├── recovery_code/valid/
│ ├── email.body.gotmpl
│ └── email.body.html.gotmpl
│ └── email.subject.gotmpl
└── ....gotmpl files are Go templates with access to flow-specific variables ({{ .RecoveryURL }}, {{ .Identity.Traits.email }}, etc.).
Customize
In your platform fork, edit:
prod/ciam-kratos/template/recovery_code/valid/email.body.html.gotmplprod/ciam-kratos/template/recovery_code/valid/email.subject.gotmpl
Apply branding, change wording, add support contact.
Example body template:
<!DOCTYPE html>
<html>
<body style="font-family: sans-serif">
<h2>Reset your password</h2>
<p>Hi,</p>
<p>Use this code to reset your password:</p>
<p style="font-size: 24px; font-weight: bold">{{ .RecoveryCode }}</p>
<p>If you didn't request this, ignore this email.</p>
<p>— Your app team</p>
</body>
</html>Mount via compose.prod.yml:
ciam-kratos:
volumes:
- ./prod/ciam-kratos/template:/etc/config/kratos/courier/template:roKratos reloads templates on SIGHUP via the reload sidecar.
Available variables
Per flow, you get different vars. Common:
.RecoveryURL, the verification / recovery link..RecoveryCode, short-code variant..Identity.Traits.email, recipient email..Identity.Traits.name.first, first name (if in schema)..AppName, configurable inkratos.yml.
Refer to Ory Kratos docs for the full list per flow type.
Per-domain customization
CIAM and IAM Kratos have separate template dirs. Customer-facing CIAM emails should differ in branding from internal IAM emails.
Localization
If you need multiple languages:
- Detect language from the identity (
identity.traits.locale: "fr-FR"). - Use Go template conditional:
{{ if eq .Identity.Traits.locale "fr-FR" }}
Bonjour, votre code: {{ .RecoveryCode }}
{{ else }}
Hi, your code: {{ .RecoveryCode }}
{{ end }}For many languages, this gets verbose. Consider an inline dict lookup or a custom Go function (Kratos's templating.funcs extension).
Testing locally
In dev, emails go to MailSlurper. Trigger a recovery flow:
# As any user, click "Forgot password" in Hera
# Then in MailSlurper, view the captured email
open http://localhost:5434See the rendered template. Iterate until it looks right.
Plain text fallback
Always provide both email.body.gotmpl (plain) and email.body.html.gotmpl (HTML). Some clients display plain; some block HTML. Both should convey the recovery code / link.