All posts
authoryself-hosted

Ory Kratos: A Practical Guide for Full-Stack Developers

A practical guide to Ory Kratos — the API-first, self-hosted identity server, and when full control over the auth flow is worth the setup cost.

SR

Suhail Roushan

August 6, 2026

·
5 min read
·
0 views

Ory Kratos ships with zero UI on purpose — it's a headless identity server that handles the entire identity lifecycle behind an API, leaving you in complete control of every pixel your users see.

Ory Kratos is an open-source, API-first identity and user management server providing registration, login, account recovery, verification, and profile management flows — all driven by configurable, self-describing JSON flows your frontend renders however you want. Unlike Auth.js or Better Auth, which run inside your application process, Kratos runs as a separate service you deploy and manage, closer in architecture to how a company like Okta or Auth0 is built internally.

Why Ory Kratos Matters (and When to Skip It)

Kratos is built for organizations that need complete control over both the identity data model and the entire UI/UX of every auth flow, without vendor lock-in to a hosted SaaS provider. Its flow-based API design means your frontend fetches a "flow" describing exactly what fields and validation rules apply at that step, and renders it however fits your design system — genuinely more flexible than embedding pre-built components.

Skip Kratos for smaller projects or teams without dedicated infrastructure capacity — it requires deploying and operating a real service (plus its own database), which is meaningfully more operational overhead than an npm package like Better Auth or a hosted provider like Clerk. The flexibility is real, but it's not free.

Getting Started with Ory Kratos

Kratos runs as a separate deployed service; your frontend interacts with its flow-based API:

// initiate a login flow
const flow = await fetch("https://kratos.yourapp.com/self-service/login/browser", {
  headers: { Accept: "application/json" },
}).then((r) => r.json());

// flow.ui.nodes describes exactly which fields to render (email, password, csrf token)

// submit the flow
const result = await fetch(flow.ui.action, {
  method: "POST",
  headers: { "Content-Type": "application/json" },
  body: JSON.stringify({
    method: "password",
    identifier: email,
    password: password,
    csrf_token: getCsrfTokenFromFlow(flow),
  }),
});

A kratos.yml config defines identity schema and enabled methods:

identity:
  default_schema_id: default
  schemas:
    - id: default
      url: file:///etc/config/identity.schema.json

selfservice:
  methods:
    password:
      enabled: true
    oidc:
      enabled: true
      config:
        providers:
          - id: google
            provider: google
            client_id: ${GOOGLE_CLIENT_ID}
            client_secret: ${GOOGLE_CLIENT_SECRET}

Core Ory Kratos Concepts Every Developer Should Know

Flows are the central abstraction. Every user action (login, registration, recovery, verification, settings update) is modeled as a "flow" — a stateful, multi-step process your frontend fetches, renders, and submits back to. This design cleanly separates identity logic (owned by Kratos) from presentation (owned entirely by you).

Identity schemas define your user data model as JSON Schema, giving you full control over what fields exist and how they're validated, unlike hosted providers with a fixed user object shape:

{
  "$id": "https://schemas.ory.sh/presets/kratos/identity.email.json",
  "type": "object",
  "properties": {
    "traits": {
      "type": "object",
      "properties": {
        "email": { "type": "string", "format": "email" },
        "name": { "type": "object", "properties": { "first": { "type": "string" } } }
      }
    }
  }
}

Sessions are managed via secure cookies issued by Kratos, validated by your backend through a session-check API call — your application services trust Kratos as the source of truth for "is this request authenticated."

Self-service flows cover the full identity lifecycle, not just login — recovery, verification, and settings updates all follow the same flow pattern, meaning you don't need separate ad-hoc implementations for each.

Common Ory Kratos Mistakes and How to Fix Them

Mistake 1: underestimating the operational overhead. Kratos requires its own database, its own deployment, its own monitoring — teams that adopt it expecting an npm-package-level integration effort are often surprised. Fix: budget real infrastructure time, or use Ory Network (the hosted version) if self-hosting operational cost isn't worth it for your team's size.

Mistake 2: not validating the CSRF token from the flow. Skipping this on flow submission opens a real CSRF vulnerability. Fix: always include the csrf_token node value from the fetched flow in your submission payload.

Mistake 3: building custom UI without handling all flow states. A login flow can return validation errors, require additional steps (2FA), or expire — a UI that only handles the happy path breaks on any of these. Fix: render based on the flow's actual ui.messages and node states, not just a static form assumption.

When Should You Use Ory Kratos Instead of Better Auth or Auth0?

Use Kratos when you need complete UI control, a fully custom identity schema, and are prepared to operate it as infrastructure — typically larger engineering teams or companies with strict design/compliance requirements around the auth experience. Use Better Auth or Clerk when a library/hosted-component approach with less operational overhead fits better, which covers the large majority of projects.

Ory Kratos in Production

Consider Ory Network (the managed/hosted version of Kratos) if the self-hosting operational burden isn't worth it for your team — you get the same flow-based flexibility without running the infrastructure yourself. If self-hosting, invest in proper monitoring and backup for the Kratos database specifically; it's now a critical-path service for every login in your application, and treating it as an afterthought operationally is a real production risk.

If your team is weighing Kratos, honestly assess whether you actually need full UI/schema control — if a component library covers your design needs, that operational overhead may not be worth paying.

Related posts

Written by Suhail Roushan — Full-stack developer. More posts on AI, Next.js, and building products at suhailroushan.com/blog.

Get in touch