Problem: Railway deploys Next.js app but always serves old cached build (never shows new code)
ricoafrei-visuals
FREEOP

6 days ago

Setup:

Next.js 15 App Router, deployed on Railway via Dockerfile

Multi-stage Docker build (deps → builder → runner)

Railway uses Docker BuildKit with shared remote cache

Symptom:

git push → Railway builds → build logs confirm correct source files

But deployed site always shows old code , never changes

Local build produces completely different hash with correct code

Root cause identified:

Railway's BuildKit shared remote cache caches the runner stage's COPY --from=builder instructions by instruction text, NOT by the actual content hash of what the builder produces. So even when the builder stage rebuilds correctly with new code, the runner stage COPY layers are served from cache containing old static files.

What didn't work:

Changing RUN instruction text

Adding ARG GIT_SHA

Pinning Node.js version (node:20-alpine → node:20.18.0-alpine)

Adding .dockerignore

Adding rm -rf .next before build

$10 Bounty

3 Replies

Status changed to Open Railway 6 days ago


Try adding NO_CACHE=1 and redeploy the service. Also try accessing the site from an incognito window as well.


a29-dev
HOBBY

6 days ago

Have you reviewed logs to see what old code is being referenced? As mentioned above, ingotnito window or trying for another browser may fix. Also, if you're running through a service like cloudflare, make sure you are not caching the page or temporarily turn off caching for testing


furpan
PRO

6 days ago

On top of using NO_CACHE=1, extending on if the case of Cloudflare is proxying the website:

  1. In Cloudflares dashboard, temporarily enable development mode
  2. In your browsers devtools, ensure cache is disabled under Network, clean possible Workers/Storage under Application, alternatively use Incognito mode to ensure local no cache is set
  3. Ensure to redeploy the service
  4. Test below Dockerfile to ensure runtime only gets explicit built artifacts by avoiding ambiguous copies to keep layers predictable between builder and runner.

Make sure next.config.{js,ts,mjs} has output: 'standalone'


# syntax=docker/dockerfile:1

FROM node:20-alpine AS deps
WORKDIR /app
COPY package.json package-lock.json* pnpm-lock.yaml* yarn.lock* ./

RUN npm ci

FROM node:20-alpine AS builder
WORKDIR /app

COPY --from=deps /app/node_modules ./node_modules
COPY . .
ENV NEXT_TELEMETRY_DISABLED=1
RUN npm run build

FROM node:20-alpine AS runner
WORKDIR /app

ENV NODE_ENV=production
ENV NEXT_TELEMETRY_DISABLED=1

# Use standalone output to keep runtime deterministic
COPY --from=builder /app/.next/standalone ./
COPY --from=builder /app/.next/static ./.next/static
COPY --from=builder /app/public ./public

EXPOSE 3000
CMD ["node", "server.js"]

Welcome!

Sign in to your Railway account to join the conversation.

Loading...