Skip to content

Build & Runtime Failures with Next.js Standalone, Turbopack, and Docker #235

@evocation01

Description

@evocation01

Title: [Bug] Comprehensive Build & Runtime Failures with Next.js Standalone, Turbopack, and Docker

Environment:

  • Framework: Next.js 15.5.2 (App Router, Turbopack)
  • Library: intlayer / next-intlayer v5.8.1
  • Build Environment: Docker (node:lts-bullseye) on Google Cloud Build
  • Local Environment: Windows (PowerShell)
  • Package Manager: pnpm v10.15.1

Problem Summary:
We've been working to create a reliable production build using output: 'standalone' and Turbopack. This process uncovered a series of cascading issues related to Turbopack compatibility, build-time race conditions in Docker, and runtime errors with the standalone server. While we have found workarounds for all issues, the number of steps required suggests potential improvements for the library.


Detailed Breakdown of Issues and Solutions

Part 1: Turbopack Compatibility (next build --turbo)

The initial build attempts failed because several dependencies tried to import Node.js built-in modules on the client.

  • Issue: Module not found errors for fs, module, child_process, and esbuild binaries/readmes.
  • Solution: We had to manually configure next.config.mjs to stub out these modules for the client using turbopack.resolveAlias. This involved:
    1. Installing and aliasing browser-compatible polyfills (path-browserify, crypto-browserify).
    2. Creating an empty stub file (stubs/empty.js) and aliasing server-only modules (fs, child_process, module, esbuild) to it.
    3. Updating the stub file to export dummy implementations for required named exports like existsSync and buildSync to satisfy imports from @intlayer/config.

Part 2: Build-Time "Dictionary not found" Error in Docker

After fixing the Turbopack issues, the build would fail during static page generation with Error: Dictionary <name> not found.

  • Issue: The next build command could not find the dictionary files, even though they were being generated in the preceding step.
  • Investigation:
    • We added ls -lR .intlayer to the Dockerfile, which definitively confirmed that pnpm exec intlayer build was successfully creating all dictionary files in the correct .intlayer/dictionary directory before the next build command began.
    • This pointed to a race condition or a filesystem caching issue within the Docker build process, where the Next.js build was not seeing the newly created files.
  • Solution: We restructured the entire build process to be more robust:
    1. Moved the pnpm exec intlayer build command to a postinstall script in package.json.
    2. This ensures the .intlayer directory is reliably generated and present on the filesystem immediately after dependencies are installed, creating a stable state before any build commands are run.

Part 3: Runtime Errors with Standalone Server (pnpm start)

After a successful build, the local standalone server would crash or fail to serve assets.

  • Issues:
    1. TypeError: Cannot read properties of undefined (reading 'match') in the intlayerMiddleware.
    2. 404 Not Found for static assets in the public folder (e.g., logos).
  • Investigation: The output: 'standalone' build was not automatically including the necessary runtime files in the final .next/standalone directory. Specifically, it was missing:
    • intlayer.config.ts (causing the middleware crash).
    • The generated .intlayer directory (would cause dictionary errors).
    • The public folder.
  • Solution: We created a postbuild script in package.json that uses copyfiles to manually copy all three of these required assets into the .next/standalone directory after the next build completes.

Part 4: @intlayer/swc Failure on Windows

  • Issue: Enabling the @intlayer/swc plugin causes a build failure on Windows with a Module not found error for the .wasm file, citing "windows imports are not implemented yet".
  • Workaround: The plugin had to be completely removed from the project to allow local development on Windows to succeed.

Final Working Configuration

For reference, here are the key parts of our final, working configuration:

package.json scripts:

"scripts": {
  "build": "pnpm exec intlayer build && next build --turbo",
  "postbuild": "pnpm exec copyfiles -u 1 \"public/**/*\" \".intlayer/**/*\" intlayer.config.ts .next/standalone",
  "start": "cross-env PORT=3001 node .next/standalone/server.js",
  "postinstall": "pnpm exec intlayer build"
}

Dockerfile:

# Use the official Node.js LTS image as a base
FROM node:lts-bullseye AS base

# Set up PNPM and Corepack environment
ENV PNPM_HOME="/pnpm"
ENV PATH="$PNPM_HOME:$PATH"
RUN corepack enable

# Create a non-root user for security best practices
RUN addgroup --system --gid 1001 nodejs
RUN adduser --system --uid 1001 nextjs

WORKDIR /app

# -----------------
# DEPS
# -----------------
# Install dependencies in a separate layer to leverage Docker's caching.
FROM base AS deps
WORKDIR /app

COPY package.json pnpm-lock.yaml ./
# This command now also runs 'pnpm exec intlayer build' automatically via your postinstall script
RUN pnpm install --frozen-lockfile

# -----------------
# BUILDER
# -----------------
# Build the application.
FROM base AS builder
WORKDIR /app

COPY --from=deps /app/node_modules ./node_modules
COPY . .

ARG NEXT_PUBLIC_API_URL
ENV NEXT_PUBLIC_API_URL=${NEXT_PUBLIC_API_URL}

# This single command runs 'next build' and then your 'postbuild' script to copy all necessary files.
RUN pnpm build

# -----------------
# RUNNER
# -----------------
# Create the final, lightweight production image.
FROM base AS runner
WORKDIR /app

ENV NODE_ENV production
ENV NEXT_TELEMETRY_DISABLED 1

# Copy the prepared standalone output (which now includes public, .intlayer, etc.)
COPY --from=builder --chown=nextjs:nodejs /app/.next/standalone ./
# Copy the necessary client-side static assets
COPY --from=builder --chown=nextjs:nodejs /app/.next/static ./.next/static

# Run as the non-root user
USER nextjs
EXPOSE 3000
ENV PORT 3000

# Start the server
CMD ["node", "server.js"]
  • This setup is now stable, but it required significant workarounds. Hopefully, this detailed report can help improve the out-of-the-box experience for other users in similar environments. Thank you!

Metadata

Metadata

Assignees

Labels

bugSomething isn't workingbundlingRelated to the way intlayer is bundled on application (Next.js, Vite.js, Nuxt.js, etc)

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions