Docker

Docker for Software Developers, Part 5: Best Practices and Tips

Lean images, efficient layers, .dockerignore, multi-stage builds, container security, and performance tuning: proven practices for efficient, secure Docker environments, with a Node.js example.

Illustration: architecture diagram with frontend application, API gateway, orders service, user service, proxy, database, cache, and message queue AI-generated image

Best Practices and Tips for Working with Docker

Docker gives developers powerful ways to containerize applications and deliver them efficiently. To get the most out of Docker and avoid common mistakes, it pays to follow proven practices for working with containers. This part presents best practices that help keep Docker environments efficient, secure, and maintainable.

1. Use Lean, Optimized Docker Images

A common mistake is building large images that contain unnecessary files and libraries. Lean images load faster and consume fewer resources.

Tips:

  • Use FROM with specific versions and prefer images with the -alpine suffix, which are particularly lightweight.
  • Remove unnecessary files and caches at the end of the Dockerfile build.
  • Never store sensitive data such as passwords or keys directly in the Dockerfile. Use environment variables instead.
dockerfile
#
# Dockerfile
#
# Use a supported Node.js LTS image as the base
FROM node:24
# Set the working directory inside the container
WORKDIR /app
# Copy package.json and install dependencies
COPY package.json ./
RUN npm install
# Copy the rest of the code
COPY . .
# Define and document the port at build time (the .env file only applies at runtime)
ENV PORT=4000
EXPOSE ${PORT}
# Start command for the app
CMD ["npm", "start"]
# Environment variable definitions
# .env file
PORT=4000
DB_HOST=mongodb://localhost:27017/mydatabase
DB_USER=root
DB_PASSWORD=example_password
bash
# Start the container with the --env-file option
docker build -t mynodeapp .
docker run -p 4000:4000 --env-file .env mynodeapp

EXPOSE only documents which port the application listens on inside the container; the port is not published to the outside until -p 4000:4000 is passed at startup. For this to work, the PORT variable has to be defined in the Dockerfile via ENV or ARG: the .env file is only read at runtime through --env-file and is unknown at build time.

2. Use Layers Efficiently and Keep Them to a Minimum

Every instruction in a Dockerfile creates a new layer in the image. An efficient layer structure reduces image size and speeds up builds.

Tips:

  • Combine commands to reduce the number of layers, for example RUN apt-get update && apt-get install -y curl.
  • Place frequently changing content, such as dependencies, further down in the Dockerfile so that the upper layers rarely need to be rebuilt.

3. Use .dockerignore Files

A .dockerignore file excludes unnecessary files from the build process and reduces image size. This is especially useful in large projects where only certain files and folders are needed in the image.

Tips:

  • Add directories such as node_modules, logs, and tmp to .dockerignore.
  • Exclude sensitive data and configuration files that are not needed in the image.
# Example .dockerignore file
# Ignore the node_modules directory
node_modules
# Ignore local log files
*.log
# Ignore temporary files and folders
tmp/
*.tmp
# Ignore environment files containing sensitive data
.env
.env.local
# Ignore build artifacts and other locally generated files
dist/
build/
coverage/
# Ignore VCS and editor files
.git
.gitignore
.DS_Store
.vscode/
.idea/

4. Use Multi-Stage Builds for a Clean Separation

Multi-stage builds let you define several build stages in a single Dockerfile and include only the end product in the final image. That reduces the image size and keeps the image lean.

Benefits:

  • Separates development tools and libraries from the production environment
  • Reduces image size and improves security through minimal dependencies

Example:

dockerfile
FROM node:24 AS build
WORKDIR /app
COPY . .
RUN npm install
RUN npm run build
FROM nginx:alpine
COPY --from=build /app/build /usr/share/nginx/html

In this example, the Node.js build runs in a first stage and only its output is copied into the lean NGINX image.

5. Secure Your Containers

Container security matters, above all when applications run in production. Containers that are not configured properly can be vulnerable to attacks and security holes.

Tips:

  • Avoid running containers as root by restricting user privileges.
  • Use signed images and verify that images come from trusted sources.
  • Run security scans on Docker images to catch known vulnerabilities early.

6. Optimize Performance

Because Docker containers are lightweight, many of them can run efficiently in parallel on a single server. Even so, a few best practices help squeeze out more performance.

Tips:

  • Use caching for frequently used data and files.
  • Limit container resource usage (for example CPU and RAM) to prevent overload.
  • Use volumes for persistent data to avoid data loss on restarts and to improve speed at the same time.

Conclusion: Best Practices for Efficient, Secure Docker Containers

Applying these best practices helps keep Docker containers efficient and secure. From optimizing image size and handling container privileges safely to tuning performance, these tips are essential for using Docker successfully in modern software development. With these methods, developers can build stable, high-performing container environments that meet the demands of today's software landscape.

Sources