28 Oct 2026 · Auth & Stripe
Auth.js v5 vs Clerk on Next.js 15, Dropping the Per-MAU Identity Tax
What Clerk costs as you grow, how Auth.js v5 plus Postgres looks in Docker, and when a hosted identity vendor is still the right call.
Clerk is fast to paste into a Next.js app. The bill is per monthly active user. At hobby volume you do not notice. At a B2B SaaS with thousands of people signing in every month, you notice, and you still need a User row in your own Postgres for Stripe and credits.
AgenticPacks OSS is Auth.js v5 with email/password and optional Google: Agentic-Packs/agenticpacks-oss. Pro adds magic links through Resend and RBAC on team workspaces. Neither pack uses Clerk. The infra bill this compares against is in self-hosting architecture and cost.
The problem
Hosted auth prices change. The shape does not.
You pay for monthly actives. You pay extra for SSO. You still model User in Prisma because billing and usage live in your database. Now you have two sources of truth for who this person is. Clerk says they exist. Your User row says they have 400 credits. Those drift.
Auth.js on a VPS is included in the same 5 euro node that already runs Next.js and Postgres. Google OAuth is free at normal app scale. Resend is a small email bill. You own the session table. You own deletion when a customer asks.
What Auth.js v5 looks like in the App Router
import NextAuth from "next-auth";
import Google from "next-auth/providers/google";
import { PrismaAdapter } from "@auth/prisma-adapter";
import { prisma } from "@/lib/prisma";
export const { handlers, auth, signIn, signOut } = NextAuth({
adapter: PrismaAdapter(prisma),
providers: [Google],
session: { strategy: "database" },
});
Export handlers from a Route Handler. Server Components and Server Actions call auth(). There is no Edge-only session in this setup. Prisma wants Node.
AUTH_SECRET, AUTH_URL, and the Google client secrets are runtime environment variables. They are not NEXT_PUBLIC_*. They are not Docker build args. That split is in the Docker environment variable guide.
AUTH_URL must be the public HTTPS origin that users see, the one Caddy terminates. A wrong AUTH_URL is the usual redirect_uri_mismatch after you move off localhost.
Magic links and teams
AgenticPacks OSS covers credentials plus Google. That is enough for a personal MIT app.
Magic links need a transactional mailer and a verify page. Pro ships that with Resend.
Teams are not a Clerk organization object unless you pay for that product. They are Membership rows in your database: Owner, Admin, Member. Credits then sit on the organization, not on each login. See team credit pools and Server Actions authorization.
When Clerk is still reasonable
- You need enterprise SSO this week and nobody on the team wants to operate SAML.
- You have no Postgres yet. That is unusual for this stack.
- The app is a prototype you expect to throw away.
If you already run Postgres, Stripe, and Docker Compose, Auth.js is the default. Adding Clerk puts a third uptime dependency in front of login.
Tired of a per-MAU line item for sessions you store anyway?
Auth.js v5 is in the MIT starter: Agentic-Packs/agenticpacks-oss.
Magic links and workspace RBAC are in Auth.js magic links and RBAC in Pro.
Leaving Clerk
New session cookies. Plan a forced re-login. Map Clerk user ids onto User.id once. Do not run both login panels in production for long.
Where to Go From Here
You do not have to rent identity when Postgres is already in Compose.
Option A: Free open-source baseline (MIT)
- GitHub: github.com/Agentic-Packs/agenticpacks-oss
- What is included: Auth.js v5 (credentials + Google), Prisma adapter, Compose, streaming chat, single-plan Stripe. No Clerk.
- Cost: free, MIT.
Option B: Ship with AgenticPacks Pro ($149 one-time)
If you need magic links and teams:
- Automated Hetzner provisioning: Terraform, Caddy, Compose.
AUTH_URLon real TLS. - Production multi-agent core: LangGraph, pgvector, Redis, workspace RBAC.
- Usage & Unit Economics: Stripe, team pools, token ledger.
