Skip to content

Project Setup and the Supabase CLI

A Supabase project starts as a hosted Postgres instance with two API keys of very different trust levels, and the Supabase CLI is what lets you link a local folder to it and run the entire stack on your own machine with Docker.

You create a new hosted project from the Supabase dashboard, or the shortcut database.new. Behind the scenes this provisions a dedicated Postgres instance, an API URL, and two API keys:

  • anon key — public, safe to ship in client-side code (a browser bundle, a mobile app). Requests using this key are subject to Row Level Security.
  • service_role key — secret, full-access, bypasses Row Level Security entirely. It is meant only for trusted server-side code.
// Server-side only — e.g. an API route, a cron job, an Edge Function.
// process.env.SUPABASE_SERVICE_ROLE_KEY must never be bundled into client code.
import { createClient } from '@supabase/supabase-js';
const supabaseAdmin = createClient(
process.env.SUPABASE_URL!,
process.env.SUPABASE_SERVICE_ROLE_KEY!
);

The CLI is how you manage a project as code instead of only through the dashboard:

Terminal window
# Scaffold a local supabase/ config directory in your project
npx supabase init
# Authenticate the CLI with your Supabase account
supabase login
# Connect this local project folder to a specific hosted project
supabase link --project-ref <project-ref>

supabase link is what tells your local supabase/ folder which hosted project it corresponds to — every later db push or db diff targets that linked project.

Terminal window
supabase start

This spins up the entire Supabase stack locally via Docker — Postgres, Studio, Auth, Storage, Realtime, and the rest, all running as containers on your machine. That means you can develop and test your schema, your RLS policies, and your client code completely offline, with no risk to your hosted project, before ever running supabase db push against it.

flowchart LR
  subgraph local["Local machine (supabase start)"]
    lpg[("Postgres")]
    lstudio["Studio"]
    lauth["Auth"]
    lstorage["Storage"]
  end
  subgraph hosted["Hosted project"]
    hpg[("Postgres")]
    hapi["API URL + anon/service_role keys"]
  end
  local -- "supabase link --project-ref" --> hosted
  local -- "supabase db push" --> hosted
A local Docker-based stack linked to a hosted project
What is the key difference between the anon key and the service_role key?
Why must the service_role key never reach client-side code?
What does `supabase start` do?
What does `supabase link --project-ref <ref>` do?