PocketBase vs Supabase: Self-Hosted Backend Showdown
Quick Verdict
PocketBase wins for solo developers and small projects. One 15 MB binary gives you auth, real-time, file storage, and a dashboard using zero external dependencies. It runs on a Raspberry Pi. Deploy it in under a minute.
Supabase wins when you need PostgreSQL. Row-level security, edge functions, GraphQL, six official client SDKs, and the full power of Postgres extensions. The cost: 13 Docker containers, 4 GB minimum RAM, and real operational complexity.
If your project fits inside SQLite’s limits — and the vast majority do — pick PocketBase and stop overthinking it. If you need SQL, read replicas, or pgvector, Supabase is the only self-hosted Firebase alternative worth running.
The Core Tradeoff
PocketBase and Supabase both replace Firebase. Both give you auth, a REST API, real-time subscriptions, file storage, and an admin dashboard. But they approach the problem from opposite ends.
PocketBase is a monolith — a single Go binary with an embedded SQLite database. Everything lives in one process, one file, one server. There is nothing to configure, nothing to network, nothing to scale independently.
Supabase is a distributed system — 13 services stitched together behind an API gateway. PostgreSQL handles the data, GoTrue handles auth, PostgREST generates the API, a Deno runtime executes edge functions, and Kong routes it all. Each piece is best-in-class open source software. Together, they replicate Firebase’s feature set on top of Postgres.
The architecture diagram tells the story:
PocketBase: One binary. One process. One data file.
Supabase: Kong gateway routing to PostgREST, GoTrue, Realtime, Storage, Edge Runtime, imgproxy, and postgres-meta — all connecting to PostgreSQL, with Supavisor pooling connections, Vector collecting logs, and Logflare analyzing them. Plus Studio for the dashboard.
That is the tradeoff in a sentence: PocketBase trades capability for simplicity. Supabase trades simplicity for capability.
Feature Comparison
| Feature | PocketBase | Supabase (Self-Hosted) |
|---|---|---|
| Database engine | SQLite (embedded) | PostgreSQL 15 |
| Authentication | Built-in email/password, OAuth2 | GoTrue: email, phone, OTP, magic links, OAuth, SAML, MFA |
| Real-time | SSE subscriptions on collections | WebSockets via PostgreSQL LISTEN/NOTIFY |
| File storage | Local filesystem or S3-compatible | S3-compatible with Postgres-managed permissions |
| REST API | Auto-generated from collection schema | PostgREST (auto-generated from DB schema) |
| GraphQL | Not available | Yes, via pg_graphql extension |
| Edge/server functions | Go hooks (requires recompilation) or JS hooks (Goja interpreter) | Deno-based edge runtime (TypeScript/JavaScript) |
| Official SDKs | JavaScript, Dart | JavaScript, Dart, Python, Swift, Kotlin, C# |
| Admin dashboard | Embedded in the binary | Supabase Studio (separate React app) |
| Access control | Collection-level API rules (JS expressions) | PostgreSQL row-level security (full SQL) |
| Database extensions | None (SQLite only) | pgvector, PostGIS, pg_cron, and 50+ Postgres extensions |
| Connection pooling | Not needed (in-process SQLite) | Supavisor (built into the stack) |
| Image processing | Not included | imgproxy (built into the stack) |
| Log analytics | Basic file logging | Logflare + Vector (built into the stack) |
| License | MIT | Apache 2.0 |
Running PocketBase with Docker
PocketBase has no official Docker image. The project provides a minimal Dockerfile in their production docs that downloads the binary into Alpine Linux. Here is a complete Docker Compose setup.
Create a Dockerfile:
FROM alpine:3.21
ARG PB_VERSION=0.36.5
RUN apk add --no-cache ca-certificates wget unzip \
&& wget -q "https://github.com/pocketbase/pocketbase/releases/download/v${PB_VERSION}/pocketbase_${PB_VERSION}_linux_amd64.zip" \
-O /tmp/pb.zip \
&& unzip /tmp/pb.zip -d /usr/local/bin/ \
&& rm /tmp/pb.zip \
&& chmod +x /usr/local/bin/pocketbase
EXPOSE 8090
CMD ["pocketbase", "serve", "--http=0.0.0.0:8090", "--dir=/pb_data"]
Create a docker-compose.yml alongside it:
services:
pocketbase:
build:
context: .
args:
PB_VERSION: "0.36.5" # Pin to a specific release
restart: unless-stopped
ports:
- "8090:8090" # Admin UI + REST API
volumes:
- pb_data:/pb_data # SQLite database, uploaded files, logs
environment:
- GOMEMLIMIT=256MiB # Optional: cap Go runtime memory on constrained hosts
healthcheck:
test: ["CMD", "wget", "--no-verbose", "--tries=1", "--spider", "http://localhost:8090/api/health"]
interval: 30s
timeout: 5s
retries: 3
volumes:
pb_data:
Start it:
docker compose up -d
Open http://your-server:8090/_/ and create your superuser account. That is the entire setup. No .env file, no JWT secrets, no database credentials.
Without Docker, it is even simpler: download the binary from the releases page, run ./pocketbase serve, and visit the admin UI.
Running Supabase with Docker
Supabase self-hosted is a 13-service stack. Clone the official repo and use their Docker Compose configuration.
# Clone only what you need
git clone --depth 1 https://github.com/supabase/supabase
cd supabase/docker
cp .env.example .env
Edit the .env file. These values must be changed before production use — the defaults are publicly known:
# ----- CHANGE THESE -----
# Database password (used by all services connecting to Postgres)
POSTGRES_PASSWORD=replace-with-a-strong-random-password
# JWT signing secret — minimum 32 characters
JWT_SECRET=replace-with-at-least-32-character-secret
# Generate ANON_KEY and SERVICE_ROLE_KEY as JWTs signed with JWT_SECRET
# Use: https://supabase.com/docs/guides/self-hosting#api-keys
ANON_KEY=eyJ...your-generated-anon-jwt
SERVICE_ROLE_KEY=eyJ...your-generated-service-role-jwt
# Studio dashboard credentials
DASHBOARD_USERNAME=admin
DASHBOARD_PASSWORD=replace-with-a-strong-password
# Secrets for encryption at rest
SECRET_KEY_BASE=replace-with-64-char-hex-string
VAULT_ENC_KEY=replace-with-64-char-hex-string
# ----- SET YOUR DOMAIN -----
SITE_URL=http://localhost:3000
API_EXTERNAL_URL=http://localhost:8000
SUPABASE_PUBLIC_URL=http://localhost:8000
The ANON_KEY and SERVICE_ROLE_KEY are not random strings — they must be valid JWTs signed with your JWT_SECRET. The Supabase docs provide a generator tool, or you can use the Supabase CLI.
Start the stack:
docker compose up -d
This pulls and starts 13 containers:
| Service | Image | Role |
|---|---|---|
| db | supabase/postgres:15.8.1.085 | PostgreSQL database |
| studio | supabase/studio:2026.02.16-sha-26c615c | Admin dashboard |
| kong | kong:2.8.1 | API gateway (routes all requests) |
| auth | supabase/gotrue:v2.186.0 | Authentication and user management |
| rest | postgrest/postgrest:v14.5 | Auto-generated REST API from schema |
| realtime | supabase/realtime:v2.76.5 | WebSocket subscriptions |
| storage | supabase/storage-api:v1.37.8 | File storage with Postgres permissions |
| imgproxy | darthsim/imgproxy:v3.30.1 | On-the-fly image transformations |
| meta | supabase/postgres-meta:v0.95.2 | Database management REST API |
| functions | supabase/edge-runtime:v1.70.3 | Deno-based server functions |
| analytics | supabase/logflare:1.31.2 | Log aggregation and analytics |
| vector | timberio/vector:0.53.0-alpine | Log collection pipeline |
| supavisor | supabase/supavisor:2.7.4 | Postgres connection pooling |
Exposed ports:
- 8000 — Kong API gateway (this is your main entry point for Studio and API calls)
- 5432 — PostgreSQL direct access via Supavisor (session mode)
- 6543 — PostgreSQL pooled access via Supavisor (transaction mode)
Expect the initial image pull to download several gigabytes. The stack takes 30-60 seconds to stabilize as services run health checks against each other. Access Supabase Studio at http://your-server:8000 and log in with your DASHBOARD_USERNAME and DASHBOARD_PASSWORD.
Resource Usage
This is where the architectural difference hits your wallet.
| Metric | PocketBase | Supabase (Self-Hosted) |
|---|---|---|
| RAM (idle) | 15-30 MB | 2-4 GB |
| RAM (under moderate load) | 50-150 MB | 4-8 GB |
| Disk (application only) | ~15 MB (the binary) | ~5 GB (Docker images) |
| Disk (data) | SQLite file grows as needed | PostgreSQL + WAL + logs |
| CPU (idle) | Negligible | Low-moderate across 13 containers |
| Cold start time | Under 1 second | 30-60 seconds |
| Minimum viable server | 512 MB RAM, 1 vCPU ($4-5/month) | 4 GB RAM, 2 vCPU ($8-15/month) |
| Recommended server | 1 GB RAM, 1 vCPU | 8 GB RAM, 4 vCPU |
PocketBase runs on a Raspberry Pi 4 without breaking a sweat. Supabase self-hosted needs a proper VPS with at least 4 GB of RAM, and 8 GB is recommended for anything beyond light development use.
SQLite’s in-process reads avoid network round-trips entirely, making PocketBase surprisingly fast for read-heavy workloads. PostgreSQL handles concurrent writes better — SQLite serializes all writes through a single writer lock.
Scaling Ceilings
PocketBase
SQLite is single-writer. PocketBase serializes all write operations. For most applications — internal tools, MVPs, mobile app backends with modest concurrency — this never matters. SQLite handles thousands of reads per second and hundreds of writes per second on reasonable hardware.
You hit the ceiling when:
- Concurrent writes exceed roughly 100-500 per second
- You need read replicas or geographic distribution
- The database grows past ~1 TB (feasible, but backup and migration get painful)
- You need horizontal scaling of any kind
PocketBase does not support clustering. One binary, one database, one server. Vertical scaling only.
Supabase
PostgreSQL scales horizontally. The self-hosted stack inherits all of Postgres’s scaling options:
- Supavisor connection pooling (included in the stack)
- Read replicas for read-heavy workloads
- Table partitioning for large datasets
- Extensions for specialized workloads — pgvector for AI embeddings, PostGIS for geospatial queries, TimescaleDB for time-series data
The operational cost scales with the architecture. Scaling Supabase self-hosted means tuning PostgreSQL, sizing connection pools, monitoring 13 services, and handling failover across a distributed system. This is real infrastructure work.
Developer Experience
PocketBase optimizes for getting started fast. Define collections in the admin UI, subscribe to changes via SSE, and authenticate users — all from a single endpoint on a single port. Extending PocketBase means writing Go hooks (which requires recompiling the binary) or using the embedded JavaScript engine via Goja (limited but avoids recompilation). The JavaScript and Dart SDKs are well-maintained; other languages depend on community effort.
Supabase optimizes for power. Write raw SQL migrations, compose row-level security policies referencing the authenticated user, deploy TypeScript edge functions, and use any of six official client SDKs. The Studio dashboard includes a table editor, SQL editor, and auth management. PostgreSQL extensions unlock entire capability domains — vector similarity search, geospatial queries, cron-scheduled database jobs — without leaving the platform.
For a solo developer prototyping a mobile app: PocketBase’s admin UI and Dart SDK get you from zero to working backend in minutes. For a team building a SaaS product with complex authorization rules and TypeScript server logic: Supabase’s SQL-native RLS and edge functions are worth the operational overhead.
Use Cases
Pick PocketBase When
- You are a solo developer shipping an MVP or side project
- Your app has under 1,000 concurrent users
- You want deploy-and-forget simplicity on cheap hardware
- You are building a mobile app backend and the Dart SDK fits your stack
- You need a backend for a static site, internal tool, or prototype
- A single SQLite file as your entire backup target sounds appealing
- You run your homelab on a Raspberry Pi or $5 VPS
Pick Supabase When
- You need PostgreSQL — full SQL, extensions, fine-grained RLS
- Your project requires edge functions for server-side logic
- You need SDKs for Python, Swift, Kotlin, or C# (PocketBase only covers JS and Dart officially)
- Multiple developers need to collaborate on the same backend
- You expect to scale past a single server
- You want GraphQL alongside REST
- You are already proficient with PostgreSQL and want to leverage that investment
Final Verdict
PocketBase and Supabase are not really competitors. They are answers to different questions.
PocketBase is the best self-hosted backend for individual developers and small projects. Nothing else in the ecosystem matches its simplicity-to-capability ratio. Auth, real-time, storage, REST API, and a dashboard — all in 15 MB of disk and 30 MB of RAM. If your project fits inside SQLite’s write concurrency limits, PocketBase is the correct default choice. Start here.
Supabase is the best self-hosted backend for teams building production applications. You pay for it: 13 containers, gigabytes of Docker images, JWT key generation, connection pooling configuration, and genuine DevOps work to keep it running. But you get a complete PostgreSQL-native platform with row-level security, edge functions, six client SDKs, and a scaling path that goes as far as Postgres itself. If you need what Postgres offers, Supabase self-hosted is the most complete way to get it without vendor lock-in.
If you are unsure: start with PocketBase. You will have a working backend in under two minutes. If you outgrow it, migrating your data model to PostgreSQL and Supabase is straightforward — the API layer changes, but the schema transfers cleanly.
Related
Get self-hosting tips in your inbox
New guides, comparisons, and setup tutorials — delivered weekly. No spam.