Node.js8 min read
Docker for Node.js Applications
Containerize Node.js apps with Docker. Learn Dockerfile, docker-compose, and deployment best practices.
Sarah Chen
December 19, 2025
0.0k0
Docker for Node.js Applications
Docker packages your app with its dependencies. Works on my machine = works everywhere.
Basic Dockerfile
# Dockerfile
FROM node:20-alpine
WORKDIR /app
COPY package*.json ./
RUN npm ci --only=production
COPY . .
EXPOSE 3000
CMD ["node", "server.js"]
Build and Run
# Build image
docker build -t my-app .
# Run container
docker run -p 3000:3000 my-app
# Run with environment variables
docker run -p 3000:3000 -e NODE_ENV=production my-app
# Run in background
docker run -d -p 3000:3000 my-app
Optimized Dockerfile
# Use specific version
FROM node:20-alpine AS builder
WORKDIR /app
# Copy package files first (better caching)
COPY package*.json ./
# Install all dependencies for build
RUN npm ci
# Copy source
COPY . .
# Build if needed (TypeScript, etc.)
RUN npm run build
# Production stage
FROM node:20-alpine
WORKDIR /app
# Copy package files
COPY package*.json ./
# Install production dependencies only
RUN npm ci --only=production && npm cache clean --force
# Copy built files from builder
COPY --from=builder /app/dist ./dist
# Security: run as non-root user
USER node
EXPOSE 3000
CMD ["node", "dist/server.js"]
.dockerignore
node_modules
npm-debug.log
.git
.gitignore
.env
.env.*
Dockerfile
docker-compose.yml
.dockerignore
README.md
.nyc_output
coverage
Docker Compose
# docker-compose.yml
version: '3.8'
services:
app:
build: .
ports:
- "3000:3000"
environment:
- NODE_ENV=production
- DATABASE_URL=mongodb://mongo:27017/myapp
- REDIS_URL=redis://redis:6379
depends_on:
- mongo
- redis
restart: unless-stopped
mongo:
image: mongo:6
volumes:
- mongo-data:/data/db
restart: unless-stopped
redis:
image: redis:7-alpine
restart: unless-stopped
volumes:
mongo-data:
Run with Compose
# Start all services
docker-compose up
# Start in background
docker-compose up -d
# View logs
docker-compose logs -f app
# Stop all
docker-compose down
# Stop and remove volumes
docker-compose down -v
Development with Docker
# docker-compose.dev.yml
version: '3.8'
services:
app:
build:
context: .
dockerfile: Dockerfile.dev
ports:
- "3000:3000"
volumes:
- .:/app
- /app/node_modules # Don't override node_modules
environment:
- NODE_ENV=development
command: npm run dev
# Dockerfile.dev
FROM node:20-alpine
WORKDIR /app
COPY package*.json ./
RUN npm install
COPY . .
CMD ["npm", "run", "dev"]
Health Checks
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
CMD wget --quiet --tries=1 --spider http://localhost:3000/health || exit 1
// Express health endpoint
app.get('/health', (req, res) => {
res.json({ status: 'ok', timestamp: new Date().toISOString() });
});
Multi-Stage for TypeScript
FROM node:20-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build
FROM node:20-alpine
WORKDIR /app
COPY package*.json ./
RUN npm ci --only=production
COPY --from=builder /app/dist ./dist
USER node
EXPOSE 3000
CMD ["node", "dist/index.js"]
Common Commands
# List containers
docker ps
# Stop container
docker stop <container_id>
# Remove container
docker rm <container_id>
# List images
docker images
# Remove image
docker rmi <image_id>
# Shell into container
docker exec -it <container_id> sh
# View logs
docker logs <container_id>
Key Takeaway
Docker ensures consistent environments. Use multi-stage builds for smaller images. Use docker-compose for multi-service apps. Include health checks. Use non-root user for security. Keep images small with Alpine.
#Node.js#Docker#DevOps#Deployment#Advanced