Skip to main content

Migrating from Supabase to PostKit: A Practical Guide

· 5 min read
PostKit Team
Appri Technologies

Supabase is a great way to get a PostgreSQL database running in minutes. But as your project grows, you often outgrow the auto-generated migrations and want more control over exactly what SQL runs against your database. PostKit gives you that control — full schema-as-code, session-based migrations, and a deploy pipeline you can reason about.

This guide walks you through bringing a Supabase project into PostKit's migration workflow without disrupting your existing data.

Migrating from Supabase to PostKit

What Changes (and What Doesn't)

SupabasePostKit
DatabasePostgreSQLPostgreSQL (same!)
Schema managementSupabase Studio / SQL editorSchema files in db/schema/
Migration filesAuto-generated by Studiopgschema-diffed, hand-reviewable
DeploymentStudio apply / CLIpostkit db deploy with dry-run
Local devSupabase local stackDocker container (auto, version-matched)

PostKit does not replace Supabase's auth, storage, or realtime features. If you use PostgREST/Supabase's auto-API, PostKit manages the underlying schema while your application code stays the same.


Step 1: Install PostKit

npm install -g @appritech/postkit

Verify:

postkit --version

Step 2: Initialize PostKit in Your Project

In your project root:

postkit init

This creates:

  • postkit.config.json — committed, non-sensitive settings
  • postkit.secrets.json — gitignored, your credentials
  • postkit.secrets.example.json — template for teammates
  • db/schema/ — where your schema files will live

Step 3: Add Your Supabase Database as a Remote

Your Supabase project has a direct PostgreSQL connection URL. Find it in the Supabase dashboard under Settings → Database → Connection string → URI.

postkit db remote add supabase "postgres://postgres:[password]@db.[project-ref].supabase.co:5432/postgres" --default

Step 4: Import the Existing Schema

PostKit's db import command connects to your Supabase database, dumps the schema, and organizes it into PostKit's directory structure:

postkit db import --url "postgres://postgres:[password]@db.[project-ref].supabase.co:5432/postgres"

For projects with multiple schemas (e.g. public and a custom app schema):

postkit db import --url "postgres://..." --schemas "public,app"

This automatically:

  • Dumps all tables, views, functions, triggers, indexes, and constraints
  • Organizes files into db/schema/public/tables/, views/, functions/, etc.
  • Extracts roles and schemas into db/infra/
  • Creates a baseline migration in .postkit/db/migrations/
  • Updates postkit.config.json with the imported schema names

Step 5: Review the Generated Schema Files

After import, your directory looks like:

db/
├── infra/
│ ├── roles.sql # Any custom roles from Supabase
│ └── schemas.sql # Schema definitions
└── schema/
└── public/
├── tables/
│ ├── 001_users.sql
│ ├── 002_posts.sql
│ └── ...
├── views/
├── functions/
└── grants/

Take a few minutes to review. Supabase often creates helper functions and policies automatically — you'll want to understand what's there before making changes.

Supabase-specific objects to watch for

  • auth.* schema — Supabase's internal auth tables. Do not touch these — they're managed by Supabase, not your migrations.
  • storage.* schema — same, managed by Supabase.
  • Row-level security (RLS) policies — pgschema captures these in grants/ files.
  • supabase_admin role — Supabase-internal role. Ignore or exclude.

If you only want to manage your own schemas (e.g. public and app) and leave auth and storage alone:

// postkit.config.json
{
"db": {
"schemas": ["public", "app"]
}
}

Step 6: Make Your First Change with PostKit

Now the normal workflow applies. Start a session:

postkit db start

Edit a schema file — for example, add a column:

-- db/schema/public/tables/001_users.sql
CREATE TABLE public.users (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
email TEXT NOT NULL UNIQUE,
display_name TEXT, -- new column
created_at TIMESTAMPTZ DEFAULT NOW()
);

Preview the diff:

postkit db plan

PostKit shows you exactly what SQL will be generated — no surprises.

Apply locally to test:

postkit db apply

Commit and deploy:

postkit db commit --message "add display_name to users"
postkit db deploy

PostKit runs a dry-run first (on a local clone of your Supabase DB), confirms the migration works, then deploys to the real database.


Common Supabase Migration Gotchas

RLS policies after import

Supabase enables RLS on many tables. pgschema captures existing policies, but new policies you add to schema files are planned per-schema in isolation. Policies that reference roles from auth.* (like auth.uid()) are fine — pgschema recognizes these as external references.

Extensions

Supabase pre-installs extensions like uuid-ossp, pgcrypto, and pg_stat_statements. These were captured by import in db/infra/. The CREATE EXTENSION IF NOT EXISTS form means re-running them is safe.

The public schema default

Supabase's public schema has a broad default grant (GRANT ALL ON SCHEMA public TO public). After import, this appears in your grants/ file. You can tighten or customize it.


What to Do With Existing Supabase Migrations

If you have existing Supabase migration files (.sql files in supabase/migrations/), you don't need to port them. PostKit's db import creates a single baseline from the current database state — all prior history is collapsed into that baseline. Future changes go through PostKit's workflow.


Summary

StepCommand
Install PostKitnpm install -g @appritech/postkit
Init projectpostkit init
Add Supabase remotepostkit db remote add supabase "postgres://..."
Import existing schemapostkit db import --url "postgres://..."
Start developingpostkit db start
Preview changespostkit db plan
Apply locallypostkit db apply
Deploy to Supabasepostkit db deploy

Your Supabase database is now under PostKit's session-based migration workflow. Schema changes are reviewed, tested locally, and deployed with a dry-run safety check before they ever touch production.