Schema Migrations with Prisma + Atlas: Safe Postgres Changes for AI Apps (2026)
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.
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 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
CallSphere ships every schema change through Prisma + Atlas across 115+ DB tables. Healthcare (Prisma healthcare_voice) gets a separate atlas env so HIPAA migrations are reviewed independently; OneRoof's RLS policies live in schema.hcl; UrackIT's Supabase migrations sync via the same pipeline. 37 agents · 90+ tools · 6 verticals, never a Friday rollback. Plans: $149/$499/$1,499 — 14-day trial, 22% affiliate.
Still reading? Stop comparing — try CallSphere live.
CallSphere ships complete AI voice agents per industry — 14 tools for healthcare, 10 agents for real estate, 4 specialists for salons. See how it actually handles a call before you book a demo.
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
Try CallSphere AI Voice Agents
See how AI voice agents work for your industry. Live demo available -- no signup required.