Fatskills
Practice. Master. Repeat.
Study Guide: TECH **Dockerfile Essentials: FROM, RUN, COPY, CMD, ENTRYPOINT**
Source: https://www.fatskills.com/kubernetes/chapter/tech-dockerfile-essentials-from-run-copy-cmd-entrypoint

TECH **Dockerfile Essentials: FROM, RUN, COPY, CMD, ENTRYPOINT**

By Fatskills Exam Guides Team — the exam nerds behind 28,500+ quizzes and 2.1M practice questions across 500+ global exams.

⏱️ ~7 min read

Dockerfile Essentials: FROM, RUN, COPY, CMD, ENTRYPOINT

A hyper-practical, zero-fluff guide for engineers who need to write production-grade Dockerfiles—fast.


1. What This Is & Why It Matters

A Dockerfile is the blueprint for your container. It defines: - What OS and dependencies your app runs on (FROM, RUN).
- How files get into the container (COPY).
- What command runs when the container starts (CMD, ENTRYPOINT).

Why this matters in production:
- Security: A poorly written Dockerfile can expose secrets, bloat attack surfaces, or run as root.
- Performance: Unoptimized layers waste disk space, slow down builds, and increase CI/CD pipeline time.
- Reliability: If CMD and ENTRYPOINT conflict, your container might fail silently or behave unpredictably.
- Cost: Bloated images cost more in storage, bandwidth, and deployment time.

Real-world scenario:
You inherit a legacy Python app with a 2GB Docker image. The Dockerfile installs every dependency under the sun, copies the entire repo (including .git), and runs as root. Your job: 1. Shrink the image to <200MB.
2. Remove secrets accidentally baked into layers.
3. Ensure the app starts reliably in Kubernetes.

This guide will show you how.


2. Core Concepts & Components


FROM

  • Definition: Specifies the base image (e.g., alpine, ubuntu, python:3.9-slim).
  • Production insight:
  • Always use official images (e.g., python:3.9-slim over python:3.9—the -slim variant is smaller).
  • Pin versions (e.g., python:3.9.18-slim) to avoid breaking changes.
  • ⚠️ Avoid latest in production—it can silently upgrade and break your app.

RUN

  • Definition: Executes commands during build time (e.g., apt-get install, pip install).
  • Production insight:
  • Chain commands with && to reduce layers (e.g., RUN apt-get update && apt-get install -y curl).
  • Clean up cache in the same layer (e.g., && rm -rf /var/lib/apt/lists/*) to shrink the image.
  • ⚠️ Never run RUN commands as root unless absolutely necessary (use USER later).

COPY

  • Definition: Copies files from the host into the container (e.g., COPY ./app /app).
  • Production insight:
  • Use .dockerignore to exclude files (like node_modules, .git, or secrets).
  • Prefer COPY over ADD (unless you need ADD’s tar/URL auto-extraction—it’s less predictable).
  • ⚠️ COPY preserves file permissions. If your app fails with Permission denied, check the source file permissions.

CMD

  • Definition: Provides default arguments for the container’s executable. Can be overridden by docker run.
  • Production insight:
  • Use shell form (CMD python app.py) for simple commands.
  • Use exec form (CMD ["python", "app.py"]) for signals (e.g., SIGTERM) to work properly.
  • ⚠️ If ENTRYPOINT is set, CMD becomes its default arguments.

ENTRYPOINT

  • Definition: Defines the main executable for the container. Harder to override than CMD.
  • Production insight:
  • Use ENTRYPOINT for the primary process (e.g., ["python", "app.py"]).
  • Use CMD for default arguments (e.g., CMD ["--port", "8080"]).
  • ⚠️ If both ENTRYPOINT and CMD are set, CMD’s args are appended to ENTRYPOINT.


3. Step-by-Step: Writing a Production-Grade Dockerfile

Task: Containerize a Python Flask app with these requirements: - Base image: python:3.9-slim.
- Install dependencies from requirements.txt.
- Copy only the app code (not .git or __pycache__).
- Run as a non-root user.
- Start the app on port 5000.

Prerequisites

  • Docker installed (docker --version).
  • A Flask app with this structure: /my-flask-app
    ├── app.py
    ├── requirements.txt
    └── .dockerignore

Step 1: Create .dockerignore

# .dockerignore
.git
__pycache__
*.pyc
.env

Step 2: Write the Dockerfile

# Dockerfile
# 1. Use a pinned, slim base image
FROM python:3.9.18-slim

# 2. Set environment variables (optional but recommended)
ENV PYTHONDONTWRITEBYTECODE=1 \
PYTHONUNBUFFERED=1 # 3. Create and switch to a non-root user RUN useradd -m appuser && \
mkdir /app && \
chown appuser:appuser /app USER appuser WORKDIR /app # 4. Install dependencies (clean up in the same layer) COPY --chown=appuser:appuser requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt # 5. Copy app code COPY --chown=appuser:appuser . .
# 6. Set the entrypoint and default command ENTRYPOINT ["python"] CMD ["app.py"]

Step 3: Build the Image

docker build -t my-flask-app:1.0 .

Step 4: Run the Container

docker run -p 5000:5000 --name flask-app my-flask-app:1.0

Step 5: Verify

  • Check logs: bash docker logs flask-app
  • Test the app: bash curl http://localhost:5000


4. ? Production-Ready Best Practices


Security

  • Never run as root: Use USER to switch to a non-root user.
  • Minimize layers: Chain RUN commands with && and clean up in the same layer.
  • Scan for vulnerabilities: Use docker scan my-flask-app:1.0 (requires Docker Desktop or Snyk).
  • Avoid secrets in layers: Use docker secret or environment variables (never COPY .env files).

Performance

  • Use multi-stage builds to reduce final image size (e.g., build in one stage, copy only binaries to the final stage).
  • Leverage build cache: Order COPY and RUN commands from least to most frequently changing.
  • Use .dockerignore: Exclude unnecessary files to speed up builds.

Reliability

  • Pin base image versions: Avoid latest or floating tags (e.g., python:3.9python:3.9.18).
  • Use ENTRYPOINT + CMD for flexibility: Allows users to override args (e.g., docker run my-flask-app --port 8080).
  • Test signal handling: Ensure SIGTERM (e.g., docker stop) shuts down gracefully.

Observability

  • Log to stdout/stderr: Avoid writing logs to files (use docker logs or a logging driver).
  • Set HEALTHCHECK: Define a health endpoint (e.g., HEALTHCHECK --interval=30s --timeout=3s CMD curl -f http://localhost:5000/health || exit 1).


5. ⚠️ Common Mistakes & Traps

Mistake Symptom Fix/Prevention
Using latest tag App breaks after a silent base image update. Pin versions (e.g., python:3.9.18-slim).
Running as root Container gets compromised; files are owned by root. Use USER to switch to a non-root user.
Not cleaning up in RUN Image is bloated (e.g., apt cache). Chain commands: RUN apt-get update && apt-get install -y curl && rm -rf /var/lib/apt/lists/*.
COPY . . without .dockerignore Secrets or large files (e.g., node_modules) get copied. Add .dockerignore to exclude unnecessary files.
CMD in shell form SIGTERM doesn’t shut down the app. Use exec form: CMD ["python", "app.py"].
ENTRYPOINT + CMD conflict Container fails to start or ignores args. Use ENTRYPOINT for the executable, CMD for default args.


6. ? Exam/Certification Focus

Typical question patterns:
1. Layer caching: "Which Dockerfile order optimizes build cache?"
- ❌ COPY . .RUN pip install (invalidates cache on every code change).
- ✅ COPY requirements.txt .RUN pip installCOPY . . (cache-friendly).


  1. CMD vs ENTRYPOINT:
  2. "What happens if you run docker run my-image --help?"


    • If ENTRYPOINT ["python"] and CMD ["app.py"], it runs python app.py --help.
    • If only CMD ["python", "app.py"], it runs python --help (overrides CMD).
  3. Security:

  4. "How do you prevent a container from running as root?"
    • USER 1000 (switches to UID 1000).
    • RUN chmod 777 /app (worse than running as root).

Key trap distinctions:
- COPY vs ADD: ADD auto-extracts tar/URLs, but COPY is more predictable.
- Shell form (CMD python app.py) vs exec form (CMD ["python", "app.py"]): Exec form handles signals properly.


7. ? Hands-On Challenge

Task: Fix this broken Dockerfile:


FROM ubuntu:latest
RUN apt-get update && apt-get install -y python3
COPY . /app
CMD python3 /app/app.py

Problems to fix:
1. Uses latest tag.
2. Runs as root.
3. No cleanup after apt-get.
4. No .dockerignore.

Solution:


FROM ubuntu:22.04
RUN apt-get update && \
apt-get install -y --no-install-recommends python3 && \
rm -rf /var/lib/apt/lists/* && \
useradd -m appuser USER appuser WORKDIR /app COPY --chown=appuser:appuser . .
CMD ["python3", "app.py"]

Why it works:
- Pinned Ubuntu version (22.04).
- Cleaned up apt cache in the same layer.
- Runs as non-root user.
- Uses exec form for CMD.


8. ? Rapid-Reference Crib Sheet

Instruction Purpose Example Exam Trap
FROM Base image FROM python:3.9.18-slim ⚠️ Avoid latest in production.
RUN Execute command during build RUN apt-get update && apt-get install -y curl ⚠️ Clean up in the same layer.
COPY Copy files into container COPY --chown=user:user . /app ⚠️ Use .dockerignore.
CMD Default command (overridable) CMD ["python", "app.py"] ⚠️ Use exec form for signals.
ENTRYPOINT Main executable (hard to override) ENTRYPOINT ["python"] ⚠️ CMD args are appended.
USER Switch to non-root user USER 1000 ⚠️ Always set after RUN commands.
WORKDIR Set working directory WORKDIR /app ⚠️ Relative paths start here.
ENV Set environment variables ENV PYTHONUNBUFFERED=1 ⚠️ Overridable at runtime.
HEALTHCHECK Define container health check HEALTHCHECK CMD curl -f http://localhost ⚠️ Fails if exit code ≠ 0.


9. ? Where to Go Next

  1. Dockerfile Best Practices (Official docs).
  2. Multi-Stage Builds (Reduce image size).
  3. Docker Security Cheat Sheet (OWASP).
  4. Kubernetes & Dockerfile Tips (K8s docs).


ADVERTISEMENT