Deployment (Railway)
Cropto deploys to Railway. Each app is its own Railway service in one project; configuration is entirely via environment variables (never hard-coded). This page covers the build topology; see External Services for provisioning the managed dependencies (Supabase, Upstash, Cloudflare R2, MSG91, …).
Service topology
How each service builds
Railway auto-detects the repo-root ./Dockerfile for any service that builds from the repo root. Two services need to opt out of that with an explicit config.
| Service | Builder | Build | Start |
|---|---|---|---|
| api | ./Dockerfile | prisma generate + tsc → apps/api/dist | npx prisma migrate deploy && node apps/api/dist/index.js |
| web | apps/web/nixpacks.toml | npm run build -w @cropto/web | npm run preview -w @cropto/web |
| admin | apps/admin/nixpacks.toml | npm run build -w @cropto/admin | npm run preview -w @cropto/admin |
| marketing | Dockerfile.marketing (set RAILWAY_DOCKERFILE_PATH=Dockerfile.marketing) | next build | next start |
Two gotchas that have shipped as incidents
- Marketing must set
RAILWAY_DOCKERFILE_PATH=Dockerfile.marketing. Otherwise Railway builds it with the API-only rootDockerfile,next startfinds no.nextbuild, and the container crash-loops. - npm 11 is required. Railway's default builders ship npm 10, which can't reliably
npm cithe rolldown-vite lockfile. Every builder here upgrades tonpm@11.6.2beforenpm ci(the Dockerfiles and everynixpacks.tomldo this).
Why a Dockerfile for the API
The API image is deliberately explicit rather than relying on Railpack/Nixpacks auto-detection on a monorepo:
- It copies every workspace's
package.jsonfirst (npm refuses tonpm ciif a declared workspace manifest is missing) but only copies source for the API and the shared packages it imports —apps/web,apps/admin,apps/mobilesource never enters the image. - It installs the native toolchain (
python3 make g++) for bcrypt + Prisma engines. - It smoke-tests
require('sharp')right afternpm ciso a pruned platform binary fails the build, not production boot. - The
CMDruns migrations then starts:npx prisma migrate deploy && node apps/api/dist/index.js.
Database migrations on deploy
The API container runs prisma migrate deploy on start.
- The Prisma schema declares both
url = env("DATABASE_URL")anddirectUrl = env("DIRECT_URL"). DIRECT_URLmust be set in Railway to a non-pooled (direct) Postgres connection somigrate deploybypasses the PgBouncer/Supabase transaction pooler — the pooler closes the shadow/DDL connection mid-migration.DATABASE_URLstays the pooled connection for normal request traffic.
Frontend env is baked at build time
VITE_* variables (VITE_API_URL, VITE_SENTRY_DSN, VITE_ENV, VITE_RELEASE) are compiled into the web/admin bundles at build time from each Railway service's env.
Admin panel data environment
Because VITE_API_URL is baked in, the admin panel's data environment is whatever DB the API it targets uses. Point admin at the correct API service per environment — a staging admin build must not carry a production API URL.
Lockfile hygiene before pushing
Railway builds with npm ci, which requires package-lock.json to be in sync.
# after any dependency change
npm install --package-lock-only # re-sync for ALL platforms
npm ci --dry-run # validate it will install cleanly
grep sharp-linuxmusl-x64 package-lock.json # must be present (Alpine binary)Running plain npm install on Windows over an existing node_modules prunes other platforms' optional native binaries from the lockfile — the exact cause of the "Could not load the sharp module … linuxmusl-x64" boot crash. Always re-sync with --package-lock-only and verify the musl binary is present.
Health, monitoring & rollback
- Health check: point Railway's healthcheck (and Better Uptime) at
GET /api/v1/health— it deep-probes Postgres + Redis and returns 503 when a configured dependency is down. - Job dashboard: Bull Board at
/admin/queues(admin auth). - Sentry release: set
SENTRY_RELEASE(API) andVITE_RELEASE(web/admin) to the git SHA so error reports map to the deployed build. - Rollback: redeploy the previous Railway deployment; migrations are forward-only, so avoid destructive migrations without a tested down-path.
See the Security Checklist and Pre-launch Ops Checklist before a production deploy.
