tinbase logoThe Supabase backend, without Docker · Open source (MIT)

The Supabase backend that fits in a tin

A Supabase-compatible backend in a single 57 MB binary — real Postgres with Row Level Security, Auth, Storage, and Realtime. The official supabase-js SDK works unchanged. No Docker.

npx tinbase start

Experimental — great for prototypes, local dev, and embedded use. Not meant for production yet.

A weight class you can deploy anywhere

The Supabase local stack is a 12-container, 2.3 GB Docker install. tinbase serves the same APIs from a single file at a fraction of the memory.

Memory under load (MB) · 1,000 inserts + 1,000 filtered reads · lower is better
PocketBaseSQLite
25
tinbase (binary)real Postgres + RLS
64
tinbase (wasm)real Postgres + RLS
347
Supabase localPostgres
1,626

Physical footprint of the whole process tree (vmmap / docker stats), Apple Silicon · macOS 15 · bench/footprint.ts

57 MB
single executable, no runtime prerequisite
~2 s
from command to serving requests
57 / 57
integration tests pass with the real supabase-js

Keep the SDK you already know

tinbase implements the PostgREST query grammar (filters, embedded resources, RPC), GoTrue auth flows, the Storage API, and the Realtime Phoenix protocol — verified by running the official SDK against it.

Row Level Security behaves exactly like hosted Supabase: every request runs with your JWT claims applied, so auth.uid() policies work as-is.

Read the docs →
import { createClient } from '@supabase/supabase-js'

const supabase = createClient('http://127.0.0.1:54321', ANON_KEY)

await supabase.auth.signUp({ email, password })
await supabase.from('todos').insert({ title: 'hello' })

const { data } = await supabase
  .from('todos')
  .select('*, author:users(name)')
  .eq('done', false)

supabase.channel('feed')
  .on('postgres_changes',
      { event: 'INSERT', schema: 'public', table: 'todos' },
      handleNewTodo)
  .subscribe()

Why tinbase exists

tinbase was built for lifo — a project that maps Linux APIs into the browser — to let Expo apps run fully in the browser with real full-stack capability: a database, auth, storage, and realtime, with no server behind them. That is why every tinbase service is a pure fetch handler and the database is Postgres compiled to WASM — the whole backend can live in-process, inside a browser tab.

It is part of RapidNative. The same architecture turned out to make a great standalone local backend — so it is open source for everyone.

Why tinbase

supabase-js works unchanged

REST, Auth, Storage, and Realtime speak the same wire protocols as hosted Supabase. Point the official SDK at tinbase and your app just runs.

Real Postgres, really small

RLS policies, auth.uid(), jsonb, triggers, foreign keys. Choose embedded native Postgres 17 (44 MB RAM) or PGlite WASM for zero-setup portability.

One file to deploy

A single 57 MB executable with no Node, npm, or Docker on the target machine. Postgres binaries (12 MB) auto-download on first run.

Your migrations stay portable

Reads supabase/migrations/*.sql and seed.sql exactly like the Supabase CLI, tracked in the same table. Outgrow tinbase? Push the same files to hosted Supabase.

Edge Functions & full auth

supabase.functions.invoke() runs your handlers in-process. Email/password, anonymous, OTP, magic links, and password recovery — all through supabase-js.

Runs in the browser

Every service is a pure fetch handler. Hand it to supabase-js as a custom fetch and the entire backend — database included — runs in-process, no server.

Built-in Studio

A Supabase-Studio-style dashboard at /_/ — full row CRUD, SQL editor, user management, and storage. Ships inside the binary, nothing extra to install.

Zero runtime dependencies

One npm dependency (@electric-sql/pglite). JWTs via WebCrypto, a hand-rolled WebSocket server, and a pure-Node Postgres wire client.