Deploy to Railway
Supabase stays as-is (Postgres, Auth, Storage). Railway hosts the API, the static frontend, and this documentation site.
Project layout
Section titled “Project layout”| Service | Root directory | Config |
|---|---|---|
| API | server |
server/railway.toml |
| Frontend | repo root | railway.toml |
| Docs | docs |
docs/railway.toml |
The API is self-contained: catalog seed data lives in server/src/lib/catalog/ (no dependency on frontend src/). Railway can build with Root Directory = server.
1. API service
Section titled “1. API service”- New service → GitHub repo → set Root Directory to
server - Variables (from
server/.env.example):
| Variable | Notes |
|---|---|
DATABASE_URL |
Supabase transaction pooler (port 6543) |
DIRECT_URL |
Supabase session pooler (port 5432) |
SUPABASE_URL |
|
SUPABASE_SERVICE_ROLE_KEY |
|
OPENAI_API_KEY |
Required for card creator chat |
CORS_ORIGIN |
Public frontend URL, e.g. https://worker24-web.up.railway.app |
CREATOR_IMAGE_BUCKET |
Optional, e.g. creator-images |
AVATAR_IMAGE_BUCKET |
Optional, e.g. avatars |
DEMO_USER_PASSWORD |
Optional |
PORT is set by Railway automatically.
- Deploy → copy the public API URL (e.g.
https://worker24-api.up.railway.app)
Build runs prisma generate + tsc. Start runs prisma migrate deploy then the server.
Health check: GET /health
2. Frontend service
Section titled “2. Frontend service”- New service → same repo → Root Directory =
/(repo root) - Variables (must be present before build — Vite bakes them in):
| Variable | Example |
|---|---|
VITE_USE_MOCK_DATA |
false |
VITE_API_URL |
https://worker24-api.up.railway.app |
VITE_SUPABASE_URL |
https://xxx.supabase.co |
VITE_SUPABASE_ANON_KEY |
anon key |
- Deploy → copy the public frontend URL
- Go back to the API service and set
CORS_ORIGINto that frontend URL (redeploy API if needed)
3. Docs service (Starlight)
Section titled “3. Docs service (Starlight)”- New service → same repo → set Root Directory to
docs - No secrets required for a static docs build
- Deploy → public URL (e.g.
https://worker24-docs.up.railway.app)
Build runs npm install && npm run build (Astro/Starlight → dist/).
Start serves the static site with serve on $PORT.
Optional: point a custom domain like docs.yourdomain.com at this service, then set site in docs/astro.config.mjs to that URL.
Local preview:
cd docs && npm run dev# http://localhost:4321Or from the repo root: npm run docs:dev
4. Local development (app)
Section titled “4. Local development (app)”# Terminal 1 — APIcd server && npm run dev
# Terminal 2 — frontend (or from repo root)npm run devRoot .env:
VITE_API_URL=/apiVITE_USE_MOCK_DATA=falseVITE_SUPABASE_URL=...VITE_SUPABASE_ANON_KEY=...server/.env — database and server secrets as before.
Local CORS always allows http://localhost:5173 — no CORS_ORIGIN needed locally.
5. Checklist after first deploy
Section titled “5. Checklist after first deploy”-
GET https://<api>/health→{ "ok": true } - Frontend loads, catalog fetches cards
- Account menu shows Sign in when logged out
- Login / sign-up works
- Card creator chat streams responses
- Avatar upload works (Supabase
avatarsbucket +AVATAR_IMAGE_BUCKET) - Docs site loads at the Docs service URL
Troubleshooting
Section titled “Troubleshooting”Sign in missing — VITE_USE_MOCK_DATA must be false and VITE_API_URL must point to the live API URL (rebuild frontend after changing).
CORS errors — CORS_ORIGIN on the API must exactly match the frontend origin (scheme + host, no trailing slash).
DB connection errors — use Supabase pooler URLs from server/.env.example, not the direct db.*.supabase.co host.
Stale frontend env — Railway rebuilds are required after changing any VITE_* variable.
Docs build fails / wrong app built — Docs service Root Directory must be docs, otherwise Railway may build the Vite frontend instead.