Railway
Deploy Swarm 357 API and landing on Railway.
Deploy Swarm 357 on Railway
Backend service
- New service → connect the TechTideOhio/swarm-357 repository → Root Directory:
.(the repository root). - Builder: Dockerfile (see railway.toml).
- Variables (minimum):
ANTHROPIC_API_KEY- for live agents (omit only if you want stub responses).SWARM_API_KEY- long random string; required for securing POST routes in production.PORT- Railway sets automatically; railway.toml uses$PORTinstartCommand.
Optional:
ALLOWED_ORIGINS- comma-separated origins for the Next.js marketing site.SUPABASE_URL/SUPABASE_SERVICE_KEY- persistence; run database/migrations/001_initial.sql in Supabase SQL first.SWARM_MAX_RUN_BUDGET_USD- hard cap for JSONbudget_usd(default500).SWARM_RATE_LIMIT_PER_MINUTE- per-IP limit (default120; set0to disable).
Custom domain
- Railway → service → Settings → Networking → generate domain or attach custom domain.
- Add the same origin to
ALLOWED_ORIGINS. - Redeploy so CORS includes the new host.
Smoke checks
Public production URLs (Railway project swarm-357):
| Surface | Service | Public URL |
|---|---|---|
| Frontend | frontend | https://swarm357fe.up.railway.app |
| Backend | backend | https://swarm357be.up.railway.app |
curl -sSf "https://swarm357be.up.railway.app/api/health"
# Expect: "status":"ok","agents":357,...See VERIFY.md for the full scorecard.
Landing (Next.js) on Railway (optional second service)
- Deploy from
TechTideOhio/swarm-357-site(Railway root = repo root). - Builder: Dockerfile, not Nixpacks. Nixpacks injects every service variable into the build environment, which would place the write key in build arguments and in the image configuration. The Dockerfile declares only the public
NEXT_PUBLIC_*arguments. - Env:
NEXT_PUBLIC_API_URL=https://swarm357be.up.railway.app - Backend CORS:
ALLOWED_ORIGINS=https://swarm357.techtideai.io,https://swarm357fe.up.railway.app(plus any other custom domains). - Demo writes: the site proxies POST
/api/swarm/runthrough its own same-origin route and attaches the write key server side. SetSWARM_API_KEYon the site service as a runtime-only variable with the same value as the API. Never expose a write key asNEXT_PUBLIC_*; anything with that prefix is inlined into the client bundle at build time.
Build-time versus runtime variables
| Variable | Where it is needed | Notes |
|---|---|---|
NEXT_PUBLIC_API_URL | Build and runtime | Inlined into the client bundle. Public value only. |
NEXT_PUBLIC_SITE_URL | Build and runtime | Inlined into the client bundle. Public value only. |
SWARM_API_KEY | Runtime only | Read by the server route on each request. It is never declared as a build ARG, so it cannot reach the bundler, the build logs, or the image configuration. |
Go-live checklist
- Root directory is
., the root of the swarm-357 repository, which is where the Dockerfile lives. - Builder = Dockerfile; confirm railway.toml
startCommanduses$PORT(Railway injects it). - Set
ANTHROPIC_API_KEY,SWARM_API_KEY, and optionalALLOWED_ORIGINSfor your marketing origin. - Run
database/migrations/001_initial.sqlin Supabase before settingSUPABASE_*env vars (see DATA_PLANE.md). - Deploy, then
curl /api/health- expect"agents":357and"status":"ok".
Troubleshooting
| Symptom | Likely cause | Fix |
|---|---|---|
Build: package.json not found / Railpack errors | Wrong Root Directory or builder | Set Root Directory to . and the builder to Dockerfile. |
| Service starts then 502 / connection refused | PORT not bound | Keep uvicorn ... --port $PORT in Railway start command; do not hardcode 8000 in production. |
/api/health shows agents: 0 | Config path missing in image | Ensure Dockerfile copies config/ and templates/; or set SWARM_CONFIG_PATH. |
| POST returns 401 | SWARM_API_KEY set | Send header X-SWARM-API-KEY with the same secret. |
| POST returns 429 | Rate limit | Raise SWARM_RATE_LIMIT_PER_MINUTE or set to 0 for internal testing only. |
| CORS blocked from browser | Origin not allowed | Add site URL to ALLOWED_ORIGINS and redeploy. |