DEV Community

Russel Dsouza
Russel Dsouza

Posted on

How to Run a Supabase Workshop for 30 People Without a Single Docker Install

If you've ever taught a backend workshop, you know the first 40 minutes. Someone's Docker Desktop won't start. Someone's on an 8 GB laptop and the Supabase stack won't fit. Someone's corporate machine blocks virtualization. Someone's on Windows Home. By the time everyone has supabase start running, you've lost a third of the session and most of the room's energy.

I've run enough of these to want a setup where the whole room has real Postgres with Row Level Security in under two minutes, no Docker anywhere. This is that setup, using tinbase, an open-source, Supabase-compatible backend that runs as one process.

What every student needs installed

Node.js. That's the list.

npx tinbase start
Enter fullscreen mode Exit fullscreen mode

That boots embedded native Postgres 17 (macOS and Linux) with REST, Auth, Storage, Realtime, Edge Functions, and a Studio-style dashboard at http://localhost:54321/_/. About two seconds to boot, about 59 MB of RAM. The official @supabase/supabase-js works unchanged, and it reads the same supabase/migrations/*.sql files the Supabase CLI uses.

On Windows, or if native Postgres can't run for any reason, the same command works with the WASM engine (PGlite):

npx tinbase start --engine wasm
Enter fullscreen mode Exit fullscreen mode

Slower, heavier on memory, but still real Postgres and still no Docker.

The workshop repo structure

Give students one repo with the schema and seed data already written, so the session is about using Supabase, not typing create table:

workshop/
  supabase/
    migrations/
      20260101000000_init.sql
    seed.sql
  app/                 # the frontend (Expo, Next.js, plain HTML, whatever you teach)
  README.md            # "npm install && npx tinbase start"
Enter fullscreen mode Exit fullscreen mode
-- supabase/migrations/20260101000000_init.sql
create table public.posts (
  id uuid primary key default gen_random_uuid(),
  user_id uuid not null references auth.users(id) on delete cascade,
  title text not null,
  body text,
  created_at timestamptz not null default now()
);

alter table public.posts enable row level security;

create policy "read all posts" on public.posts
  for select to authenticated using (true);

create policy "write own posts" on public.posts
  for insert to authenticated with check ((select auth.uid()) = user_id);

create policy "edit own posts" on public.posts
  for update to authenticated using ((select auth.uid()) = user_id);
Enter fullscreen mode Exit fullscreen mode

Every student gets the same database, the same policies, the same seed rows. When someone's exercise breaks, you know it's their code, not their environment.

Exercise 1: the RLS "aha" moment

This is the exercise that makes RLS click, and it only works if every student has their own database with two users in it.

Have each student sign up twice in the app (two emails), create a post as each, then run:

const { data } = await supabase.from('posts').select('*');
Enter fullscreen mode Exit fullscreen mode

as user A. They see both posts, because the read policy is using (true). Then have them try to update user B's post as user A. Zero rows change. No error. Just nothing.

Then have them change the read policy to using ((select auth.uid()) = user_id), re-run the migration, and watch user B's post disappear from A's query.

That five-minute loop teaches more about RLS than an hour of slides. It needs a database each student can break and reset without asking you.

Reset is one command

When someone's database is in a weird state (and someone's always is):

npx tinbase start --memory
Enter fullscreen mode Exit fullscreen mode

In-memory mode boots a fresh database from the migrations and seed every time. Nothing persists, nothing to clean up, and it's the right default for a workshop anyway: students should be able to blow it away and start over in two seconds.

Exercise 2: realtime, in a room full of laptops

Realtime demos usually need a shared server so students see each other's changes. Here's a trick that avoids that: pair students up, and have one of them run tinbase on a port the other can reach on the venue Wi-Fi:

npx tinbase start --port 54321
Enter fullscreen mode Exit fullscreen mode

The partner points their app at http://<partner-ip>:54321. Now two laptops share one database, and a postgres_changes subscription on one shows inserts from the other. Real realtime, no cloud project, no shared credentials to leak.

Exercise 3: an Edge Function that holds a secret

Give students an API key they aren't allowed to put in the client. Have them write supabase/functions/summarize/index.ts, call it with supabase.functions.invoke('summarize'), and confirm the key never appears in the browser's network tab. Plain functions run in-process with no extra setup; functions that use npm: or jsr: imports need bundling first, so keep the exercise dependency-free.

What to tell students about the limits

Be straight with the room, because they'll ask:

  • tinbase is alpha, built for local dev, CI, prototypes, and teaching. Not production.
  • It covers roughly 80% of the supabase-js surface. MFA, SSO, phone auth, and pgvector aren't there yet.
  • Writes go through a single connection, so it's for one person (or one pair), not a class-wide shared server.
  • The migration files are the same ones they'll deploy to hosted Supabase. That's the point: nothing they learn today changes when they get a real project.

The wrap-up slide

Before the session ends, have everyone create a free hosted Supabase project and run:

supabase db push
Enter fullscreen mode Exit fullscreen mode

against the same supabase/migrations/ folder. The schema and RLS policies they wrote locally are now live in the cloud, unchanged. Students leave with a working backend they built, not a tutorial they followed.

Try it before your next session

npx tinbase start
Enter fullscreen mode Exit fullscreen mode

Source and the coverage table are on GitHub. If you teach with it, I'd like to hear what broke and what landed; workshop feedback is some of the most useful we get.

Top comments (0)