Node.js Docker — Complete Guide to Containerizing Node.js Applications
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
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