This Dockerfile follows modern DevSecOps and container security best practices for deploying a Node.js application in production.
- Multi-stage build
- Minimal attack surface
- Non-root user execution
- Dependency isolation
- Reproducible builds
- Health checks
- Production-only dependencies
- Lightweight Alpine image
- Reduced image size
- Better container startup performance
########################################
# Stage 1: Build Stage
########################################
# Use a lightweight official Node.js image
FROM node:22-alpine AS builder
# Set working directory
WORKDIR /app
# Copy dependency files first for better layer caching
COPY package.json package-lock.json ./
# Install all dependencies (including devDependencies)
RUN npm ci
# Copy application source code
COPY . .
# Build the application
RUN npm run build
########################################
# Stage 2: Production Stage
########################################
# Use a fresh minimal image
FROM node:22-alpine AS production
# Create application group
RUN addgroup -S nodegroup
# Create non-root application user
RUN adduser -S nodeuser -G nodegroup
# Set working directory
WORKDIR /app
# Copy package files
COPY package.json package-lock.json ./
# Install production dependencies only
RUN npm ci --omit=dev \
&& npm cache clean --force
# Copy compiled application from builder stage
COPY --from=builder /app/dist ./dist
# Change ownership of application files
RUN chown -R nodeuser:nodegroup /app
# Switch to non-root user
USER nodeuser
# Expose application port
EXPOSE 3000
# Healthcheck configuration
HEALTHCHECK --interval=30s \
--timeout=10s \
--start-period=20s \
--retries=3 \
CMD wget --no-verbose --tries=1 --spider http://localhost:3000/health || exit 1
# Environment configuration
ENV NODE_ENV=production
# Start application
CMD ["node", "dist/index.js"]Instead of shipping build tools and development dependencies to production, the application is built in a separate stage.
Benefits:
- Smaller image size
- Reduced attack surface
- Faster deployments
- Less vulnerability exposure
FROM node:22-alpine AS builderRunning containers as root is one of the most common security mistakes.
Bad:
USER rootGood:
RUN adduser -S nodeuser -G nodegroup
USER nodeuserBenefits:
- Limits privilege escalation
- Prevents accidental host modifications
- Reduces impact of container compromise
Development dependencies should never be installed in production.
npm ci --omit=devBenefits:
- Smaller image
- Fewer vulnerabilities
- Faster startup
Using:
npm ciinstead of:
npm installensures:
- Reproducible builds
- Consistent dependency versions
- Faster installation
Containers should expose their health status.
HEALTHCHECK ...Example endpoint:
app.get('/health', (_, res) => {
res.status(200).json({
status: 'UP'
});
});Benefits:
- Automatic restart detection
- Better orchestration
- Improved observability
Using Alpine Linux:
FROM node:22-alpineBenefits:
- Smaller image size
- Fewer packages installed
- Lower vulnerability count
RUN chown -R nodeuser:nodegroup /appBenefits:
- Prevents unauthorized writes
- Improves filesystem security
Always create a .dockerignore file.
node_modules
.git
.github
.env
.env.*
coverage
dist
Dockerfile
docker-compose.yml
npm-debug.log
*.md
.vscode
.idea
Benefits:
- Faster builds
- Smaller context
- Prevents secret leakage
Docker Compose:
services:
app:
read_only: trueservices:
app:
cap_drop:
- ALLservices:
app:
security_opt:
- no-new-privileges:trueservices:
app:
deploy:
resources:
limits:
cpus: "1"
memory: 512MExample with Trivy:
trivy image my-node-app:latestIntegrate into CI/CD pipelines before deployment.
- Multi-stage build
- Non-root user
- Production dependencies only
- Health check
- Docker ignore file
- Small base image
- Reproducible builds
- Proper file ownership
- Resource limits
- Vulnerability scanning
- No privilege escalation
- Reduced attack surface
This Dockerfile provides a strong baseline for secure Node.js container deployments in:
- Kubernetes
- Docker Swarm
- ECS
- EKS
- AKS
- GKE
- On-Premise Docker Hosts
It follows modern DevSecOps principles and significantly improves the security posture of a production Node.js application compared to traditional single-stage Dockerfiles running as root.