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/ is created in Step 4 by postkit db import, not here.


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 policies/ 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 explicitly restores the broad default grant (GRANT ALL ON SCHEMA public TO public) that PostgreSQL 15+ removed from standard installations. After import, this appears in your grants/ file — you can tighten or customize it, but be aware this is Supabase-specific behaviour, not a standard PostgreSQL default.


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.