Skip to main content

pgStack vs Supabase

pgStack is designed as a self-hosted Supabase alternative. This page covers the feature overlap, differences, and migration path.

Feature comparison

FeatureSupabasepgStack
Containers15+ (GoTrue, PostgREST, Realtime, Storage, Kong, ...)2 (PostgreSQL + Proxy)
Auth providerGoTrue (Go service)Built-in Go proxy
REST APIPostgRESTpgstack-proxy (Go, PostgREST-compatible)
Real-timePhoenix Elixir serverpg_reactive C extension + Go proxy
Row Level SecurityYesYes
StorageS3-backed (Supabase Storage)Built-in (PostgreSQL-backed by default; optional disk backend via STORAGE_BACKEND=disk)
Edge FunctionsDeno runtimeDeno + PL/v8 + PL/Python + embedded JS + per-function HTTP containers (5 backends)
Transaction BatchingNot availableBuilt-in (POST /rest/v1/batch)
Studio / DashboardSupabase Studio (React)pgStack Studio (React)
SDKsupabase-js@pgstack/sdk (/pgstack entry, Supabase-compatible API)
Self-hostingComplex (15+ services, Kubernetes recommended)docker compose up -d
Job queuesVia pg_cron or externalBuilt-in DBOS (PostgreSQL-backed)
MigrationsSupabase CLIpgstack CLI
Type generationsupabase gen typespgstack generate types
OAuthGoogle, GitHub, Discord, Twitter, Apple, ...Google, GitHub, Apple, Microsoft, any OIDC provider
Magic linksYesYes
Phone authYes (Twilio)Not included
PricingFree tier + paid cloudOpen source (self-host free)
PostgreSQL version15 (cloud: managed)16 (your own)

Where pgStack wins

Simplicity. Two containers. One docker-compose.yml. Zero cloud dependencies. This makes pgStack vastly easier to understand, debug, and operate — especially for teams without dedicated infrastructure engineers.

Resource footprint. Supabase's full self-hosted stack requires 2-4 GB of RAM at idle. pgStack runs comfortably in 512 MB.

Real-time efficiency. pg_reactive delivers SQL-level deltas using EXCEPT-based diff. You get typed, structured change events — not raw WAL events that require client-side re-querying.

Transaction batching. POST /rest/v1/batch executes multiple REST operations in a single PostgreSQL transaction with {{opId.field}} cross-referencing. Supabase has no equivalent — users have been requesting this since 2015.

Edge Functions flexibility. Five runtime backends (Deno, PL/v8, PL/Python, embedded JS, and per-function HTTP containers in any language) — pick the right tool for the job. Supabase only offers Deno.

Presence and broadcast included. Ad-hoc _channel_/_presence_ channels, presence join/leave/state events, peer broadcast, and GET /ws/_presence/{channel} ship in the same proxy that serves live SQL queries.

Durable job queues. The DBOS integration gives you a PostgreSQL-backed job queue with automatic retries, cron scheduling, and observability — no Redis or RabbitMQ.

Auditability. With two containers and a single database, your entire system state lives in PostgreSQL. No syncing between microservices.

Where Supabase wins

Managed hosting. Supabase Cloud handles backups, upgrades, and scaling. pgStack is self-hosted only.

Ecosystem. Supabase has more OAuth providers and a larger community.

Phone auth. Supabase supports SMS-based auth via Twilio.

SDK compatibility

The @pgstack/sdk/pgstack client is designed to be a drop-in replacement for @supabase/supabase-js for the most common patterns:

// Supabase
import { createClient } from '@supabase/supabase-js';
const supabase = createClient(url, anonKey);
const { data } = await supabase.from('todos').select('*').eq('done', false);

// pgStack — identical API
import { createClient } from '@pgstack/sdk/pgstack';
const pgstack = createClient(url, anonKey);
const { data } = await pgstack.from('todos').select('*').eq('done', false);

Auth methods match the Supabase JS client:

Supabase methodpgStack method
auth.signUp()auth.signUp()
auth.signInWithPassword()auth.signInWithPassword()
auth.signInWithOAuth()auth.signInWithOAuth()
auth.signOut()auth.signOut()
auth.getUser()auth.getUser()
auth.getSession()auth.getSession()
auth.onAuthStateChange()auth.onAuthStateChange()

Migrating from Supabase

  1. Export your schema. Use supabase db dump or pg_dump to export your tables, RLS policies, and functions.

  2. Import into pgStack. Run the dump against your pgStack PostgreSQL instance:

    docker compose exec -T db psql -U postgres -d app < schema.sql
  3. Update your client URL. Change the Supabase URL to your pgStack proxy URL in your environment variables.

  4. Update your anon/service keys. Generate new keys in your .env file.

  5. Test auth flows. The auth API paths are identical (/auth/v1/signup, /auth/v1/token, etc.).

  6. Test REST queries. Most PostgREST-compatible queries work without changes.

  7. Migrate storage. pgStack has built-in storage (PostgreSQL-backed by default; set STORAGE_BACKEND=disk + STORAGE_DIR for filesystem storage). Upload files via the storage API.