{"id":50757994,"url":"https://github.com/arunrajiah/supafleet","last_synced_at":"2026-06-11T07:02:26.596Z","repository":{"id":355865562,"uuid":"1229839464","full_name":"arunrajiah/supafleet","owner":"arunrajiah","description":"Self-host multiple isolated Supabase projects on a single server, managed from a web UI","archived":false,"fork":false,"pushed_at":"2026-05-05T17:01:58.000Z","size":190,"stargazers_count":0,"open_issues_count":1,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-05-05T17:08:55.282Z","etag":null,"topics":["docker","multitenant","nextjs","postgresql","self-hosted","supabase"],"latest_commit_sha":null,"homepage":"https://github.com/arunrajiah/supafleet","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/arunrajiah.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":"CODE_OF_CONDUCT.md","threat_model":null,"audit":null,"citation":null,"codeowners":"CODEOWNERS","security":"SECURITY.md","support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":"NOTICE","maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-05-05T12:37:31.000Z","updated_at":"2026-05-05T17:01:36.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/arunrajiah/supafleet","commit_stats":null,"previous_names":["arunrajiah/supafleet"],"tags_count":null,"template":false,"template_full_name":null,"purl":"pkg:github/arunrajiah/supafleet","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/arunrajiah%2Fsupafleet","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/arunrajiah%2Fsupafleet/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/arunrajiah%2Fsupafleet/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/arunrajiah%2Fsupafleet/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/arunrajiah","download_url":"https://codeload.github.com/arunrajiah/supafleet/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/arunrajiah%2Fsupafleet/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":34186385,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-06-11T02:00:06.485Z","response_time":57,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["docker","multitenant","nextjs","postgresql","self-hosted","supabase"],"created_at":"2026-06-11T07:02:25.619Z","updated_at":"2026-06-11T07:02:26.591Z","avatar_url":"https://github.com/arunrajiah.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# supafleet\n\n\u003e Self-host **multiple isolated Supabase projects** on a single server — each with its own PostgreSQL database, Auth, REST API, and Storage — managed from a web UI.\n\n[![License: MIT](https://img.shields.io/badge/License-MIT-green.svg)](LICENSE)\n[![Docker](https://img.shields.io/badge/Docker-required-blue?logo=docker)](https://docs.docker.com/get-docker/)\n[![PRs Welcome](https://img.shields.io/badge/PRs-welcome-brightgreen.svg)](CONTRIBUTING.md)\n\n---\n\n## Why\n\nThe official Supabase self-hosted setup gives you **one database per server**. If you want to host multiple apps or give different users their own isolated Supabase instance, you'd normally need a separate VM for each.\n\n`supafleet` lets you run **N isolated tenants on one server** — each tenant gets:\n\n- A dedicated **PostgreSQL database** (inside a shared Postgres instance)\n- Its own **JWT secret** (no cross-tenant token reuse)\n- Dedicated **PostgREST**, **GoTrue (Auth)**, and **Storage API** containers\n- A subdomain: `myapp.yourdomain.com`\n\nEverything is managed from a **web UI** — no SSH required after the first `docker compose up`.\n\n---\n\n## Architecture\n\n```\nOne server\n│\n├── supabase-db        (PostgreSQL — all tenant databases live here)\n├── supabase-imgproxy  (shared image processing)\n├── supabase-nginx     (reverse proxy — port 80/443)\n└── supabase-admin     (management UI — http://your-server-ip)\n         │\n         ├─ tenant: myapp   → rest-myapp / auth-myapp / storage-myapp\n         ├─ tenant: client2 → rest-client2 / auth-client2 / storage-client2\n         └─ ...\n```\n\nEach tenant is a separate Docker Compose stack joined to a shared Docker network.\n\n---\n\n## Requirements\n\n| Tool | Version |\n|---|---|\n| Docker Engine | 24+ |\n| Docker Compose | v2 |\n| Python 3 | (for CLI scripts only) |\n\nA **2 vCPU / 4 GB RAM** server comfortably handles ~14 tenants. Upsize for more.\n\n---\n\n## Quick start\n\n```bash\n# 1. Clone\ngit clone https://github.com/arunrajiah/supafleet.git\ncd supafleet\n\n# 2. Configure\ncp .env.example .env\nnano .env   # set POSTGRES_PASSWORD, JWT_SECRET, DOMAIN\n\n# 3. Start  (pulls pre-built image from GHCR — no build step needed)\nmake up\n# or: docker compose up -d\n\n# 4. Open your browser\n# → http://your-server-ip\n# Set an admin password on first visit, then create databases from the UI.\n```\n\nPoint `*.yourdomain.com` (wildcard DNS) at your server and each tenant will be reachable at `\u003cname\u003e.yourdomain.com`.\n\n---\n\n## Web UI\n\nAfter `docker compose up`, open `http://your-server-ip` in a browser.\n\n| Screen | Description |\n|---|---|\n| **Setup** | First-run: set an admin password |\n| **Dashboard** | List all tenant databases with live container status |\n| **New database** | Name your tenant, optionally override the site URL |\n| **Tenant detail** | Copy API keys, endpoints, quick-start snippet; delete |\n| **Container actions** | Restart individual services or upgrade containers to latest images — no SSH needed |\n\n---\n\n## CLI (alternative)\n\nYou can also manage tenants from the command line — useful for scripting or CI:\n\n```bash\n# Provision a new tenant\n./scripts/add-tenant.sh myapp\n\n# List all tenants and their container status\n./scripts/list-tenants.sh\n\n# Remove a tenant (keeps the database)\n./scripts/remove-tenant.sh myapp\n\n# Remove a tenant AND drop the database (irreversible)\n./scripts/remove-tenant.sh myapp --drop-db\n```\n\n---\n\n## Connecting your app\n\nEach tenant exposes a standard Supabase-compatible API:\n\n```js\nimport { createClient } from '@supabase/supabase-js'\n\nconst supabase = createClient(\n  'http://myapp.yourdomain.com',   // shown in the admin UI\n  'YOUR_ANON_KEY'                  // shown after creation\n)\n```\n\n| Endpoint | Path |\n|---|---|\n| REST API | `/rest/v1/` |\n| Auth | `/auth/v1/` |\n| Storage | `/storage/v1/` |\n| GraphQL | `/graphql/v1` |\n\n---\n\n## Configuration (`.env`)\n\n| Variable | Description |\n|---|---|\n| `POSTGRES_PASSWORD` | Master Postgres password |\n| `JWT_SECRET` | Default JWT secret (admin DB only — each tenant gets its own) |\n| `JWT_EXPIRY` | Token TTL in seconds (default: 3600) |\n| `DOMAIN` | Base domain — tenants live at `\u003cname\u003e.$DOMAIN` |\n| `SMTP_HOST` / `SMTP_PORT` / `SMTP_USER` / `SMTP_PASS` | Shared SMTP for auth emails |\n| `SMTP_ADMIN_EMAIL` | Sender address |\n| `ENABLE_EMAIL_AUTOCONFIRM` | Skip email verification (`true`/`false`) |\n\n---\n\n## Capacity planning\n\n| Server | RAM | Tenants |\n|---|---|---|\n| 2 vCPU / 2 GB | 2 GB | ~6 |\n| 2 vCPU / 4 GB | 4 GB | ~14 |\n| 4 vCPU / 8 GB | 8 GB | ~30 |\n\nEach tenant uses ~250 MB RAM (rest + auth + storage containers). Shared services use ~300 MB base.\n\n---\n\n## Documentation\n\n| Guide | Audience |\n|---|---|\n| [Getting Started](docs/getting-started.md) | Install on a fresh server, first tenant |\n| [Configuration](docs/configuration.md) | All `.env` variables, `versions.json` reference |\n| [Managing Tenants](docs/managing-tenants.md) | Create, inspect, delete tenants via UI and CLI |\n| [HTTPS Setup](docs/https.md) | Caddy (auto-certs) and Nginx + Certbot |\n| [Backup \u0026 Restore](docs/backup-restore.md) | Database dumps, storage backups, migration |\n| [Architecture](docs/architecture.md) | How it works: containers, networking, JWT isolation |\n| [Local Development](docs/local-development.md) | Dev setup, API reference, adding features |\n| [FAQ](docs/faq.md) | Common questions |\n\n---\n\n## Updating Supabase versions\n\nAll service image tags are defined in a single [`versions.json`](versions.json) file at the project root.\n\nTo upgrade a tenant's containers to new image versions:\n1. Update the tags in `versions.json` (or merge a Renovate PR — it opens these automatically)\n2. Open the tenant's detail page in the admin UI → **Services → Actions → ↑ Upgrade services**\n\nThe upgrade pulls the new images, stops the old containers, and recreates them. Data is never touched.\n\n[Renovate](https://docs.renovatebot.com/) is pre-configured (`.github/renovate.json`) to open automated PRs when new image versions are released.\n\n---\n\n## HTTPS / TLS\n\nFor production, put Caddy or Nginx with Certbot in front:\n\n```bash\n# Example with Caddy (add caddy service to docker-compose.yml)\n*.yourdomain.com {\n    reverse_proxy supabase-nginx:80\n}\n```\n\nSee [`docs/https.md`](docs/https.md) for a step-by-step guide.\n\n---\n\n## Contributing\n\nContributions are welcome! See [CONTRIBUTING.md](CONTRIBUTING.md) for guidelines.\n\n---\n\n## Security\n\nFound a vulnerability? Please report it privately — see [SECURITY.md](SECURITY.md).\n\n---\n\n## Attribution\n\nPortions of this project are derived from [supabase/supabase](https://github.com/supabase/supabase),\nCopyright © Supabase, Inc., licensed under the [Apache License 2.0](https://www.apache.org/licenses/LICENSE-2.0).\n\nFiles derived from that project:\n- `volumes/db/jwt.sql`\n- `volumes/db/roles.sql`\n- `volumes/db/webhooks.sql`\n- `volumes/db/logs.sql`\n- `volumes/db/pooler.sql`\n- `volumes/db/realtime.sql`\n\nSee [NOTICE](NOTICE) for full attribution details.\n\n---\n\n## License\n\n[MIT](LICENSE) © 2025 Arun Rajiah\n\n---\n\n\u003e **Not affiliated with Supabase, Inc.** This is an independent community project.\n\u003e Supabase is a trademark of Supabase, Inc.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Farunrajiah%2Fsupafleet","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Farunrajiah%2Fsupafleet","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Farunrajiah%2Fsupafleet/lists"}