How to Fix: Parallel Routes not working on Cloud Run in Next.js 15

6 min read

Parallel Routes can appear perfectly fine in local development, then break only after deploying to Cloud Run with a Next.js 15 standalone server. The usual symptom is that a slot such as @sidebar or @modal stops rendering correctly, returns the wrong content, or fails entirely when the app runs behind the production server entrypoint.

Understanding the Root Cause

This issue is usually not caused by the Parallel Routes feature itself. The real problem is the interaction between Next.js 15, the standalone output server, and how the application is started inside Cloud Run.

In development, Next.js runs with its full framework server behavior, file tracing, and route resolution logic available directly from the project structure. In production, especially with output: ‘standalone’, the app is reduced to a minimal server bundle. That bundle depends on the correct runtime layout, traced files, and the right startup command.

When Parallel Routes fail only in Cloud Run, the common root causes are:

  • The standalone server is started incorrectly, so Next.js cannot resolve the app exactly as it does in development.
  • Required app files are missing from the container image, especially static assets or traced runtime dependencies.
  • The container copies the wrong folders, causing route segments and slot-related resources to be incomplete at runtime.
  • Custom server assumptions conflict with the generated server.js from the standalone build.

For Next.js standalone deployments, the generated server under .next/standalone must be treated as the source of truth. If Cloud Run starts a different server process, or if the final image does not include both .next/static and public in the expected places, route rendering can become inconsistent. Parallel Routes are particularly sensitive because they rely on correct app tree resolution and server-side rendering behavior for multiple slots at once.

In short: the bug happens because the Cloud Run production container is not faithfully reproducing the runtime environment that the Next.js standalone build expects.

Step-by-Step Solution

The most reliable fix is to build and run the app exactly the way Next.js standalone output expects.

1. Enable standalone output in Next.js

In next.config.js, make sure standalone output is enabled:

/** @type {import('next').NextConfig} */
const nextConfig = {
  output: 'standalone',
}

module.exports = nextConfig

This tells Next.js to generate a self-contained production server in .next/standalone.

2. Build the application normally

npm install
npm run build

After the build, confirm these paths exist:

.next/standalone
.next/static
public

If any of them are missing from the final container, production routing can break.

3. Use the generated standalone server, not a custom startup path

Start the app with the generated server.js:

node .next/standalone/server.js

If you are using Docker for Cloud Run, your container should ultimately run that file, not next start, and not a separate custom server unless you fully know how it interacts with the standalone bundle.

4. Copy the correct files into the runtime image

A working Dockerfile for Cloud Run typically looks like this:

FROM node:20-alpine AS deps
WORKDIR /app
COPY package.json package-lock.json ./
RUN npm ci

FROM node:20-alpine AS builder
WORKDIR /app
COPY --from=deps /app/node_modules ./node_modules
COPY . .
RUN npm run build

FROM node:20-alpine AS runner
WORKDIR /app
ENV NODE_ENV=production
ENV PORT=8080

COPY --from=builder /app/.next/standalone ./
COPY --from=builder /app/.next/static ./.next/static
COPY --from=builder /app/public ./public

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

This is the key deployment pattern. Notice what is happening:

  • .next/standalone is copied into the container root.
  • .next/static is copied to ./.next/static.
  • public is copied to ./public.
  • The process starts with node server.js because the standalone folder contents were copied directly into the working directory.

If instead you copy only .next/standalone and forget static or public assets, you can get broken route rendering, missing chunks, and behavior that looks like a Parallel Routes bug.

5. Make sure Cloud Run uses the expected port

Cloud Run injects the port through the PORT environment variable. The standalone server supports this, but your container must expose and use it properly:

ENV PORT=8080
EXPOSE 8080

If the app binds to a different port, Cloud Run may report startup failures or unstable behavior that masks the routing issue.

6. Test the production server locally before deploying

Do not rely only on next dev. Reproduce the real production runtime locally:

npm run build
node .next/standalone/server.js

Then open the route that uses your Parallel Routes setup and verify every slot renders correctly.

7. If using route groups and slots, verify the app structure

A correct structure might look like this:

app/
  dashboard/
    layout.js
    page.js
    @analytics/
      page.js
    @team/
      page.js

Also ensure your layout accepts the named slot props:

export default function Layout({ children, analytics, team }) {
  return (
    <>
      {children}
      {analytics}
      {team}
    </>
  )
}

If the local standalone server works but Cloud Run still fails, the problem is almost always the container file layout rather than the route definition.

8. Deploy to Cloud Run after validating the image

Build and deploy the container only after confirming the local container behaves the same way as the standalone server:

docker build -t next-parallel-routes-fix .
docker run -p 8080:8080 next-parallel-routes-fix

Once that works, deploy to Cloud Run using your normal pipeline.

If you need official deployment references, use the Next.js deployment documentation and the Cloud Run documentation.

Common Edge Cases

  • Missing default.js for unmatched slots: If a slot can be absent during navigation, you may need a default.js file inside the slot directory. Without it, production rendering may fail in cases that seem random.
  • Case-sensitive filesystem differences: Local macOS development may tolerate casing mistakes that fail in Linux containers on Cloud Run.
  • Dynamic rendering assumptions: If slot content depends on headers, cookies, or request-time logic, production behavior may differ from static expectations.
  • Custom rewrites or middleware: Middleware can change request flow and make it look like Parallel Routes are broken when the actual issue is request interception.
  • Incomplete multi-stage Docker copy: A common mistake is copying only .next/standalone and forgetting .next/static or public.
  • Wrong working directory: If server.js starts from an unexpected directory, relative asset paths and traced dependencies may not resolve correctly.

FAQ

Why do Parallel Routes work in next dev but fail on Cloud Run?

Because next dev does not use the same production runtime as the standalone server. In Cloud Run, the app depends on the exact output structure generated during build, and missing or misplaced files can break route resolution.

Should I use next start with standalone output?

No. If you built with output: ‘standalone’, you should run the generated server, typically with node .next/standalone/server.js or node server.js if that folder was copied into the container root.

Do I always need to copy .next/static and public?

Yes, in most production container setups. The standalone bundle does not mean every asset is embedded into server.js. Static files still need to exist in the runtime image in the expected locations.

The practical fix for this GitHub issue is straightforward: build with standalone output, copy the full runtime artifacts, and start the generated server exactly as intended. Once the Cloud Run container mirrors the expected Next.js production layout, Parallel Routes behave the same way they do in a correct local production test.

Leave a Reply

Your email address will not be published. Required fields are marked *