DockerSelf-Hosting

Supabase self-hosted with Docker: secure setup, every default replaced (Linux, Windows, Mac)

Before you start

  • 20 min read

Boxes marked Run in terminal are what you type; plain text explains why. Check what you need first

What you get in one minute#

Supabase self-hosted is the open-source Supabase backend (Postgres database, login, auto-generated REST API, file storage, realtime and edge functions) running as Docker containers on a machine you control, so your app's data never has to sit on someone else's cloud.

Supabase official logo

Logo: the official Supabase brand assets (supabase.com/brand-assets). Supabase and the Supabase logo are trademarks of Supabase Inc.

It suits a side project, a team tool, or an app with data-residency rules. It is not the cloud product: one project, community support, and you do the backups, updates and hardening. The rule that matters most: never start with the example passwords and keys.

WhatValue (official docs, read October 2026)
Release usedself-hosted/v0.8.3, 16 October 2026 (latest tag; images are tested together and published about monthly)
LicenceApache License 2.0 (LICENSE at the release tag)
Time needed"Less than 15 minutes" per the docs; allow 30 minutes with this guide's extra steps (our estimate)
MachineMinimum 4 GB RAM, 2 CPU cores, 40 GB SSD. Recommended 8 GB+, 4 cores+, 80 GB+ SSD
Ports8000 (gateway: dashboard and APIs), 5432 and 6543 (database pooler), 80 and 443 (only with the HTTPS proxy)
Containers11 by default (counted from the compose file at the tag)
Official docssupabase.com/docs/guides/self-hosting/docker

At a glance:

  • What runs: gateway (Envoy), Studio dashboard, Auth, REST (PostgREST), Realtime, Storage, image proxy, Edge Functions, Postgres 17 and a connection pooler.
  • The big risk: the example .env has working passwords and demo keys that everyone knows. Replace all of them before the first start.
  • Second risk: by default the gateway and the pooler publish ports on every network interface. This guide binds them to the local machine.
  • Platforms: Linux is the main route. Mac runs it through Docker Desktop. Windows runs it best inside WSL 2. We did not run Windows or Mac ourselves.
  • Privacy: the docs say it "does not phone home or collect any telemetry". "Security and testing" lists what still leaves your machine.

How it fits together#

Apps and the dashboard reach one gateway; the gateway routes by path to the services; every service talks to one Postgres database whose files live in your project folder.

Two-lane diagram: your app or browser reaches an optional Caddy or Traefik HTTPS proxy, then the Envoy gateway on port 8000, which routes by path to Auth, REST, Realtime, Storage, Edge Functions and Studio, all backed by Postgres 17; SQL tools connect through the Supavisor pooler on ports 5432 and 6543; Postgres writes to volumes/db/data and volumes/storage on the host

Drawn for this guide from the official compose file and docs. The dashed HTTPS proxy is optional.

What self-hosted lacks compared with the cloud#

Read this before you commit. The official self-hosting overview says it plainly.

TopicSupabase cloudSelf-hosted (official docs)
ProjectsMany organisations and projectsOne project; Studio has no multi-project support
Branching, Management API, ETLYesNot available
Backups and point-in-time recoveryManagedYou build them (see "Updates and backups")
Metrics and analyticsDashboardsLogs only; analytics are off until you add the logs override
Vector bucketsYesNot available
API keysSeveral sb_ keys per projectOne publishable key and one secret key
SettingsDashboardMostly .env variables
EmailBuilt inYou must supply a real mail server
SupportPlans"Community-supported" (Discussions, Issues, Discord)

You also own server upkeep, hardening, Postgres maintenance, high availability, backups, disaster recovery and monitoring. If that worries you, the managed platform is the honest answer.

Before you start#

SystemWhat you need
LinuxDocker Engine with Compose (v2.24.4 or newer for the override files), Git, openssl, and Node.js 16+ or Docker (the key script falls back to Docker)
WindowsWindows 10 or 11 with Docker Desktop (WSL 2 backend) and an Ubuntu distribution in WSL 2. Run the commands inside Ubuntu
MacDocker Desktop, Git (Xcode command line tools), Terminal. Intel and Apple silicon both work according to Docker

Check the tools. Linux or inside WSL Ubuntu:

Run in terminalbash
docker --version
docker compose version
git --version
openssl version

Windows PowerShell (installs Ubuntu once, then restart; skip if you have it):

Run in terminalpowershell
wsl --install -d Ubuntu
wsl -l -v
docker --version

In Docker Desktop settings, switch on WSL integration for Ubuntu, then run wsl -d Ubuntu to get the Ubuntu prompt.

Mac Terminal:

Run in terminalbash
docker --version
docker compose version
git --version

Checklist:

  • [ ] 4 GB RAM free for Docker (8 GB is better) and 40 GB of disk
  • [ ] Ports 8000, 5432 and 6543 free on the machine (docker ps shows nothing using them)
  • [ ] A password manager open: you will generate about a dozen secrets
  • [ ] For the HTTPS section only: a domain, DNS pointing to the server, ports 80 and 443 free

Tested by DIY Lab Hub: not yet. Commands come from the official docs and files at the release tag, read on 2026-10-30. We did not run them. The Docker images for the main services list both amd64 and arm64 on Docker Hub.

Step 1: Get the official files at a pinned release#

The Docker setup is a folder in the Supabase repository. You copy it into ~/docker_volumes/supabase, so your config and data live in one place.

Linux#

Run in terminalbash
mkdir -p ~/docker_volumes
cd ~/docker_volumes
git clone --depth 1 --branch self-hosted/v0.8.3 https://github.com/supabase/supabase supabase-source
mkdir -p supabase
cp -rf supabase-source/docker/. supabase
cd supabase
cp .env.example .env
chmod 600 .env
printf 'ref=self-hosted/v0.8.3\n' > .supabase-version
docker compose pull

The .supabase-version file lets the update script find your starting point later. You may delete ~/docker_volumes/supabase-source now.

Windows (PowerShell)#

Use the Linux commands inside Ubuntu on WSL 2. Keep the project in the Linux home folder, not on C:, so file permissions work:

Run in terminalpowershell
wsl -d Ubuntu

Then run the Linux block above at the Ubuntu prompt. The docs give no PowerShell-only route, and every script is sh. We did not test Windows.

Mac (Terminal)#

Same commands as Linux:

Run in terminalbash
mkdir -p ~/docker_volumes
cd ~/docker_volumes
git clone --depth 1 --branch self-hosted/v0.8.3 https://github.com/supabase/supabase supabase-source
mkdir -p supabase
cp -rf supabase-source/docker/. supabase
cd supabase
cp .env.example .env
chmod 600 .env
printf 'ref=self-hosted/v0.8.3\n' > .supabase-version
docker compose pull

Check it worked. ls ~/docker_volumes/supabase lists docker-compose.yml, .env, run.sh, utils and volumes, and docker compose pull ends without an error.

Tip: The docs also offer a Linux quick start (curl -fsSL https://supabase.link/setup.sh | sh) for Debian, Ubuntu and RHEL-family servers. It installs Docker and generates every secret, but takes the newest release, not a pinned one. Download and read it before running it.

Step 2: Replace every default secret#

The example .env ships working passwords and demo API keys. The docs say you should "never" start with them, and the file itself shouts that you must change all of them before the first start. Two official scripts do it.

Linux#

Run in terminalbash
cd ~/docker_volumes/supabase
sh utils/generate-keys.sh --update-env
sh utils/add-new-auth-keys.sh --update-env
rm -f .env.old docker-compose.yml.old
chmod 600 .env

The first script writes new values for the database password, JWT_SECRET, the legacy ANON_KEY and SERVICE_ROLE_KEY, the dashboard password and the encryption keys. The second creates the newer sb_publishable_ and sb_secret_ keys plus an ES256 key pair, and switches on the matching lines in docker-compose.yml. It needs Node.js 16+ or Docker (it pulls node:22-alpine once).

Both scripts leave .env.old copies behind. The second copy holds your freshly generated secrets, so the rm above matters.

Windows (PowerShell)#

Run the Linux commands at the Ubuntu prompt. Afterwards, from PowerShell, confirm that Ubuntu sees the project files:

Run in terminalpowershell
wsl -d Ubuntu -- ls -a /home/<your-user>/docker_volumes/supabase

Mac (Terminal)#

Same commands as Linux. macOS has sh and an openssl command; we did not run the scripts on a Mac:

Run in terminalbash
cd ~/docker_volumes/supabase
sh utils/generate-keys.sh --update-env
sh utils/add-new-auth-keys.sh --update-env
rm -f .env.old docker-compose.yml.old
chmod 600 .env

Now choose your own dashboard user name and look at what you have. Edit .env (nano .env) and change DASHBOARD_USERNAME from supabase if you like. The dashboard password must contain at least one letter and no special characters; the generated one does. POSTGRES_PASSWORD must be set before the first start; letters and numbers only avoid URL-encoding trouble.

Check it worked. This must print nothing. It looks for the example values and the signatures of the two demo keys:

Run in terminalbash
grep -nE "your-super-secret|this_password_is_insecure|your-32-character|your-encryption-key|supabaserealtime|UpNVntn3|secret1234|dc_X5iR_VP_qT0zsiyj_I_OZ2T9FtRU2BBNWN8Bu4GE|DaYlNEoUrrEn2Ig7tqibS-PHK5vgusbcbo7X36XVt4Q" .env
grep -c "^SUPABASE_SECRET_KEY=sb_secret_" .env

The second line should print 1. One value stays as in the example: POOLER_TENANT_ID=your-tenant-id. It is a label, not a secret; you may rename it.

Tip: Never paste .env into a chat, an issue or an AI assistant. Share variable names, not values. sh run.sh secrets prints key values to the screen, so run it only on your own terminal.

Step 3: Set the URLs and keep the ports private#

Three variables tell Auth where your instance lives. Then a small override file keeps the gateway and pooler off the network until you add HTTPS.

Linux#

Edit .env (nano .env). For a local trial keep the defaults. For a server with a domain, use your public address:

File contenttext
SUPABASE_PUBLIC_URL=http://<your-domain>:8000
API_EXTERNAL_URL=http://<your-domain>:8000/auth/v1
SITE_URL=http://<your-app-domain>

SITE_URL is where your own app lives, not Supabase. Use https:// URLs once the HTTPS section is done.

The official compose file publishes ports 8000, 5432 and 6543 on every interface. This override, our own and built from the docs' !override syntax, restricts them to the local machine:

Run in terminalbash
cd ~/docker_volumes/supabase
cat > docker-compose.local.yml << 'EOF'
services:
  api-gw:
    ports: !override
      - "127.0.0.1:${API_GW_HTTP_PORT:-8000}:8000/tcp"
  supavisor:
    ports: !override
      - "127.0.0.1:${POSTGRES_PORT}:5432"
      - "127.0.0.1:${POOLER_PROXY_PORT_TRANSACTION}:6543"
EOF
sh run.sh config add local

Windows (PowerShell)#

Do the same inside Ubuntu. Docker Desktop forwards localhost ports from WSL to Windows, so http://localhost:8000 opens in your Windows browser.

Mac (Terminal)#

Same commands as Linux. Docker Desktop for Mac publishes the loopback ports to your Mac.

Check it worked. sh run.sh config prints COMPOSE_FILE=docker-compose.yml:docker-compose.local.yml and lists both files.

Tip: Do not rely on ufw to protect Docker ports. Docker's own documentation says published ports are diverted before ufw's rules apply. Binding to 127.0.0.1 is the reliable fix. We did not run this override; if docker compose config complains, check that Compose is 2.24.4 or newer.

Step 4: Start Supabase and check every container#

Linux#

Run in terminalbash
cd ~/docker_volumes/supabase
sh run.sh start
docker compose ps

sh run.sh start is docker compose up -d --wait: it starts everything and waits until the containers report healthy.

Windows (PowerShell)#

Run the Linux commands at the Ubuntu prompt, then confirm from PowerShell:

Run in terminalpowershell
curl.exe -I http://localhost:8000/

Mac (Terminal)#

Same commands as Linux.

Check it worked. Per the docs, after a minute or less all services show Up ... (healthy). If one shows created but not Up, run:

Run in terminalbash
sh tests/test-container-logs.sh
sh run.sh logs storage

Use the name of the failing service. The docs publish no sample docker compose ps output, so we show none.

Step 5: Open the dashboard and test the keys#

Studio is behind HTTP basic authentication, with the user and password from .env.

Linux#

Open http://localhost:8000 on the same machine and log in. Test the API with the new keys (values from sh run.sh secrets):

Run in terminalbash
curl http://localhost:8000/rest/v1/ -H "apikey: <SUPABASE_SECRET_KEY>"
curl -i http://localhost:8000/rest/v1/ -H "apikey: <SUPABASE_PUBLISHABLE_KEY>"

On a remote server, tunnel instead of exposing the port: ssh -N -L 8000:127.0.0.1:8000 <your-user>@<your-server-address>.

Windows (PowerShell)#

Open http://localhost:8000 in your Windows browser. Test with curl.exe:

Run in terminalpowershell
curl.exe http://localhost:8000/rest/v1/ -H "apikey: <SUPABASE_SECRET_KEY>"

For a remote server run the same SSH tunnel from PowerShell.

Mac (Terminal)#

Same commands as Linux, with localhost:8000 in Safari or another browser.

Check it worked. You can log in, and the secret-key request returns "a valid response from PostgREST" (docs wording). The publishable key must return 403 on this endpoint; the docs say /rest/v1/ needs an admin-level key. Your app uses SUPABASE_PUBLIC_URL as supabaseUrl with the publishable key; the secret key stays on a trusted server.

Put it behind HTTPS (with or without Traefik)#

The docs call HTTPS "required for production": the dashboard password would otherwise cross the network in the clear, and OAuth providers need it. On localhost only, skip this section. Both routes need a domain whose DNS points to the server.

Official proxy rules: forward to the gateway on port 8000, allow WebSockets (Realtime), add X-Forwarded headers, drop the gateway's host port, and set the three URLs to https://.

With Traefik#

On a server that already runs Traefik v3 with the Docker provider, add a small override and join the proxy network. This assumes an entrypoint named https; use yours. Traefik handles WebSockets and forwarded headers by default (not tested here).

Run in terminalbash
docker network create proxy
cd ~/docker_volumes/supabase
cat > docker-compose.traefik.yml << 'EOF'
services:
  api-gw:
    ports: !reset []
    networks:
      default: {}
      proxy: {}
    labels:
      - "traefik.enable=true"
      - "traefik.docker.network=proxy"
      - "traefik.http.routers.supabase.rule=Host(`supabase.<your-domain>`)"
      - "traefik.http.routers.supabase.entrypoints=https"
      - "traefik.http.routers.supabase.tls=true"
      - "traefik.http.services.supabase.loadbalancer.server.port=8000"

networks:
  proxy:
    external: true
EOF
sh run.sh config add traefik

Set SUPABASE_PUBLIC_URL=https://supabase.<your-domain>, API_EXTERNAL_URL=https://supabase.<your-domain>/auth/v1 and SITE_URL=https://<your-app-domain> in .env, then sh run.sh recreate. If docker network create says the network exists, that is fine.

Check it worked. The docs' check works here too: curl -I https://supabase.<your-domain>/auth/v1/ returns 401, which proves you reached Auth over HTTPS.

Without Traefik: Caddy#

Supabase ships its own Caddy override. It gets and renews Let's Encrypt certificates for a public domain. Order matters: add caddy after local, because Caddy's override clears the gateway's host port.

Set PROXY_DOMAIN=<your-domain> and the three https:// URLs in .env, then run (Linux, WSL Ubuntu and Mac alike):

Run in terminalbash
cd ~/docker_volumes/supabase
sh run.sh config add caddy
sh run.sh start

The Caddyfile lives at volumes/proxy/caddy/Caddyfile. Its core is {$PROXY_DOMAIN} with reverse_proxy api-gw:8000 for the API paths and the dashboard behind basic auth. The container is supabase-caddy; ports 80, 443 and 443/udp must reach the server.

Home only, no public domain. Caddy's own local certificate authority works: add tls internal as the first line inside the {$PROXY_DOMAIN} { block of that Caddyfile and set PROXY_DOMAIN to a name your devices resolve (for example supabase.home.arpa in your router's DNS or a hosts entry). We did not run this edit; it follows Caddy's documented tls internal. Then copy Caddy's root certificate out and trust it on each device:

Run in terminalbash
docker exec supabase-caddy find /data -name root.crt
docker cp supabase-caddy:<path-from-find> ./caddy-root.crt

Install caddy-root.crt as a trusted root. These OS steps are not from the Supabase or Caddy docs: Windows, "Trusted Root Certification Authorities"; macOS, Keychain Access, "Always Trust"; Linux, your distribution's CA store. Windows and Mac are only clients here.

Check it worked.

Run in terminalbash
curl -I https://<your-domain>/auth/v1/
docker logs supabase-caddy --tail 50

You should see 401. A padlock for a tls internal name appears only after the root is trusted on that device.

Tip: Let's Encrypt has rate limits. If the certificate fails twice, fix DNS and ports first (the docs list both) and wait before retrying.

Security and testing#

ControlHow
SecretsReplace every default (Step 2). .env mode 600, never in Git or a chat
PortsGateway and pooler on 127.0.0.1 (Step 3). Only 80 and 443 face the internet
DashboardStudio can run SQL on your database. Strong password, HTTPS, ideally an SSH tunnel or a VPN
Postgres direct accessNot exposed by default. The docs warn that exposing it "opens your database to the network"
Secret keySUPABASE_SECRET_KEY and SERVICE_ROLE_KEY bypass row-level security. Server side only
Sign-upsDISABLE_SIGNUP=false by default. Set true if only you should create accounts
Rotationsh utils/rotate-new-api-keys.sh --update-env, then sh run.sh recreate
Production secretsThe docs suggest a secrets manager instead of .env

Tests after every change:

Run in terminalbash
cd ~/docker_volumes/supabase
docker compose ps
docker compose logs --tail=100 auth
curl -I http://localhost:8000/auth/v1/

The docs use this /auth/v1/ request over HTTPS as their connection test: a 401 means Auth answered.

What leaves the machine. Per the docs, no telemetry. Still: image downloads, the Let's Encrypt request for your domain, mail through the SMTP server you configure, OAuth providers you enable, and prompts to OpenAI if you add your own OPENAI_API_KEY for the Studio AI Assistant. The separate Supabase CLI does collect telemetry.

Common mistakes#

These come from the official docs' warnings and from open or closed issues on the Supabase repository, read in October 2026.

MistakeWhy it happensDo this instead
Starting with .env.example as it isIt looks like a ready config. The docs say you should "never" use these valuesRun both key scripts (Step 2) and run the check that prints nothing
A dashboard password with only digits or special charactersThe docs: the password "must include at least one letter" and no special charactersLetters plus numbers; let the script generate it
Changing POSTGRES_PASSWORD after the first startThe database was created with the old oneSet it before the first start. Later use sh utils/db-passwd.sh, then sh run.sh recreate
Leaving ports 5432 and 6543 on every interfaceThe compose file publishes the pooler without an address, and ufw does not filter Docker portsBind to 127.0.0.1 (Step 3). Open Postgres to others only with a firewall
Rootless Docker without telling SupabaseThe log collector cannot find the socket (container supabase-vector exited (0)). Issue #36726: Storage uploads fail with 500 when volumes/storage is owned by rootSet DOCKER_SOCKET_LOCATION in .env; make the Storage folder writable for your user
Expecting big downloads or slow SQL through the gatewayIssue #51618 (open): Envoy cuts most routes after 30 seconds; big files arrive truncated with status 200, long Studio queries return 504Keep requests short; read the issue before relying on big files
Sending a redirect_to header to AuthIssue #51791 (open): Envoy rejects headers with underscoresPass redirect_to as a query parameter
Changing POSTGRES_DBIssue #50486 (open): jwt.sql hard-codes postgres, so the JWT settings land in the wrong databaseKeep the default database name
Installing on a 2 GB machineIssue #44376 (open): Studio's health check has no start period, so slow machines fail in a cascade. The docs ask for 4 GBUse 4 GB or more; if it fails once, run sh run.sh start again
Waiting for sign-up emails that never arrive.env points to a placeholder mail host (supabase-mail)Configure a real SMTP server in .env, then restart
AI assistants: describing the old layoutMany answers still show Kong as the gateway, Postgres 15 and only ANON_KEY / SERVICE_ROLE_KEY. The docs now use Envoy (since v0.8.0), Postgres 17 and the sb_ keysFollow the pinned release's own files and docs, not remembered configs
AI assistants: telling you to run supabase start to self-hostThe CLI starts a local test stackThe docs say that stack "is not a self-hosted deployment" and "must not be exposed to external traffic". Use the Docker files here
AI assistants: putting the secret key in browser codeExamples often use one key for everythingPublishable key in the app, secret key on the server only

Tip: On Windows keep the project inside the Linux file system of WSL 2. The docs warn that Windows line endings (CRLF) can break the gateway start-up with an entrypoint error.

Tip: Before any update, copy your .env and read the changelog entry. The docs say update.sh backs up configuration only, never your data.

Tip: After each step, run docker compose ps and read the first error line in sh run.sh logs <service>. Most failures print the cause there.

Troubleshooting#

ProblemSolution
A service shows created, not Upsh tests/test-container-logs.sh, then sh run.sh logs <service>
container supabase-vector exited (0)Rootless Docker: set DOCKER_SOCKET_LOCATION in .env (for example /run/user/1000/docker.sock)
Gateway fails with an entrypoint errorFiles have CRLF line endings. Re-clone inside WSL or Linux, or convert docker/ files to LF
Studio unhealthy, rest of the stack never startsSlow start (issue #44376). Give Docker more RAM, then sh run.sh start again
403 on /rest/v1/Expected with the publishable key. Use the secret key (docs)
504 upstream request timeout or truncated downloadsThe 30 second gateway timeout (issue #51618). Shorten the request
Certificate not issuedPorts 80 and 443 open? DNS A record correct? docker logs supabase-caddy (docs)
OAuth "callback URL mismatch"API_EXTERNAL_URL must be your HTTPS URL plus /auth/v1; then sh run.sh recreate (docs)
Realtime socket fails behind your proxyThe proxy must pass WebSocket upgrades (Caddy does; Nginx needs explicit headers)
Changed .env but nothing changedsh run.sh recreate (environment changes apply only when containers are recreated)

Known bugs (open when read, October 2026)#

  • Realtime in v0.8.3: Postgres Changes events can be missed for a few seconds every 10 minutes. The changelog's workaround is REGION: us-east-1 under realtime environment: in docker-compose.yml. It is not fixed in this release.
  • Issue #43643: pg_wal files can grow without limit when the analytics replication slot falls behind. Only relevant if you enabled the logs override; watch disk space.
  • Issue #52346: the gateway's CORS rules for /functions/v1 allow every origin and override your function's own CORS.

Diagnostics: docker compose ps, sh run.sh logs <service>, sh run.sh printenv <service>, docker inspect supabase-db.

Updates and backups#

Update. Supabase publishes tested snapshots about once a month. Releases can contain one-way database migrations: v0.8.3 changes Storage so that rolling back to the previous Storage version makes uploads fail. Back up first (below), then:

Run in terminalbash
cd ~/docker_volumes/supabase
sh update.sh --dry-run
sh update.sh
sh run.sh pull
sh run.sh recreate

update.sh needs git and jq (on Mac, brew install jq), keeps your .env and data, merges new files over yours and stops at breaking changes. Pin a release with sh update.sh --to self-hosted/<tag>.

Our override files are not part of Supabase's release, so after an update check that sh run.sh config still lists them. Note any docker-compose.yml edit (such as the Realtime workaround): it may conflict.

Back up. The official docs give no database backup command; they say you own backups. This is a plain cold copy, our method: stop, archive the database files, uploads, config and the pgsodium key volume, start.

Run in terminalbash
cd ~/docker_volumes/supabase
cat > backup.sh << 'EOF'
#!/bin/sh
cd "$(dirname "$0")" || exit 1
mkdir -p backups
sh run.sh stop
tar -czf "backups/supabase-$(date +%F).tar.gz" .env docker-compose.yml volumes/db/data volumes/storage volumes/functions
docker run --rm -v supabase_db-config:/src -v "$PWD/backups":/dst alpine tar -czf "/dst/db-config-$(date +%F).tar.gz" -C /src .
chmod 600 backups/*.tar.gz
sh run.sh start
EOF
sudo sh backup.sh

sudo is needed on Linux because the database files belong to the database user; on a Mac, drop it. Copy the archives to another machine; they contain every secret.

Schedule. Linux and Mac: sudo crontab -e, then add 0 3 * * * sh /home/<your-user>/docker_volumes/supabase/backup.sh (on Mac use your own home path). Windows: a Task Scheduler task that runs wsl -d Ubuntu -u root -- sh /home/<your-user>/docker_volumes/supabase/backup.sh daily at 03:00. We did not test the schedules.

Restore test. Unpack into a new empty folder, never over the live one:

Run in terminalbash
mkdir -p /tmp/supabase-restore && tar -xzf backups/supabase-<date>.tar.gz -C /tmp/supabase-restore
ls /tmp/supabase-restore/volumes/db/data | head

Uninstall. sh reset.sh deletes the containers, named volumes, database and storage folders; irreversible.

Performance and resources#

The official minimum is 4 GB RAM, 2 cores and 40 GB SSD; recommended 8 GB+, 4 cores+ and 80 GB+. We did not measure memory or CPU, so this page gives no figures of its own.

  • Watch it: docker stats shows live CPU and memory for each container.
  • Slim it down: the docs say you may remove services you do not need (Realtime, Storage, imgproxy, Edge Runtime) from docker-compose.yml.
  • Logs: analytics are off by default because they add load. sh run.sh config add logs turns them on.
  • Connections: pooler defaults are 20 database and 100 client connections (POOLER_DEFAULT_POOL_SIZE, POOLER_MAX_CLIENT_CONN).
  • Mac: Docker Desktop bind mounts can break the Storage file backend. The docs say to switch that mount to a named Docker volume.

FAQ#

Is it free? The code is Apache License 2.0. You pay for the server and your own time.

Can I run more than one project? Not in one stack: Studio supports a single project.

Can I run it on a Raspberry Pi? The main images list arm64, but the docs ask for 4 GB RAM and an SSD, and issue #44376 shows small machines struggling.

Does it send data to Supabase? The docs say it does not phone home or collect telemetry. See "What leaves the machine" for the traffic you cause yourself.

Where do I get help? GitHub Discussions and Issues with the self-hosted label, Discord and Reddit, per the docs.

Sources#

Checked against the official docs on 2026-10-30.