Skip to main content
What Database Should You Use With Clerk? (2026)
Engineering

What Database Should You Use With Clerk? (2026)

Clerk gets sign-in working in an afternoon, then leaves you with the question its quickstart skips: where does everything else live, and how does that database know who is asking? Choose without a plan and you end up writing webhook sync code you didn't need, or shipping a Row-Level Security policy that quietly returns nothing because it reads a claim Clerk has since renamed.

What database should you use with Clerk?

Use Postgres. For most SaaS products the best single pairing is Clerk with Postgres on Supabase, which accepts Clerk session tokens natively so Row-Level Security can enforce each user's and organisation's access; choose Neon if only your server code touches the database, or Convex if you want a realtime TypeScript backend instead of SQL.

Clerk doesn't hold your application data and doesn't care which database you pick. It stores users, organisations, memberships and sessions; your tables store Clerk's IDs. What the choice really decides is where authorisation is enforced: by the database verifying Clerk's token itself (Supabase, Neon's Data API, Convex), or by your server code checking the session before every query (which works with any database at all).

How do Supabase, Neon, Convex, MongoDB and RDS compare with Clerk?

Prices are in USD as of October 2026, from each vendor's pricing page. Vendors change pricing, so check before you commit.

SupabaseNeonConvexMongoDB AtlasRDS or any Postgres
How auth reaches the databaseClerk is a native third-party auth provider; RLS reads the session tokenServer-side checks through an ORM; optional Data API verifies Clerk tokens via JWKS for RLSFirst-party Clerk integration; checks live in your query and mutation functionsNo Clerk integration; server-side checks onlyServer-side checks; optional RLS from a per-request setting
Setup effortLow: one setup step in each dashboardLow with an ORM; moderate with the Data APILow: activate in Clerk, add one config fileLow, but every query carries its own filterModerate: networking and pooling come first
Org multi-tenancyRLS on the token's o.id claimorg_id filter in code, or RLS via the Data APIOrg checks inside each functionorgId on every document, filtered in codeorg_id plus RLS set from the session's org
RealtimeYes, and Realtime honours RLSNewer service, not yet in LondonYes: every query is reactiveNo client realtime; build on change streamsNone built in
Entry priceFree (2 active projects, 500 MB); Pro $25/monthFree (100 projects); Launch $0.106 per CU-hour, no minimumFree; Professional $25 per developer/monthFree (512 MB); Flex $0.011/hour, capped at $30/monthInstance-hours plus storage; no permanent free tier
Lock-inLow for data; higher with its API and StorageLowHigh: its own data model and functions, not SQLMedium: MongoDB's query APILowest

The first row is the decision. When the database verifies Clerk's token, a query that forgets its filter can't leak data — Postgres won't return the rows. When only your server checks the session, every query owns its org_id filter, which is fine until someone forgets one. Supabase gives you the database-enforced version with the least setup; on Neon and RDS you get the same guarantee by setting the tenant per transaction, as shown below.

Realtime and lock-in pull in opposite directions. Convex's reactive queries are the best realtime developer experience here, but your data lives in its document model, not portable SQL. Supabase's Realtime sits on standard Postgres you can dump and move.

How does Clerk connect to your database?

Through IDs and tokens: your rows store Clerk's user and organisation IDs, and every request carries a short-lived session token saying who the user is and which organisation is active. Three mechanisms do the work.

IDs on your rows. Clerk IDs are prefixed strings — user_… and org_… — so store them in text columns, not uuid. A created_by or org_id column holding the Clerk ID is all most tables need.

The session token. Clerk's version 2 session token carries the user ID in sub and the active organisation in a compact o object: o.id, o.rol (role), o.slg (slug) and permissions. Version 1's separate org_id, org_role and org_slug claims were deprecated on 14 April 2025, so treat older tutorials that read org_id with suspicion. Keep custom claims small: Clerk recommends under 1.2 KB, because the token travels in a cookie.

Webhooks. For changes outside a request — a user edits their email, an admin removes a member — Clerk sends webhooks, delivered through Svix with automatic retries. Most apps handle user.created, user.updated and user.deleted, plus the matching organization.* and organizationMembership.* created, updated and deleted events. Verify each delivery with verifyWebhook() from @clerk/nextjs/webhooks.

Clerk's docs are candid that delivery isn't guaranteed, events can arrive out of order or twice, and synced data is eventually consistent. So write handlers as idempotent upserts keyed on the Clerk ID, and never make a synchronous flow such as onboarding wait for a webhook.

Do you need a users table if you use Clerk?

Often not at first: Clerk's docs recommend reading identity from the session token, which is always current, rather than maintaining a copy. If a request only needs to know who the current user is and which organisation they're acting for, the token already says so — no lookup, no sync, nothing to drift.

You need data about other users the moment you build a member list, an "assigned to" column, a mention picker or a report joining activity to people. There are two ways to get it:

  • Call Clerk's Backend API. Fine for an admin screen or a short team list. Production instances allow 1,000 requests per 10 seconds and development instances 100, per Clerk's rate limits, so don't call it once per row of a table someone pages through.
  • Keep a thin, webhook-synced copy. A users table keyed on the Clerk ID, with email, name and avatar, plus a memberships table if you filter people by organisation. Now it's a SQL join — but it's a cache of Clerk, not the source of truth.

Our rule: start token-only, add the synced table when a feature first needs to join on people, and also create each row lazily on the user's first authenticated request, so a late user.created webhook never leaves anyone without one. Owning user records outright is a different auth choice, weighed in Clerk vs Better Auth.

How do you build multi-tenancy with Clerk Organizations?

Put a text org_id column on every tenant-owned table, fill it from the active organisation in Clerk's session token, and let Row-Level Security reject any row whose org_id doesn't match. Clerk Organizations supplies the rest: the organisation switcher, invitations and roles. It's the shared-schema model from our multi-tenant SaaS architecture guide, with Clerk's organisation ID as the tenant ID.

On Supabase, policies read Clerk's token directly through auth.jwt():

alter table projects enable row level security;

create policy "Members read their org's projects"
on projects for select to authenticated
using (org_id = (select auth.jwt()->'o'->>'id'));

create policy "Admins create projects in their org"
on projects for insert to authenticated
with check (
  org_id = (select auth.jwt()->'o'->>'id')
  and (select auth.jwt()->'o'->>'rol') = 'admin'
);

Three details catch people out. Version 2 roles drop the org: prefix, so the check is 'admin', not 'org:admin'. If the user has no active organisation the o claim is absent, the comparison yields null and the policy returns nothing — the safe failure. And on the client, the Supabase SDK sends Clerk's token when you pass a function returning session.getToken() as its accessToken option; ignore any guide that has you copy your Supabase JWT secret into a Clerk JWT template, an integration Supabase deprecated on 1 April 2025.

On Neon or RDS through an ORM, read orgId from Clerk's auth() helper on the server and filter by it. For the same database-enforced guarantee, set it per transaction with set_config('app.tenant_id', orgId, true) and write policies against that setting. Neon's Data API can also verify Clerk tokens itself via Clerk's JWKS URL, exposing claims to policies through auth.user_id() and auth.session().

Budget for Clerk's side too: 100 monthly retained organisations are free per app; beyond that you need the $100/month Enhanced B2B add-on, then $1 per organisation a month.

Can Clerk integrate with an existing user database?

Yes, by importing your users into Clerk rather than connecting it to your table: Clerk's Backend API accepts existing password hashes, so people keep their passwords, and an external_id field preserves the link to your old user IDs. Clerk becomes the system of record for identity; your application tables stay where they are.

  • Password hashes carry over. Clerk's create-user endpoint takes a password digest and the algorithm that produced it — bcrypt, argon2i and argon2id, several PBKDF2 variants, Firebase's scrypt, Django and phpass formats among them. Hashes from weak schemes such as MD5 and SHA-256 are upgraded to bcrypt transparently at each user's next successful sign-in.
  • Old IDs keep working. Store each user's existing ID as external_id, then customise the session token so a claim returns {{user.external_id || user.id}}. Migrated users carry their old ID, new users their Clerk ID, and your foreign keys don't change.
  • Bulk or trickle. Clerk's open-source migration script imports a JSON or CSV export through the Backend API within its rate limits. A trickle migration runs both systems side by side and moves users as they sign in, then bulk-imports anyone who never came back.
  • Organisations are a separate job. Recreating organisations, memberships and roles is your own script against the Backend API.

Everyone signs in once more after cutover, with their old password. Budget a week or two, cutover included.

Our default: Next.js, Clerk and Postgres on Supabase

For the SaaS MVPs we build through our web development work, the stack is Next.js, Clerk, Postgres on Supabase, Stripe and Resend on Vercel, with Clerk connected as a Supabase third-party auth provider. Every tenant-owned table carries the Clerk organisation ID, and RLS policies read it straight from the session token, so isolation is the database's job rather than every query's. Jobs that genuinely cross tenants — billing, admin tools, migrations — use a privileged connection, kept to a small, reviewed corner of the codebase.

On cost, Supabase Pro is $25 a month, and Clerk users count as third-party MAUs with 100,000 included; the full bill is in Supabase pricing explained.

We deviate in two situations:

Neon, when the app only reaches its database from server-side code. A Next.js app on Clerk that queries through Drizzle or Prisma in server components and actions doesn't need Supabase's API, Storage or Realtime. Neon's scale-to-zero billing and per-PR branches fit that shape better, with tenancy enforced by per-transaction settings and RLS. The full comparison, including RDS for clients already on AWS, is in Neon vs Supabase vs AWS RDS.

Convex, when realtime is the product. Collaborative editors, live dashboards, anything multiplayer. Convex's Clerk integration is first-party, and its reactive queries remove a layer of WebSocket plumbing; Professional is $25 per developer a month, per Convex's pricing. The trade is a proprietary data model, harder reporting than SQL, and no London region: its EU deployments run in Ireland, where usage bills at 1.3x the US rate.

If enterprise SSO rather than speed is what's driving your auth decision, compare Clerk, WorkOS and Stytch before wiring anything to a database.

Frequently asked questions

Does Clerk work with MongoDB?

Yes, but only at the application level — Clerk has no MongoDB integration, so your server verifies the session and adds the user or organisation filter to every query. That works, but there's no database-level backstop like RLS: one forgotten orgId filter returns another customer's documents. Atlas's free tier is 512 MB, and Flex is capped at $30 a month. If you're weighing Atlas against a Postgres backend, see MongoDB Atlas vs Supabase.

Should you use Clerk's user ID as your primary key?

For a new build, yes — store Clerk's user and organisation IDs directly as text keys rather than minting your own and mapping between them. RLS policies can then compare columns straight to sub and o.id with no lookup. The exception is an existing product: keep your IDs, store them as Clerk's external_id and expose them in the token, as above.

Do Clerk users count towards Supabase's monthly active users?

Yes — users signing in through Clerk are billed as third-party MAUs, with 50,000 included on Free and 100,000 on Pro, then $0.00325 each. That's on top of Clerk's own bill, which counts monthly retained users: 50,000 included per app on both Hobby and Pro. At MVP scale neither number is close; at consumer scale, model both before you commit.

What database should a new SaaS startup choose?

Managed Postgres, on Supabase or Neon, unless your data is genuinely document-shaped. SaaS data is relational — users, organisations, subscriptions, invoices — and Postgres handles the flexible parts with jsonb. Both hosts offer a free tier for validation and a London region, and moving between them is a pg_dump away. The longer argument is in Postgres vs MongoDB for a SaaS.

Want Clerk and your database wired up properly from day one?

Auth and data get connected in week one — token claims, webhooks, tenant columns and policies — and every later feature inherits those choices. To make them deliberately, book a free scoping call. We'll map the right stack to your product and quote the build fixed-price, so you know the cost before committing.

Sameer AhmadCo-Founder & CTO, Coderacle

Sameer is the co-founder and CTO of Coderacle, a London software studio building SaaS MVPs for UK founders. He leads engineering and architecture on every build — stack decisions, scalable foundations, and getting products to production without the usual rewrites.

Leave a comment