Skip to main content

Command Palette

Search for a command to run...

Dockerfiles

Building Efficient Container Images.

Published
•3 min read•View as Markdown
Dockerfiles

A Dockerfile is a text file containing step-by-step instructions for building a Docker image. These images serve as blueprints for creating containers that run your application consistently across all environments.

Key Benefits:

  • Reproducible builds (same Dockerfile = same image everywhere)

  • Version-controlled infrastructure (store Dockerfiles in Git)

  • Optimized container creation (smaller images, faster builds)


Essential Dockerfile Instructions

1. FROM - The Foundation

FROM ubuntu:22.04  # Or alpine, python:3.9, etc.
  • Purpose: Sets the base image (creates the first layer).

  • Best Practice:

    • Use official images from Docker Hub when possible.

    • Prefer slim variants (e.g., alpine, -slim) to reduce size.

2. RUN - Executing Commands

RUN apt-get update && apt-get install -y \
    python3 \
    pip \
    && rm -rf /var/lib/apt/lists/*
  • Purpose: Installs dependencies or runs setup commands.

  • Key Notes:

    • Each RUN creates a new layer → chain related commands with && to minimize layers.

    • Clean up cache/files in the same RUN to reduce image size.

3. COPY vs. ADD - Adding Files

COPY ./app /app      # Preferred for simplicity
ADD ./app.tar.gz /   # Use only for remote URLs/tarballs
  • COPY: Straight file copying (recommended for most cases).

  • ADD: Has extra features (URL downloads, tar extraction) but can be unpredictable.

  • Layer Impact: Each creates a new layer → group files in one command.

4. WORKDIR - Setting the Working Directory

WORKDIR /app  # All subsequent commands run from here
  • Purpose: Avoids paths like RUN cd /app && ....

  • Layer Impact: Creates a layer only if the directory doesn’t exist.

5. ENV - Environment Variables

ENV APP_PORT=8080 \
    NODE_ENV=production
  • Purpose: Sets variables usable by the app and Dockerfile.

  • Best Practice: Group variables in a single ENV to reduce layers.

6. EXPOSE - Documenting Ports

EXPOSE 8080  # Metadata only; doesn't publish the port
  • Purpose: Documents which ports the app uses (actual publishing happens with -p in docker run).

  • Layer Impact: No new layer; just metadata.

7. CMD vs. ENTRYPOINT - Default Commands

CMD ["python", "app.py"]          # Default run command (overrideable)
ENTRYPOINT ["python", "app.py"]   # Fixed base command (appends `CMD` args)
  • Key Differences:

    • CMD: Default command, can be overridden (e.g., docker run <image> /bin/bash).

    • ENTRYPOINT: Makes the container act like an executable.

  • Layer Impact: No new layers; stored as metadata.


Optimizing Dockerfiles

1. Minimize Layers

  • Combine related RUN, COPY, and ENV commands.

  • Bad:

      RUN apt-get update
      RUN apt-get install -y python
    
  • Good:

      RUN apt-get update && apt-get install -y \
          python \
          && rm -rf /var/lib/apt/lists/*
    

2. Leverage Build Caching

  • Docker caches each layer → order instructions from least to most frequently changed:

    1. FROM

    2. ENV/WORKDIR

    3. COPY (only dependency files first, e.g., requirements.txt)

    4. RUN (install dependencies)

    5. COPY (rest of the app code)

3. Use .dockerignore

  • Exclude unnecessary files (like .git, node_modules):

      **/.git
      **/node_modules
      *.log
    

Example: Optimized Dockerfile

# Stage 1: Build
FROM python:3.9-slim as builder
WORKDIR /app
COPY requirements.txt .
RUN pip install --user -r requirements.txt

# Stage 2: Runtime
FROM python:3.9-slim
WORKDIR /app
COPY --from=builder /root/.local /root/.local
COPY . .
ENV PATH=/root/.local/bin:$PATH \
    PORT=8080
EXPOSE 8080
CMD ["python", "app.py"]

Key Takeaways

  1. Layer Reduction: Fewer layers → smaller images → faster builds.

  2. Caching: Order instructions to maximize cache reuse.

  3. Security: Avoid ADD, run as non-root, and clean up temporary files.

  4. Multi-Stage Builds: Use for separating build/runtime environments (reduces final image size).


Practice Steps

  • Try building an image:

      docker build -t my_app .
    
  • Inspect layers:

      docker history my_app