Containerize Node.js
Use Deterministic Builds
Avoid :latest: Use explicit versions for consistent builds. Example: node:23.6.1-bullseye-slim@sha256:....
Slim Images: Prefer -slim variants for smaller images and reduced attack surfaces, but avoid node:alpine due to compatibility issues.
Install Production Dependencies Only
Use npm ci --only=production or pnpm install --frozen-lockfile to install only production dependencies for smaller, more secure images.
Set
NODE_ENV=production
Enables performance and security optimizations for frameworks like Express. Always define this in the Dockerfile.
Avoid Running Containers as Root
Use the least-privilege user (node).
Ensure proper ownership using COPY --chown=node:node.
Graceful Application Termination
Use tools like dumb-init to handle termination signals (SIGTERM, SIGINT) properly.
Implement signal handlers in Node.js to close resources gracefully.
Multi-Stage Builds
Separate build and production stages to optimize image size and remove sensitive data (e.g., NPM_TOKEN).
Security Scanning
Test images with tools like Snyk for vulnerabilities. Regularly update dependencies and base images.
Exclude Unnecessary Files
Use .dockerignore to avoid including sensitive or unnecessary files like .git, node_modules, and .env.
Handle Secrets Securely
Mount secrets (e.g., .npmrc) securely during the build using Docker secrets. Avoid embedding them in the final image.
Graceful Shutdown and Signal Handling
Use Node.js event handlers to close connections and finish requests before shutting down.
Example Dockerfile:
Key Tools and Recommendations:
Use Snyk: Integrate into CI/CD for continuous security monitoring.
Docker Secrets: Safely handle sensitive information during builds.
dumb-init: Manage PID 1 and signal forwarding.
References:
Last updated
Was this helpful?
