Configure Ory Kratos Self‑Service Registration with Email Verification
Step‑by‑step guide to enable Ory Kratos self‑service registration with email verification, including config snippets, verification checks, and recovery actions.
10 Aug 2026, 21:34 UTC

Desired outcome
Enable Ory Kratos to allow new users to register with a password, receive a verification email, and have their email_verified trait set to true after clicking the link.
Prerequisites
- A running Ory Kratos instance (version 0.11 or newer).
- Access to an SMTP server that can relay mail from the Kratos host; you need the host, port, username, password, and a sender address.
- An identity schema that includes at least the traits
email(string, format email) andpassword(string). The schema should also contain a boolean traitemail_verified(default false). - Administrative access to the Kratos configuration file (usually
config.yml) and permission to restart the service.
Procedure
- Enable the required self‑service methods
Editconfig.ymland set the following under theselfservicesection:
selfservice:
methods:
password:
enabled: true
profile:
enabled: true
link:
enabled: true
# optional: adjust lifespan if you need a different expiry
lifespan: 1h
Save the file.
- Configure the email courier
Still inconfig.yml, add or update thecourierblock:
courier:
smtp:
host: YOUR_SMTP_HOST
port: 587 # or 465 for TLS, 25 for plain
username: YOUR_SMTP_USER
password: YOUR_SMTP_PASSWORD
from: no-reply@example.com
email:
verify:
subject: "Verify your email address"
# You can reference a template file or embed the body directly.
# Example inline body (HTML):
body: |
Hello {{ .Identity.Traits.email }},
Please click the link below to verify your address:
This link expires in {{ .VerifiableAddress.Lifespan }}.
Replace the placeholders with your actual SMTP details and sender address. If you prefer to keep the template in a separate file, set courier.email.verify.bodyPath to the file path instead of using body.
- Apply the configuration and restart Kratos
Depending on how you run Kratos (Docker, binary, Kubernetes), reload the configuration. For a binary run:
# Send SIGHUP to the process to reload config without downtime
kill -HUP $(pgrep ory kratos)
If you cannot use SIGHUP, restart the service:
systemctl restart ory-kratos # example for a systemd service
Expected checks
- Registration request
Call the public registration endpoint (replaceKRATOS_PUBLICwith your base URL):
curl -X POST "$KRATOS_PUBLIC/selfservice/methods/password/registration" \
-H "Content-Type: application/json" \
-d '{"traits":{"email":"test@example.com","password":"Secret123!"}}'
You should receive an HTTP 200 response containing a session token. The response body will include a ui element indicating that the flow is waiting for email verification.
- Email delivery
Check the inbox of the address used in the request. You should see an email with the subject configured above and a link that contains a one‑time token. You can also verify on the SMTP side using a tool likeswaks:
swaks --to test@example.com --from no-reply@example.com \
--server YOUR_SMTP_HOST --port 587 --auth LOGIN \
--auth-user YOUR_SMTP_USER --auth-password YOUR_SMTP_PASSWORD \
--data "Subject: test\n\nThis is a test."
If the email arrives, the courier is working.
- Verification link
Copy the verification URL from the email and open it in a browser or withcurl:
curl -i "https://example.com/selfservice/methods/link/verify?token=abc123..."
Expect an HTTP 200 response and a JSON body indicating success ({"status":"success"}).
- Identity update
Using the Kratos Admin API (requires admin token), fetch the identity:
curl -H "Authorization: Bearer $ADMIN_TOKEN" \
"$KRATOS_ADMIN/admin/identities/"
In the returned JSON, the traits object should now contain "email_verified":true. No further verification emails should be sent for this identity.
Recovery and rollback options
If any step fails, you can revert the configuration changes:
- Disable the link method – set
selfservice.methods.link.enabled: falseand reload/restart Kratos. This stops verification emails from being sent while you troubleshoot. - Correct SMTP credentials – update the
courier.smtpblock with valid values and reload. - Resend verification – for an existing unverified identity, call the Admin API endpoint
POST /admin/identities//verification(if enabled) or ask the user to repeat the registration flow. - Handle expired links – the default lifespan is 1 hour. If a user clicks an expired link, the verification endpoint returns an error. The user must request a new verification email (via the same registration flow or an admin‑triggered resend).
After making a correction, repeat the expected checks to confirm that the flow works as intended.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.