nx: Next.js 12 Output File Tracing Fails to Build

Current Behavior

  • create a new nx workspace w/ a next.js app
  • turn on next.js output file tracing
  • run nx build --prod
  • build fails w/ the error message saying dist/apps/haha/.next/standalone/apps/haha/server.js doesn’t exist (it doesn’t)

Expected Behavior

The standalone folder should contain a server.js file.

Steps to Reproduce

I’ve created a repro repo here. Clone & run yarn nx build --prod to see error message.

Failure Logs

ENOENT: no such file or directory, open '/nx_next_standalone_repro/dist/apps/haha/.next/standalone/apps/haha/server.js'

Environment

Output of next info

    Operating System:
      Platform: darwin
      Arch: arm64
      Version: Darwin Kernel Version 21.2.0: Sun Nov 28 20:29:10 PST 2021; root:xnu-8019.61.5~1/RELEASE_ARM64_T8101
    Binaries:
      Node: 16.13.1
      npm: 8.1.2
      Yarn: 1.22.17
      pnpm: N/A
    Relevant packages:
      next: 12.1.0
      react: 17.0.2
      react-dom: 17.0.2

Output of nx report

   Node : 16.13.1
   OS   : darwin arm64
   yarn : 1.22.17
   
   nx : 13.8.2
   @nrwl/angular : undefined
   @nrwl/cli : 13.8.2
   @nrwl/cypress : 13.8.2
   @nrwl/detox : undefined
   @nrwl/devkit : 13.8.2
   @nrwl/eslint-plugin-nx : 13.8.2
   @nrwl/express : undefined
   @nrwl/jest : 13.8.2
   @nrwl/js : 13.8.2
   @nrwl/linter : 13.8.2
   @nrwl/nest : undefined
   @nrwl/next : 13.8.2
   @nrwl/node : undefined
   @nrwl/nx-cloud : undefined
   @nrwl/react : 13.8.2
   @nrwl/react-native : undefined
   @nrwl/schematics : undefined
   @nrwl/storybook : 13.8.2
   @nrwl/tao : 13.8.2
   @nrwl/web : 13.8.2
   @nrwl/workspace : 13.8.2
   typescript : 4.5.5
   rxjs : 6.6.7
   ---------------------------------------
   Community plugins:

About this issue

  • Original URL
  • State: closed
  • Created 2 years ago
  • Reactions: 38
  • Comments: 70 (7 by maintainers)

Commits related to this issue

Most upvoted comments

Thanks to the comments above here’s what worked for me on “nx”: “14.8.3” and “next”: “12.3.1”.

As Yizhachok said I had to add the following, although I had to add ‘output’ outside of experimental. Also needed to add const path = require('path'); to the top of the file.

  output: 'standalone',
  experimental: {
    // this includes files from the monorepo base two directories up
    outputFileTracingRoot: path.join(__dirname, '../../'),
  },

then in pages/index I added

import path from 'path';
path.resolve('./next.config.js');

This wasn’t working for me initially so the final thing I had to do was delete my dist folder that I had generated previously and then run the build command with the --skip-nx-cache argument so: nx build shop-next --skip-nx-cache. This finally worked and I was able to run the server file in dist/apps/my-app/.next/standalone/apps/my-app/server.js

I think a more proper solution by NX is really necessary as without these workarounds the docker image is 1.5 GB for me.

Just don’t ask how I investigated this and how much time I spend to find out how to fix this. ⚠️ WARNING ⚠️ Next text for people who believe in magic, skeptics, please stop read this text right now. I warned you.

Steps to get working repo for standalone Next.js server with build using NX CLI.

Init example workspace (or use your own)

  1. Run npx create-nx-workspace@latest --preset=next (tested on v14.1.9 workspace)
  2. Answers to questions: ✔ Workspace name (e.g., org name): next-test ✔ Application name: site ✔ Default stylesheet format: scss
  3. Go to next.config.js file and add to config:
experimental: {
    outputStandalone: true,
    outputFileTracingRoot: path.join(__dirname, "../../")
}

Preparation finished, now let’s make real magic

Go to pages/index.tsx (or any page file) and add this lines (don’t ask why, this is MAGIC):

import path from 'path';
path.resolve('./next.config.js');

Really can be any js file that can be resolved from current location (even empty). This code enough to insert only once.

Show magic to the world

Run npm run build. That’s it! No errors!

Bonus

This is a VERY SIMPLE Docker config that show how to work with this build.

FROM node:16-alpine AS builder
RUN apk add --no-cache libc6-compat python3 make g++
WORKDIR /build
COPY . .
RUN npm i && npm run build


FROM node:16-alpine AS runner
ARG APP=site
WORKDIR /app

RUN addgroup --system --gid 1001 nodejs
RUN adduser --system --uid 1001 nextjs

COPY --from=builder --chown=nextjs:nodejs /build/dist/apps/${APP}/.next/standalone ./
COPY --from=builder --chown=nextjs:nodejs /build/dist/apps/${APP}/.next/static ./dist/apps/${APP}/.next/static
COPY --from=builder --chown=nextjs:nodejs /build/dist/apps/${APP}/public ./apps/${APP}/public

USER nextjs

EXPOSE 3000

ENV NODE_ENV=production
ENV APP_PATH=apps/${APP}/server.js
ENV PORT 3000

CMD node $APP_PATH

Thanks to @bboyz269 for working example, from his repository I found the issue why build didn’t work on clean project. His repo works because of next-i18next lib code.

Thanks everyone for your comments on this issue!

Wrapping up here what worked for me, as it took me a few hours to get it done. Make sure to change your-app to your app/project’s name. The steps below consider that your workspace’s layout is the default “empty” with apps/ and libs/ folders in the workspace root directory.

A few considerations:

  • Good Not having to download all the dependencies inside Docker build stages, particularly when your app depends on publishable libraries that are mantained inside the workspace and may have been updated but not published yet.
  • Maybe not good Not sure how the “ideal” workflow for a CI would be or how it would fit in setups like the ones described in this article from Creotip and this other one on Medium.

Build and deploy a Next.js app in standalone mode from an Nx workspace with Docker

See more

package.json — for reference

"next": "12.1.6",
"@nrwl/next": "14.4.0"

workspace.json — for reference

{
  "$schema": "./node_modules/nx/schemas/workspace-schema.json",
  "version": 2,
  "projects": {
    "your-app": "apps/your-app",
  }
}

app/your-app/project.json — for reference only, generated with yarn nx g @nrwl/next:application your-app

{
  "$schema": "../../node_modules/nx/schemas/project-schema.json",
  "sourceRoot": "apps/your-app",
  "projectType": "application",
  "targets": {
    "build": {
      "executor": "@nrwl/next:build",
      "outputs": ["{options.outputPath}"],
      "defaultConfiguration": "production",
      "options": {
        "root": "apps/your-app",
        "outputPath": "dist/apps/your-app"
      },
      "configurations": {
        "development": {},
        "production": {}
      }
    },
  }
}
  1. To ensure it builds with outputStandalone: true, add path.resolve('./next.config.js') to apps/your-app/pages/index.tsx as suggested by @Yizhachok
import path from 'path' // ← add this line
import { useTheme } from '@my-lib/themes'

path.resolve('./next.config.js') // ← and this line

const Home = () => {
  const { theme } = useTheme()
...
  1. Update your apps/your-app/next.config.js as suggested by @LudovicPelleDMS
/**
 * @type {import('@nrwl/next/plugins/with-nx').WithNxOptions}
 **/
const nextConfig = {
  nx: {
    svgr: false,
  },
  // add the lines below ↓
  experimental: {
    outputStandalone: true,
    outputFileTracingRoot: path.join(__dirname, '../../'),
  }
}
  1. Run yarn nx build your-app

  2. Create a Dockerfile in the workspace root directory (I haven’t managed to get it working anywhere else yet) as suggested by @always-maap

FROM node:16-alpine

ENV NODE_ENV=production

COPY dist/apps/your-app/.next/standalone app/
COPY dist/apps/your-app/public/ app/apps/your-app/public/
COPY dist/apps/your-app/.next/static app/dist/apps/your-app/.next/static/

WORKDIR /app

EXPOSE 3000
ENV PORT 3000

CMD ["node", "apps/your-app/server.js"]
  1. Run docker buildx build -t ghcr.io/your-github-username/your-app .

  2. Run docker run -p 3000:3000 ghcr.io/your-github-username/your-app

  3. Open http://localhost:3000 and check if it works.

I post mine maybe it helps

2022-03-25-190339_1920x1080_scrot

experimental: {
    outputStandalone: true,
    outputFileTracingRoot: path.join(__dirname, '../../'),
  },
# Dockerfile/zoomit-editorial/Dockerfile


FROM docker-repo.url.com/node:v-alpine

COPY dist/apps/zoomit-editorial/.next/standalone app/
# TODO: serve with CDN
COPY dist/apps/zoomit-editorial/public/ app/apps/zoomit-editorial/public/
COPY dist/apps/zoomit-editorial/.next/static app/dist/apps/zoomit-editorial/.next/static/

WORKDIR /app

ENV NODE_ENV production
ENV PORT 4200

CMD ["node", "apps/zoomit-editorial/server.js"]

For those running next.js in docker this is a groundbreaking feature so I would be very happy to have this issue resolved. The docker image will reduce in size by a lot.

In our case it will also reduce the build time very much as we are doing what we can to have the docker image as small as possible, and in order to achieve that we have to do npm install twice, first for the build, and then again for the generated package.json file for this app.

Changing the outputPath in production configuration works for me:

"configurations": { "production": { "outputPath": "apps/<my-app>/dist" } } Or

npx nx run my-app:build --configuration=production --outputPath="apps/<my-app>/dist"

Preparation finished, now let’s make real magic

Go to pages/index.tsx (or any page file) and add this lines (don’t ask why, this is MAGIC):

import path from 'path';
path.resolve('./next.config.js');

Can confirm this fixed everything for me 😂

The steps by @moatorres work great except I had to change:

const nextConfig = {
  nx: {
    svgr: false,
  },
  // add the lines below ↓
  experimental: {
    outputStandalone: true,
    outputFileTracingRoot: path.join(__dirname, '../../'),
  }
}

to:

const nextConfig = {
  nx: {
    svgr: false,
  },
  // add the lines below ↓
  output: 'standalone',
  experimental: {
    outputFileTracingRoot: path.join(__dirname, '../../'),
  }
}

And in 13.9.7, reproduce the issue by cloning the following repository and run

npm install
npm run build

Errror message: image

This is now fixed with Nx version 16.3.0

This is a huge problem for us. We don’t use any deployment platform like vercel or netlify. We deploy our front-end to a kubernetes cluster, so we need to be able to dockerize our next.js applications. Any comment from nx team on the status of the issue? This has been open for more than a year.

Unfortunately the above workaround did not work for me while using nx: 14.5.15 and next:12.2.3. I’m just going to go without the new functionality for now until their is support for such with nx.

Hey all,

My standalone build outputs this server.js file

const NextServer = require('next/dist/server/next-server').default
const http = require('http')
const path = require('path')
process.env.NODE_ENV = 'production'
process.chdir(__dirname)
...

However it only outputs this next-server.js file in the node_modules folder


"use strict";
Object.defineProperty(exports, "__esModule", {
    value: true
});
exports.default = void 0;
require("./initialize-require-hook");
require("./node-polyfill-fetch");
require("./node-polyfill-web-streams");
....

image

The server fails to start because required(“./initialize-require-hook”); doesn’t exist in the node_modules.

Here is my next config file

//@ts-check

// eslint-disable-next-line @typescript-eslint/no-var-requires
const { withNx } = require('@nrwl/next/plugins/with-nx');
const path = require('path');

/**
 * @type {import('@nrwl/next/plugins/with-nx').WithNxOptions}
 **/
const nextConfig = {

  // outputStandalone: true,
  output: 'standalone',
  experimental: {
    // this includes files from the monorepo base two directories up
    // outputFileTracingRoot: path.join(__dirname, '../../'),
  },


  nx: {

    // Set this to true if you would like to to use SVGR
    // See: https://github.com/gregberge/svgr
    svgr: false,
  },
};

module.exports = withNx(nextConfig);

Am I missing something?

First of all thank you for your work, so I also came across the same problem, I was able to find a Hack similar to the comments, it is indeed a problem of Path between Nx (root, outputPath) and the standalone of next,

"targets": {
  "build": {
      "executor": "@nrwl/next:build",
      "outputs": ["{options.outputPath}"],
      "defaultConfiguration": "production",
      "options": {
          "root": "apps/terminal",
          "outputPath": "apps/terminal"
      },

and

    experimental: {
        outputStandalone: true,
        outputFileTracingRoot: path.join(__dirname, "../../")
    }

finish with Capture d’écran 2022-04-15 à 05 54 48

this standalone functionality is a real “plus value” for the production of minimal image (docker, k8s, …) I hope you will be able to look into it quickly, thank you

Hi all, I was wondering if there was an official fix for this. I’m currently leaving the build inside the Next.js directory (I just added Nx; not using @nrwl/next), but I was able to get it working with this Dockerfile (app is named dashboard):

  experimental: {
    outputStandalone: true,
    outputFileTracingRoot: path.join(__dirname, "../../"),
  },
# Automatically leverage output traces to reduce image size 
# https://nextjs.org/docs/advanced-features/output-file-tracing
COPY --chown=nextjs:nodejs /packages/dashboard/.next/standalone ./
COPY --chown=nextjs:nodejs /packages/dashboard/.next/static ./packages/dashboard/.next/static
COPY /packages/dashboard/public ./packages/dashboard/public
# These are partially installed for some reason. Remove or it won't work.
RUN rm -rf ./packages/dashboard/node_modules/react
RUN rm -rf ./packages/dashboard/node_modules/next

...

CMD ["node", "./packages/dashboard/server.js"]

Pretty weird combination of folders/files, but I hope it helps someone in the meantime, or reveals the source of the issue.

Probably Nx v16.3.0 will fix this issue thanks to the direct use of nextjs cli and API instead of nx wrappers

@nowlena Have you tried the workarounds suggested on this thread?

Yes, I’m not being lazy here, I’m unable to find a working solution.

I’m not implying that either, but since I did share workarounds above for both Next 12 and 13, I find it fairly difficult to help you without any inputs other than “I’ve been trying to resolve this all day without luck. Is there any way to get eyes on this?” 😕

What have you tried? What is the structure of your workspace? Are you using the default ‘dist’ path? What error messages are you catching? Are you using Next 12 or 13? Have you added the suggested “import path from ‘path’”? On which file? I understand your frustration. Hence why I’m here replying to you.

Has anyone else noticed issues when it comes to the automatic environment variable expansion in the .env files when using the outputStandalone and outputFileTracingRoot options?

Example from the docs for clarity:

# .env
HOSTNAME=localhost
PORT=8080
HOST=http://$HOSTNAME:$PORT

Everything else seems ok, but my pages are dying because of incorrect configuration values (my env vars seem to be passed along as simple strings like $HOSTNAME instead of actually being substituted)

It seems this issue won’t be resolved for quite some time. I created a sample repo nx-next-i18next-standalone building a minimal docker image that works with multiple apps nx workspace. Thanks to all comments from this thread!
There’s also next-i18next integration if you’re interested.

I looked into this a bit more last weekend. It looks like the root-cause might be a race-condition of some sort. The step that writes the tiny server to the server.js file located here is throwing an error because it thinks that file hasn’t been created yet.

On next.js’ side, output-file-tracing is working fine (if you create-next-app and enable outputFileTracing, everything works as expected). So I’m thinking somewhere around this step, something in nx is maybe removing that server.js file.

Hope that helps.

Just don’t ask how I investigated this and how much time I spend to find out how to fix this. ⚠️ WARNING ⚠️ Next text for people who believe in magic, skeptics, please stop read this text right now. I warned you.

Steps to get working repo for standalone Next.js server with build using NX CLI.

Init example workspace (or use your own)

  1. Run npx create-nx-workspace@latest --preset=next (tested on v14.1.9 workspace)
  2. Answers to questions: ✔ Workspace name (e.g., org name): next-test ✔ Application name: site ✔ Default stylesheet format: scss
  3. Go to next.config.js file and add to config:
experimental: {
    outputStandalone: true,
    outputFileTracingRoot: path.join(__dirname, "../../")
}

Preparation finished, now let’s make real magic

Go to pages/index.tsx (or any page file) and add this lines (don’t ask why, this is MAGIC):

import path from 'path';
path.resolve('./next.config.js');

Really can be any js file that can be resolved from current location (even empty). This code enough to insert only once.

Show magic to the world

Run npm run build. That’s it! No errors!

Bonus

This is a VERY SIMPLE Docker config that show how to work with this build.

FROM node:16-alpine AS builder
RUN apk add --no-cache libc6-compat python3 make g++
WORKDIR /build
COPY . .
RUN npm i && npm run build


FROM node:16-alpine AS runner
ARG APP=site
WORKDIR /app

RUN addgroup --system --gid 1001 nodejs
RUN adduser --system --uid 1001 nextjs

COPY --from=builder --chown=nextjs:nodejs /build/dist/apps/${APP}/.next/standalone ./
COPY --from=builder --chown=nextjs:nodejs /build/dist/apps/${APP}/.next/static ./dist/apps/${APP}/.next/static
COPY --from=builder --chown=nextjs:nodejs /build/dist/apps/${APP}/public ./apps/${APP}/public

USER nextjs

EXPOSE 3000

ENV NODE_ENV=production
ENV APP_PATH=apps/${APP}/server.js
ENV PORT 3000

CMD node $APP_PATH

Thanks to @bboyz269 for working example, from his repository I found the issue why build didn’t work on clean project. His repo works because of next-i18next lib code.

I was unable to get standalone builds working until I followed this. There is no reason why it should be this difficult.

Looks like the server folder is not copied properly.

After running nx build myapp --skip-nx-cache, the apps/myapp/.next/standalone/node_modules/next/dist/server folder has significantly less number of files as compared to the a non nx nextjs build created via npx create-next-app --example with-docker nextjs-docker .

Here are the contents for apps/myapp/.next/standalone/node_modules/next/dist/server

get-page-files.js next-server.js
htmlescape.js     utils.js

Contents for with-docker for .next/standalone/node_modules/next/dist/server

accept-header.js
api-utils
app-render.js
base-http
base-server.js
body-streams.js
config-shared.js
config-utils.js
config.js
crypto-utils.js
font-utils.js
get-page-files.js
google-font-metrics.json
htmlescape.js
image-optimizer.js
initialize-require-hook.js
internal-utils.js
lib
load-components.js
next-server.js
node-polyfill-fetch.js
node-polyfill-web-streams.js
node-web-streams-helper.js
optimize-amp.js
post-process.js
render-result.js
render.js
request-meta.js
require.js
response-cache
router.js
send-payload
serve-static.js
server-route-utils.js
utils.js
web

Unfortunately, none of workarounds work with nx 14.8.1 and next 12.3.1. I’ve also tried to debug Next CLI and change the output folder, but it looks like .nft files are not generated correctly. For sure the bug is present in NX and should be fixed, as Next standalone works fine.

@Yizhachok Yes, I also tried it on a fresh project, and it absolutely works. An interesting tidbit is that resolving ./next.config.js makes the build succeed, but resolving ../next.config.js, i.e the correct path relative to pages/index.tsx, fails the build.

As for my own project I’m guessing I accidentally “solved” this problem in a similar fashion along the way without realizing it, since I was still using the outputPath-hack so the original error was obscured.

@cocamm how are you getting that to work for you? doesn’t that output the build in your app’s directory, where the standalone bundle is several layers deep? are you wrapping the next/build executor in a run-command executor and shuffling that bundle around till everything’s in its place?

Yes, you are right. But, I will run this command just in the CI step of my workflow and I’ve made some changes in my Dockerfile to get just the files from this folder.

I know it is not the best solution, but it was the only way that I could get this working.

One thing that’s interesting: at one point I hard-coded the distDir value as an absolute-path inside the next.js codebase (for that build process), and the correct standalone output was generated. Albeit, in the wrong directory lol it put the build output in apps/<my-app>/<absolute-path-from-home-dir>/<dist-folder-with-correct-standalone>.

This actually does seem to be a dependency mismatch issue. The last next.js version that output-file-tracing works for is 12.0.7 (which has a known security vulnerability according to snyk).

Does Nx document supported next.js versions anywhere? Just saw an issue asking for support for next.js@12.1.0, so I’m assuming its not officially supported?