Home / Configs / Containers / Dockerfile for Node.js

Dockerfile for Node.js: example and generator

A small, working Dockerfile for a Node.js app, with build step, non-root user and health check options.

For Docker 20 or newer and a Node.js app that has a package-lock.json.

A Dockerfile is a recipe that packs your app and everything it needs into an image. Fill in your Node version, port and start file below, save the result as Dockerfile in your project folder, then build and run it.

Create your config file

Type your own values. The file below updates as you type, and nothing you type leaves your browser.

Pick a current long-term support version
Use Slim if a package fails to install on Alpine
The file that starts your app, like server.js or dist/index.js
The port your app listens on

Dockerfile
FROM node:24-alpine
WORKDIR /app
COPY package*.json ./
RUN npm ci --omit=dev
COPY . .
ENV NODE_ENV=production
USER node
EXPOSE 3000
CMD ["node", "server.js"]

How this setup works

Diagram: Your code, then docker build, then Container
Your code (package.json and app files) → docker build (makes the image) → Container (runs on your port)

Step-by-step setup

  1. Save the file above as Dockerfile (no extension) in your project folder, next to package.json.
  2. Create a file named .dockerignore with the lines node_modules and .git, so they are not copied into the image.
  3. Build the image: docker build -t myapp .
  4. Run it: docker run -p 3000:3000 myapp.
  5. Open http://localhost:3000 in your browser.

What the important lines mean

Common errors and how to fix them

npm error The npm ci command can only install with an existing package-lock.json
The lock file is missing or was not copied. Run npm install once on your machine and commit package-lock.json, or change npm ci to npm install.
Error: Cannot find module '/app/server.js'
The start file name or path is wrong, or the file was never copied. Check the name, and make sure .dockerignore does not exclude it.
The container runs but the browser cannot connect
Your app must listen on 0.0.0.0, not only 127.0.0.1, and you must publish the port with -p 3000:3000.
sh: tsc: not found, or the build fails
The build tools are dev dependencies, which are skipped by default. Tick the build step option above so they are installed first.
npm error gyp ERR! or a native module fails on Alpine
Some packages need tools that Alpine lacks. Switch the image type to Slim.

Frequently asked questions

Should I use Alpine or Slim?
Alpine gives the smallest image. Slim is based on Debian and works with more native packages. If an install fails on Alpine, try Slim.
Why use npm ci instead of npm install?
npm ci installs exactly what the lock file says and stops if it does not match, so every build gives the same result.
Do I need EXPOSE?
No. The app works without it. EXPOSE just documents the port. What actually opens the port to your computer is the -p option on docker run.

Written for: Docker 24 or newer, Node 22 or 24 · Updated 2026-10-11

Free tools that help

Fix a problem with this setup

More container configs

See all 41 config generators