I am dealing with several project that utilise NodeJS and this is my favorite Dockerfile for base:
# Use the official Node.js Alpine image as a base
FROM node:22-alpine AS base
# Install dumb-init
RUN apk add --no-cache dumb-init
# Use dumb-init as the entrypoint
ENTRYPOINT ["/usr/bin/dumb-init", "--"]
# Set the working directory
WORKDIR /home/node/app
RUN chown -R node:node /home/node/app
# Run as none root user
USER node
This Dockerfile has several good practices that contribute to a well-structured and efficient container image. Here are some reasons why it is considered good:
Minimal Dependencies: The apk add --no-cache dumb-init command installs dumb-init without caching the package index, which keeps the image size smaller. dumb-init is a simple process supervisor that helps manage zombie processes and signal handling, making the container more robust.
Entrypoint Configuration: By setting dumb-init as the entrypoint, the Dockerfile ensures that the application runs with proper signal handling. This is particularly important for gracefully shutting down the application and managing child processes.
Working Directory: The WORKDIR /home/node/app command sets the working directory for subsequent instructions. This makes the Dockerfile cleaner and ensures that all commands are executed in the correct context.
Non-Root User: The USER node instruction specifies that the container should run as a non-root user. Running applications as a non-root user is a security best practice, as it limits the potential damage that can be done if the application is compromised.
Anyway this Snyk article is good reference: https://snyk.io/blog/10-best-practices-to-containerize-nodejs-web-applications-with-docker/