Bring up a full Supabase stack with Docker Compose — but with the part you actually care about done right: a single automated script that generates keys, renders configs, orchestrates startup, and waits until the system is truly ready.
Gouryella/supabase-tiny is a minimal, production-minded Supabase self-hosted setup built around deploy.sh — an idempotent deployment script that:
- generates/updates
.envsecrets (including real JWTs for ANON / SERVICE ROLE keys) - renders Kong declarative config from a template
- starts services in the correct order with readiness checks
- initializes the required Postgres roles/schemas and the MinIO storage bucket
If you want “clone → run one command → get a working Supabase”, this repo is for you.
- One entry point:
./deploy.shhandles config + startup end-to-end. - Safe-by-default networking: HTTP services bind to
127.0.0.1; you expose only what you intend (typically via Caddy on 80/443). - Repeatable upgrades: re-run the script to recreate config, refresh containers, and keep credentials in sync.
- Practical defaults: Kong is pre-wired for Auth, REST, Realtime, Storage, Functions, Meta, and Studio.
- Low-memory friendly: no bundled analytics/logging stack, and you can start only the services you need (keep Studio/Functions optional).
- One-command deploy (auto-install Docker, auto-download compose/kong template, then deploy). Installer will prompt profile selection:
Installer only downloads the compose file for the selected profile.
curl -fsSL https://raw.githubusercontent.com/Gouryella/supabase-tiny/main/install.sh | bash # Choose at prompt: # 1) tiny (Studio Go, lower memory) # 2) standard (Official Studio image) # Then enter your SUPABASE_PUBLIC_DOMAIN (example: supabase.yourdomain.com) # Finally confirm deployment with y/N
- Set your domain in
/root/supabase-tiny/Caddyfile(or$HOME/supabase-tiny/Caddyfileif not root), then rerun:bash /root/supabase-tiny/deploy.sh
- Open Studio:
https://<your-domain>/
# Skip prompt and force tiny profile
curl -fsSL https://raw.githubusercontent.com/Gouryella/supabase-tiny/main/install.sh | bash -s -- --tiny
# Skip prompt and use official profile
curl -fsSL https://raw.githubusercontent.com/Gouryella/supabase-tiny/main/install.sh | bash -s -- --standard
# Force container recreation
curl -fsSL https://raw.githubusercontent.com/Gouryella/supabase-tiny/main/install.sh | bash -s -- --recreate
# Skip final y/N confirmation (for automation)
curl -fsSL https://raw.githubusercontent.com/Gouryella/supabase-tiny/main/install.sh | bash -s -- --yes
# Custom install path
curl -fsSL https://raw.githubusercontent.com/Gouryella/supabase-tiny/main/install.sh | INSTALL_DIR=/opt/supabase bash
- Linux host with
curl(Docker is auto-installed byinstall.shif missing) opensslpython3orenvsubst(used to renderconfig/kong.yml)- Caddy (recommended) or any reverse proxy you prefer
This is still a real Supabase stack (multiple containers), so memory usage depends on traffic and which components you run. That said, this repo is designed to be friendly to small VPS setups:
- Studio analytics/logging are disabled by default (
ANALYTICS_ENABLED=false,NEXT_PUBLIC_ENABLE_LOGS=false). - You can deploy config once, then start a minimal service set and add heavier components only when needed.
Generate config and secrets first:
./deploy.sh --config-onlyStart only the core APIs behind Kong:
docker compose up -d \
db minio \
auth rest realtime storage imgproxy meta \
kongOptional (only if you need connection pooling):
docker compose up -d pgbouncerBring up Studio only when you actually need the UI:
docker compose up -d studioBring up Edge Functions only when you need them:
docker compose up -d functions- If you don’t need image transformation, set
ENABLE_IMAGE_TRANSFORMATION=falseforstorageand stopimgproxy. - On tiny VPSes, enabling swap can help avoid OOM kills during cold starts.
deploy.sh is not a thin wrapper around docker compose up.
It performs the kind of glue-work you usually end up doing by hand:
- Generates
.env(preserves existing values; fills missing ones) - Generates
ANON_KEYandSERVICE_ROLE_KEYas JWTs signed byJWT_SECRET - Renders
config/kong.ymlfromconfig/kong.yml.template - Initializes runtime bind-mount directories (
./volumes/functions,./volumes/snippets) - Starts Postgres + MinIO first, then waits for readiness
- Waits for Supabase initialization roles to exist (
supabase_auth_admin,supabase_storage_admin) - Creates the
_supabasedatabase and_realtimeschema if missing - Syncs service account passwords when
POSTGRES_PASSWORDchanges - Creates the
supabase-storagebucket in MinIO - Starts the full stack and prints access info
flowchart LR
client["Client / Browser"] -->|"HTTPS :443"| caddy["Caddy (TLS termination)"]
caddy --> kong["Kong Gateway"]
kong --> auth["Auth (GoTrue)"]
kong --> rest["REST (PostgREST)"]
kong --> realtime["Realtime"]
kong --> storage["Storage API"]
kong --> functions["Edge Functions Runtime"]
kong --> meta["Postgres Meta"]
kong --> studio["Studio"]
auth --> db["Postgres"]
rest --> db
realtime --> db
storage --> db
storage --> minio["MinIO (S3)"]
Most service ports are bound to 127.0.0.1 (loopback) unless noted otherwise.
| Service | Container | Local Port | Notes |
|---|---|---|---|
| Postgres | supabase-db |
13732 |
direct DB access (binds to all interfaces by default; consider restricting to 127.0.0.1) |
| Auth | supabase-auth |
13799 |
internal via Kong |
| REST | supabase-rest |
13730 |
internal via Kong |
| Realtime | supabase-realtime |
13740 |
internal via Kong |
| DB Pooler | supabase-pgbouncer |
13743 |
PgBouncer |
| Storage | supabase-storage |
13750 |
internal via Kong |
| Imgproxy | supabase-imgproxy |
13751 |
used by Storage |
| Meta | supabase-meta |
13760 |
internal via Kong |
| MinIO | supabase-minio |
13790 |
S3 API |
| MinIO Console | supabase-minio |
13791 |
admin UI |
| Kong | supabase-kong |
13780 |
reverse proxy target |
| Functions | supabase-functions |
13792 |
internal via Kong |
| Studio | supabase-studio |
13731 |
internal via Caddy → Kong |
./deploy.sh --tiny # tiny profile (default)
./deploy.sh --standard # standard profile
./deploy.sh --config-only # generate .env + kong.yml, do not start services
./deploy.sh --recreate # force recreate all containersProfiles map to compose files:
tiny→docker-compose.tiny.yml(Studio uses the Go version image)standard→docker-compose.yml(Studio uses the official Supabase Studio image)
You can also control profile via environment variables:
SUPABASE_DEPLOY_VARIANT=tiny|standard ./deploy.sh.envis generated automatically and contains secrets + service keys.config/kong.ymlis generated fromconfig/kong.yml.template(edit the template, not the output)../volumes/functions/is mounted into Edge Functions and Studio's Edge Functions manager../volumes/snippets/is mounted into Studio snippets management.
Important values:
SUPABASE_PUBLIC_DOMAIN(used for Studio URLs and GoTrue site URL)JWT_SECRET,ANON_KEY,SERVICE_ROLE_KEYPOSTGRES_PASSWORD,MINIO_ROOT_PASSWORD
Caddyfile proxies your public domain to Kong:
example.com {
encode zstd gzip
reverse_proxy http://127.0.0.1:13780
}To get HTTPS certificates:
- Ensure DNS points to your server
- Ensure ports
80and443are reachable from the internet
docker compose ps
docker compose logs -f
docker compose restart <service>
docker compose down- Deploy script times out waiting for roles: check
docker compose logs --tail=200 dband consider increasingROLE_WAIT_SECONDS. - Caddy cannot issue certificates: verify DNS and open ports 80/443; confirm the domain in
Caddyfile. - Studio loads but API calls fail: confirm
SUPABASE_PUBLIC_DOMAINis correct and restart the stack. Database error creating new userin Studio/Auth: keepauthon directdb:5432instead of transaction-pooled PgBouncer, otherwise GoTrue may hit prepared-statement errors likepgx_0 does not exist.
- Treat
.envas sensitive and never commit it. - Keep everything loopback-bound (default); expose only via a reverse proxy.
- Rotate keys and passwords if you suspect they were exposed.
- Restrict Postgres to localhost by changing the port mapping in
docker-compose.yml:- "13732:5432" + "127.0.0.1:13732:5432"
- Put a firewall in front of the host and expose only
80/443.
If this repo saves you time, consider starring it on GitHub.