Skip to content

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.

ServiceBuilderBuildStart
api./Dockerfileprisma generate + tsc → apps/api/distnpx prisma migrate deploy && node apps/api/dist/index.js
webapps/web/nixpacks.tomlnpm run build -w @cropto/webnpm run preview -w @cropto/web
adminapps/admin/nixpacks.tomlnpm run build -w @cropto/adminnpm run preview -w @cropto/admin
marketingDockerfile.marketing (set RAILWAY_DOCKERFILE_PATH=Dockerfile.marketing)next buildnext start

Two gotchas that have shipped as incidents

  1. Marketing must set RAILWAY_DOCKERFILE_PATH=Dockerfile.marketing. Otherwise Railway builds it with the API-only root Dockerfile, next start finds no .next build, and the container crash-loops.
  2. npm 11 is required. Railway's default builders ship npm 10, which can't reliably npm ci the rolldown-vite lockfile. Every builder here upgrades to npm@11.6.2 before npm ci (the Dockerfiles and every nixpacks.toml do 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.json first (npm refuses to npm ci if a declared workspace manifest is missing) but only copies source for the API and the shared packages it imports — apps/web, apps/admin, apps/mobile source never enters the image.
  • It installs the native toolchain (python3 make g++) for bcrypt + Prisma engines.
  • It smoke-tests require('sharp') right after npm ci so a pruned platform binary fails the build, not production boot.
  • The CMD runs 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") and directUrl = env("DIRECT_URL").
  • DIRECT_URL must be set in Railway to a non-pooled (direct) Postgres connection so migrate deploy bypasses the PgBouncer/Supabase transaction pooler — the pooler closes the shadow/DDL connection mid-migration.
  • DATABASE_URL stays 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.

bash
# 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) and VITE_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.

Internal technical documentation — Cropto