
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
RUNcreates a new layer → chain related commands with&&to minimize layers.Clean up cache/files in the same
RUNto 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
ENVto 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
-pindocker 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, andENVcommands.Bad:
RUN apt-get update RUN apt-get install -y pythonGood:
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:
FROMENV/WORKDIRCOPY(only dependency files first, e.g.,requirements.txt)RUN(install dependencies)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
Layer Reduction: Fewer layers → smaller images → faster builds.
Caching: Order instructions to maximize cache reuse.
Security: Avoid
ADD, run as non-root, and clean up temporary files.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



