To package a Node.js application and its dependencies into a lightweight, portable container image that runs consistently across any environment.
- When preparing an application for production deployment.
- When ensuring development environment consistency.
- When isolating microservices in a cluster (Kubernetes/ECS).
Use multi-stage builds to keep the final production image as small as possible.
# Stage 1: Build
FROM node:20-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build
# Stage 2: Production
FROM node:20-alpine
WORKDIR /app
ENV NODE_ENV=production
# Install only production dependencies
COPY package*.json ./
RUN npm ci --only=production
# Copy built assets from builder stage
COPY --from=builder /app/dist ./dist
# Security: Run as non-root user
USER node
EXPOSE 3000
CMD ["node", "dist/index.js"]Prevent unnecessary files from bloating the build context.
node_modules
npm-debug.log
dist
.git
.env
Dockerfile
.dockerignore
Simplify local development with linked services.
# docker-compose.yml
services:
app:
build:
context: .
dockerfile: Dockerfile.dev
ports:
- "3000:3000"
volumes:
- .:/app
- /app/node_modules
environment:
- DATABASE_URL=postgres://user:pass@db:5432/db
depends_on:
- db
db:
image: postgres:15
environment:
- POSTGRES_PASSWORD=passCommands for the CLI.
# Build the image
docker build -t my-app:v1 .
# Run the container
docker run -p 3000:3000 --env-file .env my-app:v1- Base Image: Always use
alpineorslimtags for smaller, more secure images. - Layer Caching: Copy
package.jsonand install dependencies before copying the rest of the source code to leverage Docker layer caching. - Security: Never include
.envfiles or hardcoded secrets in the image. Use environment variables at runtime.
A production-ready Dockerfile that produces a minimal, secure image containing only the necessary runtime files.