Shipping TanStack Start and Bun to Railway
Source: Dev.to
Problem
Railway’s Nixpacks autobuild detects Bun projects, but it can’t handle the sequence this site needs:
prisma generateat build time- Vite + the TanStack Start plugin
- a custom
server.tsentry point prisma migrate deployon boot
Teaching Nixpacks to orchestrate all of that is more complex than using a straightforward multi‑stage Dockerfile.
Solution
A four‑stage Dockerfile solves the problem:
- Two parallel
bun installstages – one installs all dependencies, the other installs only production‑only packages. - Build stage – runs
prisma generateagainst a dummyDATABASE_URL. - Lean runtime stage – layers the generated Prisma client on top of the production
node_modules. - Entrypoint – runs migrations before
exec‑ing into Bun so the container’s PID 1 shuts down cleanly on Railway’sSIGTERM.
Dockerfile recipe (summary)
- Stage 1:
FROM bun:latest AS deps-full→bun install(all deps). - Stage 2:
FROM bun:latest AS deps-prod→bun install --production. - Stage 3:
FROM deps-full AS build→ set a dummyDATABASE_URL, runprisma generate, then build the Vite/TanStack Start bundle. - Stage 4:
FROM bun:slim AS runtime→ copynode_modulesfromdeps-prodand the generated Prisma client frombuild, addserver.ts, and set the entrypoint that:- Executes
prisma migrate deploy exec bun server.ts
- Executes
Railway‑side details
- Postgres reference variable: Wire the Railway‑provided Postgres URL to the app’s
DATABASE_URLenvironment variable. - Binding address: Explicitly bind the server to
0.0.0.0so Railway can route traffic correctly. - COPY order: Ensure the generated Prisma client is copied after the production
node_modulesso the runtime sees the correct client version.
Originally published at andreasbergstrom.dev — read the full post there.