← Back to blog

Migrating From Supabase to Plain Postgres and a Backend You Own

Iman RadjaviIman Radjavi·

You built on Supabase because it gave you a database, an API, auth, storage, and realtime in one afternoon. The reasons to leave tend to be the same three. The client SDK ends up in every file that touches data, so the lock-in is spread across the codebase rather than confined to one layer. Authorization lives in row level security policies, which get hard to test and reason about as the app grows. And the first time you need something long-running, a worker, a queue consumer, a job that outlives an edge function, Supabase has nowhere to run it, so you add a second platform.

This post is about moving to Specific. It gives you the same Postgres, plus object storage, real-time sync, and durable workflows, but your app talks to a backend service you write, in any language, instead of a generated API. Your coding agent declares all of it in one config file, runs it locally, and deploys it, so the migration itself is the agent's job. This post is for deciding whether to do it: what carries over, what has to be written, what happens to your users, and what the finished project looks like. The feature-by-feature mapping is in the Supabase migration guide in our docs, and the side-by-side comparison is on the Supabase comparison page.

If you have already decided, give your agent this prompt in the project:

Help me migrate my project from Supabase to Specific. Start with https://docs.specific.dev/for-ai/onboarding, then run `specific docs /migrations/supabase` and follow it.

Whether to migrate

The migration is worth it when at least one of the three reasons above is biting: the SDK is everywhere, RLS is where your authorization bugs live, or you already run something on a second platform because Supabase can't. It is also worth it if you want the frontend deployed next to the backend rather than on a separate host.

It is not worth it if you are happy on supabase-js with bundled auth and have no backend needs beyond the generated API. In that case the migration is mostly writing an API you didn't have to write before.

What carries over and what gets written

Part What happens
Postgres schema, functions, triggers, extensions (pgvector included) Carry over as SQL. Schema changes then run through Reshape, or your ORM's migrations.
Database rows and storage objects Exported and imported manually with standard Postgres and S3 tooling.
Auto-generated REST API (PostgREST) Written as a backend service in the language you pick. This is the largest piece of new code.
Row level security policies Become authorization checks in that backend, next to the endpoints they protect.
Edge functions Move into the same service.
Authentication Keep Supabase Auth on the Supabase project, or replace it with Better Auth or a provider such as Auth0, Clerk, or WorkOS.
Storage An S3-compatible bucket; any S3 client works, locally too.
Realtime Postgres sync, powered by ElectricSQL, or polling where that is enough.
Queues Temporal workflows, run by a worker service.
pg_cron The cron block for scheduled commands, or Temporal Schedules for multi-step work.
Frontend @supabase/supabase-js removed, fetch calls to your API with a bearer token instead.

What happens to your users

If you keep Supabase Auth for sign-in, nothing: users and passwords stay put, the frontend signs in as before, and your backend verifies the JWT Supabase issues on each request. If you replace it, the users and their password hashes can be exported from Supabase's auth schema, but whether the new system can verify that hash format depends on it, so plan a password reset flow as the fallback.

What it takes

The agent reads the migration guide that ships inside the CLI, writes the backend service, moves the authorization rules into it, edits specific.hcl, validates with specific check, and iterates against specific dev until the app works end to end. The size of the job is the size of the API: an app that leaned heavily on the generated endpoints has more to write than one that already had a backend.

What the finished project looks like

One specific.hcl for a Node API, a web frontend, the database with sync enabled, and a bucket:

postgres "main" {
  reshape {
    enabled = true
  }
}

storage "uploads" {}

secret "auth_secret" {
  generated = true
}

build "api" {
  base    = "node"
  root    = "api"
  command = "npm run build"
}

build "web" {
  base    = "node"
  root    = "web"
  command = "npm run build"
}

service "api" {
  build   = build.api
  command = "node dist/index.js"

  endpoint {
    public = true
  }

  env = {
    PORT                 = port
    DATABASE_URL         = postgres.main.url
    DATABASE_SYNC_URL    = postgres.main.sync.url
    DATABASE_SYNC_SECRET = postgres.main.sync.secret
    S3_ENDPOINT          = storage.uploads.endpoint
    S3_ACCESS_KEY        = storage.uploads.access_key
    S3_SECRET_KEY        = storage.uploads.secret_key
    S3_BUCKET            = storage.uploads.bucket
    AUTH_SECRET          = secret.auth_secret
  }

  dev {
    command = "npm run dev"
  }
}

service "web" {
  build   = build.web
  command = "npm start"

  endpoint {
    public = true
  }

  env = {
    PORT    = port
    API_URL = service.api.url
  }

  dev {
    command = "npm run dev"
  }
}

Referencing postgres.main.sync.url is what turns sync on; there is no separate block for it. Locally, specific dev starts Postgres, the sync engine, an S3-compatible server, and both services, with no Docker. In production, specific deploy provisions the same things and injects the same environment variables.

Common questions

Is Supabase a bad choice?

No. If you are happy building on supabase-js and want bundled auth, Supabase is a fine product, and this migration is real work. The trade is coupling: after it, your code talks to Postgres, S3, and HTTP, and would keep working on any platform that offers those.

Can I migrate the data automatically?

Not yet. Moving rows and storage objects out of Supabase is a manual export and import today. Schema, functions, and triggers move as SQL.

Do I lose the Supabase dashboard?

You get a different one. specific dev serves a local dashboard with a database browser and editor, and the production dashboard has the same for deployed environments, plus logs, metrics, and storage browsing.

Try it

Give your agent the prompt above, or install the CLI yourself and run specific init:

curl -fsSL https://specific.dev/install.sh | sh

If you want help planning or running the migration, email us at [email protected]. If your Supabase project came from Lovable, start with leaving Lovable instead.