Deployment
What a Deployment Serves
Section titled “What a Deployment Serves”A Rebase project deploys as one server at one URL (on Rebase Cloud: https://<project>.rebase.website). That server handles:
/api/*— the data API, authentication, realtime, and storage- everything else — your built
frontend/as a static SPA
There is no separate admin URL: the admin panel is part of your frontend, so where it appears depends on what your frontend is.
| Project type | Root URL shows | Admin panel is at |
|---|---|---|
Default scaffold (rebase init) |
The admin panel | / — the frontend is the admin |
| Custom product frontend | Your app | Wherever you mount it, commonly /admin — see Changing the Base URL |
| Backend-only project | Nothing (API only) | Not deployed |
Docker Compose (Recommended)
Section titled “Docker Compose (Recommended)”The generated project already includes a working docker-compose.yml (Postgres + backend + frontend) and the backend//frontend/ Dockerfiles — that generated file is the source of truth; use it as-is rather than hand-writing one. The shape is:
services: db: image: postgres:18-alpine environment: POSTGRES_USER: rebase POSTGRES_PASSWORD: ${DATABASE_PASSWORD:-changeme} POSTGRES_DB: rebase ports: - "5432:5432"
backend: build: # Context is the PROJECT ROOT so the image can copy # pnpm-workspace.yaml, backend/, and config/. A `./backend` # context would fail — the Dockerfile lives at backend/Dockerfile. context: . dockerfile: backend/Dockerfile ports: - "3001:3001" env_file: .env depends_on: - db
volumes: postgres_data: uploads:docker compose up -dCreate the database schema
Section titled “Create the database schema”Bringing the stack up is not enough on its own. The backend boots and
auto-creates the auth tables, but it does not create the tables for
your own collections — you run that once, explicitly, against the production
database. From a checkout of your project (with dependencies installed), set
DATABASE_URL to your production database and push the schema:
pnpm run db:pushdb:push is the fast option — it applies the schema directly, with no
migration files. For a versioned, team workflow, commit migration files
with pnpm run db:generate and run pnpm run db:migrate as a release step
instead. Whichever you choose, it runs against the production DATABASE_URL
from a project checkout (or your CI job), not inside the running container —
the production image ships without the CLI.
Production Checklist
Section titled “Production Checklist”Before deploying to production, ensure:
| Item | Details |
|---|---|
| Database schema | Run pnpm run db:push (or pnpm run db:migrate for versioned migrations) against the production database once. The app boots without your collection tables, but every collection errors until they exist. |
| JWT_SECRET | Use a cryptographically strong random string (≥ 32 chars). Never reuse across environments. |
| DATABASE_URL | Use a managed Postgres instance (Neon, Supabase, RDS) with TLS enabled |
| CORS | Configure allowed origins on your backend if frontend and backend are on different domains |
| Storage volumes | Mount persistent volumes for file uploads. Or switch to S3 for production. |
| HTTPS | Terminate TLS at your reverse proxy (nginx, Cloudflare, load balancer) |
| Registration | Set ALLOW_REGISTRATION=false after creating your admin account |
Serving the Frontend
Section titled “Serving the Frontend”In production, the backend can serve the frontend as a static SPA:
import { serveSPA } from "@rebasepro/server";import path from "path";
// After initializeRebaseBackend()serveSPA(app, { frontendPath: path.resolve(process.cwd(), "../frontend/dist") });Build the frontend first:
cd frontend && pnpm buildThis way you only need to deploy one server that handles both SPA and API.
Platform Deployment Guides
Section titled “Platform Deployment Guides”Detailed step-by-step guides for each platform:
| Platform | Type | Guide |
|---|---|---|
| AWS | App Runner / ECS + RDS | Deploy on AWS → |
| Google Cloud | Cloud Run + Cloud SQL | Deploy on GCP → |
| Azure | Container Apps + PostgreSQL | Deploy on Azure → |
| Hetzner Cloud | VPS + Docker Compose | Deploy on Hetzner → |
| Scaleway | Serverless Containers | Deploy on Scaleway → |
| Railway | PaaS (auto-detect Dockerfile) | Deploy on Railway → |
| Fly.io | Container runtime | Deploy on Fly.io → |
Changing the Base URL
Section titled “Changing the Base URL”If you want Rebase to run at a sub-path (e.g., /admin):
Frontend — Update the BrowserRouter basename:
<BrowserRouter basename="/admin"> <App /></BrowserRouter>Backend — Update the base path:
await initializeRebaseBackend({ // ... basePath: "/admin/api"});Product App + Admin in One Deployment
Section titled “Product App + Admin in One Deployment”The common reason to move the admin to /admin is shipping your own product app
at the root of the same deployment. A single Vite entry can serve both, split by URL,
so each app is lazy-loaded and product visitors never download the admin bundle:
const isAdmin = window.location.pathname.startsWith("/admin");
const ProductApp = lazy(() => import("./App"));const AdminApp = lazy(() => import("./AdminApp")); // renders <RebaseAdmin basePath="/admin" />
if (isAdmin) { // The admin uses useBlocker → needs a data router const router = createBrowserRouter([{ path: "/admin/*", element: <AdminApp /> }]); root.render(<RouterProvider router={router} />);} else { root.render(<BrowserRouter><ProductApp /></BrowserRouter>);}The backend needs no changes for this pattern — the API stays at /api and the SPA
catch-all serves index.html for both / and /admin/*.
Next Steps
Section titled “Next Steps”- Backend Overview — Full backend configuration
- Storage Configuration — S3 setup for production
