Docker Best Practices
Use Official Docker Image as Base Image
Instead of using Ubuntu as a base image and installing dependencies on it, take a base image (e.g., Node). This will have all dependencies installed and would have been made with best practices.
Use Specific Image Versions
If a version is not specified, the latest version will be installed, which may not be suitable for all cases.
Use Small-Sized Official Images
Images based on full-blown OS distributions include all dependencies, many of which may not be needed.
Using smaller images results in:
- Less attack surface
- More security
- Lower storage usage
- Easier transfer
Optimize Caching Image Layers
If a layer is reinstalled, all the following layers will be reinstalled as well.
Order Dockerfile commands from least to most frequently changing to maximize cache usage.
Example:
FROM node:20.0.2-alpine
WORKDIR /app
# Copy package.json and package-lock.json before project files
COPY package.json package-lock.json .
# Install dependencies only when package.json changes (cache-efficient)
RUN npm install --production
# Now copy the project files (won’t invalidate npm install cache)
COPY myapp /app
# Start the application
CMD ["node", "src/index.js"]
Key Takeaways:
- DO NOT re-run
npm install
if only project files change. - Re-run
npm install
whenpackage.json
orpackage-lock.json
changes. - Use
.dockerignore
to exclude unnecessary files.
Use Multi-Stage Builds
The Multi-Stage Build technique helps optimize Docker images by reducing their final size and improving efficiency.
Example:
# Build stage
FROM maven AS build
WORKDIR /app
COPY myapp /app
RUN mvn package
# Runtime stage
FROM tomcat
COPY --from=build /app/target/file.war /usr/local/tomcat/webapps/
Key Takeaways:
- Multi-stage builds separate the build process from the final image.
- The first stage (build stage) compiles the application.
- The second stage (runtime stage) includes only the necessary artifacts.
- Only the last stage defines the final image.
Benefits of Multi-Stage Builds:
- Reduces image size by excluding build tools.
- Improves security by keeping only necessary files in the final image.
- Enhances efficiency by preventing build environment bloat.
Use the Least Privileged User
Create a dedicated user and group in the Dockerfile:
RUN groupadd -r tom && useradd -g tom tom
Some images already include a generic user (e.g., Node includes the node
user). Use it with:
USER node
Scan Images for Vulnerabilities
Login to Docker and scan for vulnerabilities using:
docker scout cves <ImageName>