ReviseAlgo Logo

Building Docker Images

Dockerfile Best Practices — Layer Ordering, .dockerignore, Non-Root Users

Structuring layers, reducing size, and running securely.

Interview: Essential for testing your production engineering skills. Be prepared to analyze a poorly written Dockerfile and identify layer issues, cache misses, and security holes.

Last Updated: June 14, 2026 15 min read

Introduction

Writing a Dockerfile that "just works" is easy. Writing a Dockerfile that builds rapidly, runs securely, and minimizes resource usage is much harder.

Production container engineering relies on structural conventions that optimize the compilation process and protect the underlying host operating systems.

The Four Pillars of Best Practices

1. Layer Chaining & Cleanup

Every RUN directive creates a new history layer. To avoid bloating images:
• Chain multiple shell commands using && and backslashes (\).
• Clean up package lists and temporary build files in the **same instruction** they are installed:

2. Cache Alignment Ordering

Copy static lock files and download dependencies *before* copying your active source directories. Since application code changes on every commit, copying dependencies first keeps the package downloads cached.

3. Context Reduction (.dockerignore)

Create a .dockerignore file in your build root. This prevents heavy, secret, or unnecessary directories (like .git, local node_modules, and local configurations) from being sent to the Docker daemon.

4. Run as Non-Root

Create a custom system group and user, and assign ownership to your active runtime directory:

Quick Quiz

Q1: Why should "rm -rf /var/lib/apt/lists/*" be in the same RUN command as "apt-get install"?

A) Because it speeds up command execution.

B) Because Docker merges them anyway.

C) If run in a separate RUN instruction, the files are deleted in the new layer, but they remain inside the previous layer history, saving no image space.

D) To avoid running as root.

Answer: C — File cleanup must be done in the same layer they were created, to prevent the build data from being committed to history.

Scenario-Based Challenge

Production Scenario:

You run docker build .. The output terminal hangs at "Sending build context to Docker daemon: 4.8 GB" for several minutes, even though your Node application files only consume 5MB. What is causing this delay, and how do you resolve it?

View Solution

Cause: When you start a build, the Docker client packs everything in your root directory into a tar archive (the build context) and sends it to the daemon. Without a .dockerignore file, Docker packs your local node_modules folder, local log directories, database exports, and the hidden .git repository history.

Solution: Create a .dockerignore file in the project root:

This prevents local build data from entering the context, reducing upload context size from GBs to MBs and resolving the delay.

Interview Questions

1. Why is it recommended to use double quotes and JSON arrays in CMD/ENTRYPOINT?

Using JSON arrays (Exec Form) executes commands directly without starting a shell wrapper. This ensures your app runs as PID 1, meaning it receives termination signals (SIGTERM) immediately, allowing database connections to close gracefully.

2. What does the "--no-install-recommends" flag do in apt-get?

It tells the package manager to install only the explicit package dependencies, skipping optional recommended packages. This prevents the installation of dozens of auxiliary libraries, keeping image sizes small.

Production Considerations

Use security linters (like **Hadolint**) in your CI/CD pipelines to audit Dockerfiles. Hadolint scans instructions and highlights violations such as missing package tags, unchained commands, or running as root, blocking dangerous code before it reaches production.