Skip to content

Node.js Docker — Complete Guide to Containerizing Node.js Applications

DodaTech Updated 2026-06-28 4 min read

In this tutorial, you will learn about Node.js Docker. We cover key concepts, practical examples, and best practices to help you master this topic.

Docker containerizes Node.js applications into portable, reproducible environments that run consistently across development, testing, and production servers.

What You'll Learn

By the end of this tutorial, you'll create Dockerfiles for Node.js apps, use multi-stage builds, configure docker-compose for multi-service apps, manage environment variables, and optimize images for production.

Why Docker Matters

Docker eliminates "it works on my machine" problems. It packages your application with all its dependencies into a single container that runs the same everywhere, from your laptop to production servers.

Real-World Use

A Node.js microservice with Express, Redis, and PostgreSQL runs in three Docker containers orchestrated by docker-compose. New developers get a fully functional environment with one command: docker-compose up.

Docker Learning Path

flowchart LR
  A[Caching Redis] --> B[Docker]
  B --> C[Deployment]
  C --> D[DevOps]
  D --> E[Production]
  A --> F{You Are Here}
  style F fill:#f90,color:#fff

Dockerfile for Node.js

FROM node:22-alpine AS base
WORKDIR /app
COPY package*.json ./
RUN npm ci --only=production
COPY . .
EXPOSE 3000
CMD ["node", "server.js"]

Multi-Stage Build

# Build stage
FROM node:22-alpine AS build
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build
# Production stage
FROM node:22-alpine AS production
WORKDIR /app
COPY --from=build /app/package*.json ./
RUN npm ci --only=production
COPY --from=build /app/dist ./dist
EXPOSE 3000
CMD ["node", "dist/server.js"]

Docker Compose

version: "3.8"
services:
  app:
    build: .
    ports:
      - "3000:3000"
    environment:
      - NODE_ENV=production
      - REDIS_URL=redis://redis:6379
    depends_on:
      - redis
    volumes:
      - uploads:/app/uploads
  redis:
    image: redis:7-alpine
    ports:
      - "6379:6379"
    volumes:
      - redis-data:/data
volumes:
  uploads:
  redis-data:

Environment Configuration

# .env file (not committed to git)
DATABASE_URL=postgresql://user:pass@db:5432/mydb
REDIS_URL=redis://redis:6379
JWT_SECRET=your-secret-key
// config.js
export default {
  databaseUrl: process.env.DATABASE_URL,
  redisUrl: process.env.REDIS_URL,
  jwtSecret: process.env.JWT_SECRET,
  port: parseInt(process.env.PORT, 10) || 3000
};

.dockerignore

node_modules
npm-debug.log
.env
.git
.gitignore
README.md
test/
coverage/
.dockerignore

Common Mistakes

1. Including node_modules in the Image

Always add node_modules to .dockerignore. COPY runs npm install in the container with the correct platform binaries.

2. Running as Root in Container

Run as a non-root user for security. Use USER node in the Dockerfile (node image has a built-in node user).

3. Using Latest Tag for Base Image

Latest breaks builds when new major versions release. Pin to a specific version: node:22-alpine, not node:latest.

4. Hardcoding Environment Variables

Use environment variables with default values. Store secrets in Docker secrets or .env files, not in the Dockerfile.

5. Not Setting Proper Health Checks

Docker needs to know when your app is ready. Add HEALTHCHECK instruction and expose a /health endpoint.

Practice Questions

1. What is the difference between Dockerfile and docker-compose?

Dockerfile builds a single container image. docker-compose defines and runs multiple containers as a service.

2. Why use multi-stage builds?

They separate build dependencies from runtime dependencies, resulting in smaller, more secure production images.

3. What does .dockerignore do?

It prevents specified files/directories from being copied into the Docker image, reducing image size and preventing secret leaks.

4. How do you pass environment variables to a Docker container?

Via Dockerfile ENV, docker-compose environment section, docker run -e, or .env files.

5. Challenge: Create a Dockerfile for a Node.js app that uses a non-root user, multi-stage build, and health check.

FROM node:22-alpine AS build
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
FROM node:22-alpine
RUN addgroup -S appgroup && adduser -S appuser -G appgroup
WORKDIR /app
COPY --from=build /app .
USER appuser
EXPOSE 3000
HEALTHCHECK --interval=30s CMD wget --no-verbose --tries=1 localhost:3000/health || exit 1
CMD ["node", "server.js"]

FAQ

Should I use Alpine or full Node image?

Alpine (node:22-alpine) is much smaller (~50MB vs ~350MB). Use Alpine for production. Full image for debugging.

How do I debug a Node.js container?

docker exec -it sh enters the container. Use --inspect flag and map the debug port.

What is the difference between CMD and ENTRYPOINT?

CMD provides default arguments. ENTRYPOINT is the main command. Combine both: ENTRYPOINT ['node'], CMD ['server.js'].

How do I handle log files in Docker containers?

Log to stdout/stderr. Docker captures container logs. Use docker logs to view them.

Can I run npm install in production containers?

No, for security and speed. Pre-install in build stage, copy node_modules to production.

Mini Project: Dockerized Express API

Build a complete docker-compose setup for an Express API with Redis.

FROM node:22-alpine
WORKDIR /app
COPY package*.json ./
RUN npm ci --only=production
COPY . .
EXPOSE 3000
HEALTHCHECK --interval=30s --timeout=3s CMD wget -qO- localhost:3000/health || exit 1
CMD ["node", "server.js"]
# docker-compose.yml
version: "3.8"
services:
  api:
    build: .
    ports: ["3000:3000"]
    environment:
      REDIS_URL: redis://redis:6379
    depends_on: [redis]
  redis:
    image: redis:7-alpine

What's Next

Node.js Deployment Docker Compose Express Security

Built by the developers of DodaTech

Doda Browser, DodaZIP & Durga Antivirus Pro