🐳 Dockerfile Execution Basics
The Recipe Book Analogy
Imagine you’re a master chef. Every dish you create needs a recipe. Without it, you’d forget ingredients, skip steps, or make mistakes.
A Dockerfile is your recipe book for building containers. It tells Docker exactly how to create your application’s home—step by step, ingredient by ingredient.
📖 What is a Dockerfile?
A Dockerfile is a simple text file. It contains instructions that Docker reads from top to bottom. Each instruction creates a new “layer” in your container image.
Think of it like stacking building blocks:
- Each instruction = one block
- All blocks together = your complete container
# This is a Dockerfile
FROM python:3.9
RUN pip install flask
CMD ["python", "app.py"]
That’s it! Three lines. Three blocks. One container.
🏠 FROM: Choosing Your Foundation
Every recipe starts somewhere. You don’t bake a cake from atoms—you start with flour, eggs, butter.
FROM is your starting point. It picks the base image your container will build upon.
FROM ubuntu:22.04
This says: “Start with Ubuntu 22.04 as my foundation.”
Common Base Images
| Image | What it is |
|---|---|
ubuntu |
Full operating system |
alpine |
Tiny, lightweight Linux |
python |
Python pre-installed |
node |
Node.js ready to go |
Example: Starting with Python
FROM python:3.11-slim
python= the base image3.11= the versionslim= a smaller, lighter version
Rule: FROM must be the first instruction in your Dockerfile (except for comments and ARG).
🔨 RUN: Executing Commands
Now you have your foundation. Time to add ingredients!
RUN executes commands inside the container during the build process. It’s like following recipe steps.
FROM ubuntu:22.04
RUN apt-get update
RUN apt-get install -y python3
Each RUN creates a new layer. More layers = bigger image.
Pro Tip: Combine Commands
Instead of many RUN instructions:
# ❌ Creates 3 layers
RUN apt-get update
RUN apt-get install -y python3
RUN apt-get clean
Combine them:
# ✅ Creates 1 layer
RUN apt-get update && \
apt-get install -y python3 && \
apt-get clean
The && chains commands. The \ continues to the next line.
🎬 CMD: The Default Action
Your container is built. It’s ready to run. But what should it DO when someone starts it?
CMD provides the default command that runs when the container starts.
FROM python:3.11
CMD ["python", "--version"]
When you run this container, it prints Python’s version.
Two Ways to Write CMD
Exec form (recommended):
CMD ["python", "app.py"]
Shell form:
CMD python app.py
The exec form is preferred. It runs your command directly without a shell wrapper.
Important: CMD Can Be Overridden
# Uses the Dockerfile's CMD
docker run myimage
# Overrides CMD with "bash"
docker run myimage bash
The user can replace your default command completely.
🚀 ENTRYPOINT: The Main Program
ENTRYPOINT is like CMD’s stubborn sibling. It sets the main program your container runs—and it’s harder to override.
FROM python:3.11
ENTRYPOINT ["python"]
Now your container IS a Python interpreter. Whatever you pass gets sent to Python.
docker run myimage script.py
# Runs: python script.py
docker run myimage -c "print('Hi')"
# Runs: python -c "print('Hi')"
ENTRYPOINT vs Being Overridden
# This won't work as expected
docker run myimage bash
# Runs: python bash (error!)
# You need --entrypoint to override
docker run --entrypoint bash myimage
⚔️ ENTRYPOINT vs CMD: The Dynamic Duo
Here’s where it gets exciting. ENTRYPOINT and CMD work together like peanut butter and jelly.
The Golden Rule
| Instruction | Purpose | Can Override? |
|---|---|---|
| ENTRYPOINT | The main executable | Hard (needs --entrypoint) |
| CMD | Default arguments | Easy (just add args) |
The Perfect Combo
FROM python:3.11
ENTRYPOINT ["python"]
CMD ["app.py"]
What happens:
docker run myimage
# Runs: python app.py
docker run myimage script.py
# Runs: python script.py (CMD overridden)
docker run myimage -c "print('hello')"
# Runs: python -c "print('hello')"
ENTRYPOINT stays. CMD becomes the default argument that users can change.
Visual Flow
graph TD A[Container Starts] --> B{User provides args?} B -->|No| C[ENTRYPOINT + CMD] B -->|Yes| D[ENTRYPOINT + User Args] C --> E[python app.py] D --> F[python user-args]
When to Use What
| Scenario | Use |
|---|---|
| General utility | CMD alone |
| Container as executable | ENTRYPOINT + CMD |
| Wrapper scripts | ENTRYPOINT |
| Flexible defaults | CMD alone |
🎯 Putting It All Together
Here’s a complete Dockerfile that uses everything:
# Start with Python
FROM python:3.11-slim
# Install dependencies
RUN pip install flask gunicorn && \
mkdir /app
# Set the main program
ENTRYPOINT ["gunicorn"]
# Default arguments
CMD ["--bind", "0.0.0.0:8000", "app:app"]
What this creates:
- A Python container
- With Flask and Gunicorn installed
- That runs Gunicorn as its main program
- With sensible default settings
Users can customize:
# Use defaults
docker run myapp
# Custom port
docker run myapp --bind 0.0.0.0:5000 app:app
# Development mode
docker run myapp --reload app:app
📝 Quick Reference
graph TD A[FROM] -->|Base Image| B[RUN] B -->|Build Commands| C[ENTRYPOINT/CMD] C -->|Runtime Command| D[Container Runs]
| Instruction | When it runs | Purpose |
|---|---|---|
| FROM | Build start | Set base image |
| RUN | Build time | Install software |
| CMD | Container start | Default command |
| ENTRYPOINT | Container start | Main executable |
🌟 You Did It!
You now understand the core building blocks of every Dockerfile:
- FROM picks your foundation
- RUN builds your environment
- CMD sets the default action
- ENTRYPOINT defines your main program
- Together, they create powerful, flexible containers
Go build something amazing! 🐳