Skip to content
Guides · Recipes

Run Supabase Locally in a Cloud Sandbox

Updated October 27, 2026 .md

Short answer: Create a default Sandboxer sandbox, install the Supabase CLI from its .deb release, then run supabase init and supabase start. Docker is preinstalled. The first start took about 100 seconds. Host port 54323 (Studio) and 54321 (API) with sbx host for private HTTPS URLs. A memory-restoring stop and resume keeps the stack running.

This is the Supabase CLI's local stack: Postgres, the API gateway, Auth, Storage, Realtime and Studio in Docker containers on one machine. It is not a Supabase cloud project. You don't need a Supabase account, and the keys it prints are shared local development defaults.

At a glance#

Step What we saw (single runs, October 27, 2026, default size)
Install Supabase CLI 2.122.0 from the .deb release; Docker 29.9.0 and Node 22 were already there
supabase start, first run About 100 seconds, most of it pulling images; 12 containers running
supabase stop + supabase start 11.7 s to stop, 25.8 s to start again with images cached; data kept
Memory after start About 2.1 GB used of 8 GB
Studio on a hosted URL 401 without the token, 302 then 200 with it
REST call through the hosted URL 200, median 0.22 s over 20 calls from our test machine; 3.6 ms inside the sandbox
sbx stop --wait + sbx resume 2.2 s and 1.4 s; memoryRestored: true, same container PIDs, same rows

Which size#

We used default (4 vCPU, 8 GB RAM, 50 GB disk, $0.036/hour). Supabase's local development guide gives no minimum RAM, CPU or disk. The stack runs 12 containers and used about 2.1 GB of RAM once up, so default leaves headroom for your app and a test run next to it. We didn't try small (4 GB). Moving to a smaller size isn't supported yet, so pick the size you plan to stay on.

Install the CLI and start the stack#

Supabase lists Homebrew, Linux packages and npx supabase as install methods. We used the Linux .deb from the CLI releases:

sbx new --name supabase-dev --type default
id=sb_ydztcc1fj6    # the id that sbx new printed

sbx exec $id 'curl -fsSLo /tmp/supabase.deb https://github.com/supabase/cli/releases/download/v2.122.0/supabase_2.122.0_linux_amd64.deb && sudo dpkg -i /tmp/supabase.deb && supabase --version'
Unpacking supabase (2.122.0) ...
Setting up supabase (2.122.0) ...
2.122.0

The first supabase start pulls about a dozen images and runs longer than a foreground sbx exec waits (ours ended with exit 124). Run it detached and poll a result file:

sbx exec $id 'mkdir -p ~/myapp && cd ~/myapp && supabase init'
sbx exec $id --detach 'cd ~/myapp && s=$(date +%s.%N); supabase start > start.log 2>&1; echo exit=$? > start.result; echo elapsed=$(python3 -c "print(round($(date +%s.%N)-$s,1))") >> start.result'

sbx exec $id 'cat ~/myapp/start.result'
exit=0
elapsed=101.0

Part of the images had already been downloaded by our timed-out first attempt, so a completely fresh pull can take longer. The tail of start.log:

Started supabase local development setup.
│ Studio  │ http://127.0.0.1:54323     │
│ Mailpit │ http://127.0.0.1:54324     │
│ Project URL    │ http://127.0.0.1:54321              │
│ REST           │ http://127.0.0.1:54321/rest/v1      │
│ URL │ postgresql://postgres:postgres@127.0.0.1:54322/postgres │
…
Local dev security notice
All services bind to 0.0.0.0 (network-accessible, not just localhost)
API keys and JWT secrets are shared defaults. Do not use in production
Studio, pgMeta (/pg/*), and analytics have no authentication

supabase status -o env prints the same values as shell variables, including ANON_KEY, PUBLISHABLE_KEY and SERVICE_ROLE_KEY. They are demo defaults, but don't paste the service role key into logs or chat anyway.

Create a table and call it over REST#

The sandbox has no psql, so run it inside the database container. The container is named supabase_db_<project id>, and the project id defaults to the folder name.

-- todos.sql
create table public.todos (
  id bigint generated always as identity primary key,
  task text not null,
  done boolean not null default false
);
alter table public.todos enable row level security;
create policy "anyone can read todos" on public.todos
  for select to anon using (true);
insert into public.todos (task) values ('write the guide'), ('delete the sandbox');
sbx scp todos.sql $id:/home/user/myapp/todos.sql
sbx exec $id 'cd ~/myapp && docker exec -i supabase_db_myapp psql -U postgres -d postgres < todos.sql'
sbx exec $id 'cd ~/myapp && eval $(supabase status -o env 2>/dev/null | grep ^ANON_KEY=); curl -s "http://127.0.0.1:54321/rest/v1/todos?select=id,task,done" -H "apikey: $ANON_KEY"'
CREATE TABLE
ALTER TABLE
CREATE POLICY
INSERT 0 2
[{"id":1,"task":"write the guide","done":false},
 {"id":2,"task":"delete the sandbox","done":false}]

PostgREST serves the new table at once. An insert with the anon key was refused by the policy: 401 with new row violates row-level security policy for table "todos".

Host Studio and the API on private URLs#

The CLI binds every service to 0.0.0.0, so sbx host reaches them without --localhost. Keep both private (the default):

sbx host $id 54323 --title studio
sbx host $id 54321 --title api
{"ok":true,"type":"port.hosted","sandboxId":"sb_ydztcc1fj6","port":54323,"url":"https://vivid-able-fig-54323.box.sandboxer.com?_token=<token>","access":"private",…}
{"ok":true,"type":"port.hosted","sandboxId":"sb_ydztcc1fj6","port":54321,"url":"https://vivid-able-fig-54321.box.sandboxer.com?_token=<token>","access":"private",…}

Open the Studio link in a browser. What curl returned:

curl -s -o /dev/null -w '%{http_code}\n' https://vivid-able-fig-54323.box.sandboxer.com/
# 401   (no token)

curl -sL -c jar -b jar -o /dev/null -w '%{http_code} %{url_effective}\n' "https://vivid-able-fig-54323.box.sandboxer.com?_token=<token>"
# 200 https://vivid-able-fig-54323.box.sandboxer.com/project/default

For the API, trade the token for the __sbx_auth cookie once, then call PostgREST with the anon key as usual:

API=https://vivid-able-fig-54321.box.sandboxer.com
curl -s -o /dev/null -c api.jar "$API/?_token=<token>"        # 302, sets the cookie
curl -s -b api.jar "$API/rest/v1/todos?select=id,task,done" -H "apikey: $ANON_KEY" -w '\nHTTP %{http_code} in %{time_total}s\n'
[{"id":1,"task":"write the guide","done":false},
 {"id":2,"task":"delete the sandbox","done":false}]
HTTP 200 in 0.240160s

Over 20 calls from our test machine to the sandbox (region eu-central), the median was 0.22 s. Inside the sandbox the same call took 3.6 ms, so nearly all of that is the network path.

Don't make these ports public. Studio has no login, as the CLI's notice says. In this CLI version the local gateway also answered anon reads with no apikey header and with apikey: nope (both 200). On a private URL the _token is the only thing keeping strangers out. Treat the full URL and the cookie like a password.

Stop and resume: the stack keeps running#

Before stopping, the sandbox had been up 325 seconds and supabase_db_myapp had PID 13272.

sbx stop $id --wait     # 2.2 s
sbx resume $id          # 1.4 s
sbx info $id            # "memoryRestored": true
sbx exec $id 'cat /proc/uptime; docker inspect -f "{{.Name}} {{.State.Pid}}" $(docker ps -q) | sort | head -3; docker ps -q | wc -l'
342.25 1001.76
/supabase_analytics_myapp 13537
/supabase_auth_myapp 14153
/supabase_db_myapp 13272
12

Uptime kept counting and the PIDs are unchanged: memory was restored, so nothing restarted. The REST call through the hosted URL returned the same two rows, and Studio answered 200, with no new sbx host call. Routes and tokens are kept across stop and resume.

If the sandbox boots from disk instead#

Resume boots from disk, keeping files but not processes, when you resize on resume or no machine with the same CPU type is free. Docker is an enabled systemd service and the database lives in Docker volumes on disk, so what comes back depends on each container's restart policy. The CLI sets them for you:

sbx exec $id 'docker inspect -f "{{.Name}} restart={{.HostConfig.RestartPolicy.Name}}" $(docker ps -q) | sort'
/supabase_analytics_myapp restart=unless-stopped
/supabase_auth_myapp restart=unless-stopped
/supabase_db_myapp restart=unless-stopped
/supabase_edge_runtime_myapp restart=no
…   (the other 8 containers: unless-stopped)

We didn't resize, so we tested the same mechanism by restarting the Docker daemon (sudo systemctl restart docker). Eleven containers came back healthy and the rows were still there. supabase_edge_runtime_myapp stayed Exited (15), and supabase start replied supabase start is already running. without starting it. docker start supabase_edge_runtime_myapp brought it back. Do the same after a cold boot if you use Edge Functions.

Clean up#

supabase stop keeps your data in Docker volumes (supabase stop --no-backup deletes it). Deleting the sandbox removes everything:

sbx delete $id --yes

After the delete, the hosted API URL returned 404.

FAQ#

Is this the same as a Supabase cloud project?#

No. supabase start runs the open-source services in Docker inside your sandbox, with no Supabase account and shared demo keys. Use it for development and tests; it doesn't replace a hosted project.

Can my app outside the sandbox use the hosted API URL?#

Yes, if it can send the __sbx_auth cookie: open the ?_token= link once, keep the cookie, and pass the anon key in the apikey header. We tested this with curl. Code running inside the sandbox can call http://127.0.0.1:54321 directly.

Why did the first start time out?#

It pulls about a dozen images, which ran past the time a foreground sbx exec waits, and the command ended with exit 124. Start it with sbx exec $id --detach and poll a result file, as shown above. With images cached, a restart took 25.8 seconds.

Do I lose my tables when the sandbox stops?#

No. A memory-restoring resume left all 12 containers running with the same PIDs. A boot from disk keeps the Docker volumes, and 11 of the 12 containers restart on their own.

Can I connect to Postgres directly?#

Port 54322 is the database. From your laptop, forward it over SSH with sbx forward $id --remote 54322 rather than a hosted URL, which serves HTTP. We didn't run this step; see SSH.

More guides

Top AI sandbox platforms for code execution in 2026, ranked

For running AI-written code we rank Sandboxer, Freestyle and E2B first: each gives code its own VM with an API or SDK and can pause with memory. Pick Daytona for Windows or GPUs, Modal or Northflank for GPUs at scale, E2B or Northflank to run in your own cloud, and Vercel Sandbox if your app lives on Vercel.

Read the guide
Best sandboxes for running coding agents (Claude Code, Codex, Gemini CLI) in 2026, ranked

For coding agents we rank Sandboxer first: a full Ubuntu VM with Claude Code and Codex preinstalled, Docker, and stop-and-resume that keeps the agent's session in memory. Freestyle and Amp Orbs also keep memory across pauses. Pick E2B for prebuilt agent templates on open source, Docker Sandboxes for free local runs, and Codespaces if you live in GitHub.

Read the guide
Best Daytona sandbox alternatives in 2026, ranked

The best Daytona alternatives depend on why you're switching. For a full Linux microVM on every sandbox with memory-saving stop and Docker, we rank Sandboxer first. E2B suits open source and self-hosting, Modal massive short-job fleets, Northflank bring-your-own-cloud with GPUs, and Vercel Sandbox apps on Vercel.

Read the guide
Best E2B alternatives in 2026, ranked

If you're leaving E2B for longer sessions or a fuller machine, we rank Sandboxer first, then Freestyle: both give each sandbox a full Linux VM that keeps memory across pauses, with Docker and no short session cap. Pick Daytona for Windows or GPUs, Northflank for bring-your-own-cloud, and stay on E2B if you need open source.

Read the guide