Schema Migrations with Prisma + Atlas: Safe Postgres Changes for AI Apps (2026)
By Sagar Shankaran, Founder of CallSphere
Prisma owns the schema model, Atlas owns the migration plan, lint, and CI/CD. Together you get declarative schema, automatic diff plans, and zero-downtime production deploys for AI-heavy Postgres.
Key takeaways
TL;DR — Prisma's built-in migrations work for solo devs; Atlas handles the production-grade pipeline (linting, CI checks, RLS, triggers, declarative drift detection). Run Prisma for the schema, Atlas for the plan and apply.
What you'll build
A repo where schema.prisma is the single source of truth, Atlas auto-generates SQL migrations, lint rules block dangerous changes (DROP COLUMN without IF EXISTS, missing CONCURRENTLY, etc.), and GitHub Actions applies them safely.
Schema
// schema.prisma
datasource db {
provider = "postgresql"
url = env("DATABASE_URL")
}
model Conversation {
id BigInt @id @default(autoincrement())
tenantId String @map("tenant_id") @db.Uuid
agentId String @map("agent_id") @db.Uuid
body String
embedding Unsupported("vector(1536)")?
createdAt DateTime @default(now()) @map("created_at")
@@map("conversations")
@@index([tenantId, createdAt(sort: Desc)])
}
Architecture
flowchart LR
DEV[Edit schema.prisma] --> DIFF[atlas migrate diff]
DIFF --> SQL[Generated SQL]
SQL --> LINT[atlas migrate lint]
LINT --> CI[GitHub Actions]
CI --> STAGE[Staging apply]
STAGE --> PROD[Production apply<br/>CONCURRENTLY-safe]
Step 1 — Wire Atlas to Prisma
# atlas.hcl
data "external_schema" "prisma" {
program = ["npx", "prisma", "migrate", "diff", "--from-empty", "--to-schema-datamodel", "prisma/schema.prisma", "--script"]
}
env "local" {
src = data.external_schema.prisma.url
url = "postgres://postgres:postgres@localhost:5432/dev"
dev = "docker://postgres/17/dev"
migration { dir = "file://migrations" }
}
Step 2 — Generate the next migration
atlas migrate diff add_embedding_index --env local
Output:
-- migrations/20260507142100_add_embedding_index.sql
CREATE INDEX CONCURRENTLY IF NOT EXISTS conversations_embedding_hnsw
ON conversations USING hnsw (embedding vector_cosine_ops)
WITH (m = 16, ef_construction = 64);
Step 3 — Lint dangerous changes
atlas migrate lint --env local --latest 1
Atlas catches:
Hear it before you finish reading
Talk to a live CallSphere AI voice agent for IT support in your browser — 60 seconds, no signup.
- DROP COLUMN on non-empty tables without backfill
- ALTER TYPE that locks the table
- Missing CONCURRENTLY on big-table indexes
- Backwards-incompatible RENAME
Step 4 — Apply in CI
# .github/workflows/migrate.yml
- uses: ariga/atlas-action/migrate/lint@v1
with:
dir: 'file://migrations'
dev-url: 'docker://postgres/17/dev'
- uses: ariga/atlas-action/migrate/apply@v1
with:
url: ${{ secrets.DATABASE_URL }}
dir: 'file://migrations'
Step 5 — Manage RLS / triggers declaratively
Atlas understands RLS, triggers, functions — Prisma doesn't. Add a schema.hcl for those:
policy "tenant_isolation" {
on = table.conversations
as = "PERMISSIVE"
for = "ALL"
using = "tenant_id = current_setting('app.tenant_id', true)::uuid"
with_check = "tenant_id = current_setting('app.tenant_id', true)::uuid"
}
Step 6 — Zero-downtime patterns
For renames or splits, use the expand/contract pattern:
- Expand — add new column, dual-write app code.
- Backfill — async job copies data.
- Contract — drop old column once new column is canonical.
Each step is a separate Atlas migration.
Pitfalls
- Manual edits to migration SQL — break Atlas's understanding of schema state. Prefer regenerating.
- Skipping lint — production crashes from one missing CONCURRENTLY are Friday-night classics.
- Drift between Prisma and DB —
atlas schema inspectcatches it before deploy. - Long-running migrations in transactions — Postgres holds locks. Split into separate concurrent steps.
CallSphere production note
Explore a live demo and compare current plans to find the right fit for your business.
Still reading? Stop comparing — try CallSphere live.
See the IT support AI agent handle a real call — complete, industry-specific, and live in your browser. No signup.
FAQ
Q: Atlas or just Prisma migrate? Atlas if you need lint, RLS/triggers, or CI/CD guards. Prisma migrate is fine for personal projects.
Q: Drizzle migrations? Drizzle has its own kit, similar shape to Atlas; ecosystem narrower for advanced lint.
Q: Can Atlas manage extensions?
Yes — CREATE EXTENSION in schema.hcl is supported.
Q: Rollback strategy? Atlas supports down migrations; in practice prefer forward-only with expand/contract.
Q: How fast are large migrations?
CREATE INDEX CONCURRENTLY on 100M rows: 30-90 min. Plan during low-write windows.
Sources

Written by
Sagar Shankaran· Founder, CallSphere
LinkedInSagar Shankaran is the founder of CallSphere, where he builds production AI voice and chat agents deployed across healthcare, hospitality, real estate, and home services. He writes about agentic AI, LLM engineering, and shipping voice agents that handle real calls in production.
Try CallSphere AI Voice Agents
See how AI voice agents work for your industry. Live demo available -- no signup required.