Nest.js CLI not found in Docker multi-stage build

64 Views Asked by At

I'm trying to set up a multi-stage Docker build for my Node.js and Nest.js application. The Dockerfile includes installing dependencies, building the application, and running it. However, I'm encountering an issue with the npm run build command in the production stage of the Dockerfile.

Here's a simplified version of my Dockerfile:

# Use node base image with tag 18-slim
FROM node:18-slim as base

# Set NODE_ENV environment variable to production
ENV NODE_ENV=production

# Expose port 3000
EXPOSE 3000

# Create app directory and set permissions
WORKDIR /app
RUN chown -R node:node /app

# Switch to non-root user
USER node

# Copy package.json and package-lock.json
COPY --chown=node:node package*.json ./

# Install dependencies
RUN npm ci && npm cache clean --force

# Set PATH environment variable in dev stage
ENV PATH /app/node_modules/.bin:$PATH

# Copy source code
COPY --chown=node:node . .

# Development stage
FROM base as dev

# Set NODE_ENV environment variable to development
ENV NODE_ENV=development

# Command to run development server
CMD ["npm", "run", "start:dev"]

# Production stage
FROM base as prod

# Build production code
RUN npm run build

# Command to run production server
CMD ["node", "dist/main.js"]

The error message I'm getting is:

sh: 1: nest: not found

It seems that the Nest.js CLI is not available in the PATH during the production build, even though it's installed as a dev dependency in my package.json.

#11 [prod 1/2] RUN ls /app/node_modules/.bin
#11 0.152 acorn
#11 0.152 color-support
#11 0.152 highlight
#11 0.152 js-yaml
#11 0.152 mime
#11 0.152 mkdirp
#11 0.152 node-pre-gyp
#11 0.152 nopt
#11 0.152 opencollective
#11 0.152 semver
#11 0.152 sha.js
#11 0.152 ts-node
#11 0.152 ts-node-cwd
#11 0.152 ts-node-esm
#11 0.152 ts-node-script
#11 0.152 ts-node-transpile-only
#11 0.152 ts-script
#11 0.152 tsc
#11 0.152 tsserver
#11 0.152 typeorm
#11 0.152 typeorm-ts-node-commonjs
#11 0.152 typeorm-ts-node-esm
#11 0.152 uuid
#11 DONE 0.2s

How can I ensure that the Nest.js CLI is available in the PATH during the production build in my Dockerfile?

2

There are 2 best solutions below

1
Shigerello On
Rev2

The following instruction in base stage renders /app/node_modules/.bin/nest unavailable.

# Set NODE_ENV environment variable to production
ENV NODE_ENV=production

But in the current configuration of Dockerfile, because base stage is meant for both dev and prod, including dev dependencies will not sit well for prod stage.

Now, I suggest the following to remedy the situation:

  • Remove ENV NODE_ENV=production and RUN npm ci from base stage, making this stage free from preparing dev/prod dependencies.
  • Put ENV NODE_ENV=development and RUN npm ci to dev stage.
  • Put ENV NODE_ENV=production and RUN npm ci to prod stage.

Docker has a nice Dockerfile example on this matter.

https://docs.docker.com/language/nodejs/develop/#update-your-dockerfile-for-development

Rev1

See if changing /dev/node_modules/.bin to /app/node_modules/.bin resolves the issue.


At base stage, instruction WORKDIR /app set the working directory for the following instructions like RUN to be performed at /app, so that following instruction RUN npm install creates /app/node_modules.

But later on, at dev and prod stage, instructions ENV PATH include a path /dev/node_modules/.bin, which contradicts the setup in base stage.

0
datawookie On

Not sure what your project layout looks like, but suppose that it's simple like this:

├── Dockerfile
├── package.json
├── package-lock.json
├── src
│   └── index.js
└── webpack.config.js

The following will work and has minimal changes relative to your current setup.

Dockerfile

FROM node:18-slim as base

ENV NODE_ENV=production

EXPOSE 3000

WORKDIR /app
RUN chown -R node:node /app

USER node

COPY --chown=node:node package*.json ./

RUN npm ci && npm cache clean --force

ENV PATH /app/node_modules/.bin:$PATH

COPY --chown=node:node . .

# STAGE: DEV ------------------------------------------------------------------

FROM base as dev

ENV NODE_ENV=development

CMD ["npm", "run", "start:dev"]

# STAGE: prod -----------------------------------------------------------------

FROM base as prod

RUN npm run build

CMD ["node", "dist/main.js"]

package.json

{
    "name": "application-name",
    "version": "1.0.0",
    "description": "",
    "main": "index.js",
    "scripts": {
      "start": "node dist/main.js",
      "start:dev": "nodemon src/index.js",
      "build": "webpack"
    },
    "keywords": [],
    "author": "",
    "license": "ISC",
    "dependencies": {
      "express": "^4.17.1",
      "@nestjs/cli": "10.3.2",
      "webpack-cli": "5.1.4"
    },
    "devDependencies": {
      "nodemon": "^2.0.7"
    }
  }