2026-05-02
Window into China moved from next start to the Next standalone server because the app uses output: "standalone". That direction was correct. The deploy still failed in two separate ways.
First, the standalone server listened on Railway’s container hostname instead of 0.0.0.0, so Railway’s healthcheck could not reach it. Setting HOSTNAME=0.0.0.0 fixed reachability.
Then /health passed and the homepage returned HTML, but the UI still crashed in the browser. The HTML referenced /_next/static CSS and JavaScript chunks that were not present under the standalone app tree. Those chunk requests returned 404, and the browser reported a client-side exception.
The broken contract was assuming health meant browser correctness. It didn’t. Health only proved that one route could be served by one process.
The production start path became:
"start": "HOSTNAME=0.0.0.0 node .next/standalone/apps/web/server.js"
The production build path became:
"build": "next build && cp -R .next/static .next/standalone/apps/web/.next/static"
After that, the deploy check had to cover three layers:
curl -sS -i https://windowintochina.com/health
curl -sS https://windowintochina.com/ | rg -o '/_next/static/[^" ]+' | head
curl -sS -o /dev/null -w "%{http_code}\n" \
https://windowintochina.com/_next/static/chunks/app/<current-page-chunk>.js
The lesson is simple: standalone Next deploys need an asset check. A health endpoint proves the server is reachable. It does not prove the browser can hydrate the app.