Lesson 3 of 9 ↓
- 1.Docker Comprehensive Guide: From Zero to Hero
- 2.Docker Environment Variables: the Ultimate Guide
- 3.Docker Images: the Ultimate Comprehensive Guide
- 4.Docker: Cmd vs. Entrypoint - the Comprehensive Guide
- 5.Docker Networking: the Ultimate Deep Dive
- 6.Docker Storage & Filesystem: the Complete Reference
- 7.Docker Compose: the Ultimate Master Guide
- 8.Docker Registry: the Ultimate Master Guide
- 9.Docker Engine: the Ultimate Deep Dive
8 min read
Docker Images: the Ultimate Comprehensive Guide
This document is designed to be the only reference you will ever need for Docker Images. It covers concepts, creation, building, management, and advanced distribution techniques in extreme detail.
What is a Docker Image?
The Concept
Think of a Docker Image as a frozen snapshot of an application. It contains everything the app needs to run:
-
The Code.
-
The Runtime (e.g., Python, Node.js, Java).
-
System Tools & Libraries.
-
Settings & Configurations.
Image vs. Container
-
Image: The Class (in programming). It is read-only. You cannot change it once built.
-
Container: The Object (Instance). It is the running version of the image. You can write data to it.
Analogy:
Image: A recipe for a cake.
Container: The actual cake you baked. You can eat the cake, but eating it doesn't change the recipe on the paper.
The dockerfile: Writing the Recipe
A Dockerfile is a text file (no extension) containing instructions. Docker reads this file top-to-bottom to build the image.
Core Instructions (the Building Blocks)
from (the Foundation)
Command: FROM <image>:<tag>
-
Description: Defines the starting point. Every Dockerfile starts here.
-
Example:
FROM python:3.9-slim -
Why specific tags? Using
slimoralpinereduces the image size significantly. Avoid usinglatestin production to prevent unexpected updates.
workdir (the Navigation)
Command: WORKDIR /path/to/dir
-
Description: Sets the "current directory" inside the image.
-
Behavior: If the directory doesn't exist, Docker creates it.
-
Importance: It keeps your file system organized. It's better than running
RUN cd /app.
copy vs add (getting Files In)
-
COPY <src> <dest>-
Description: Copies files from your computer to the image.
-
Example:
COPY . .(Copy all files from current folder on PC to current folder in image). -
Recommendation: Use this 99% of the time.
-
-
ADD <src> <dest>-
Description: Like COPY, but has superpowers:
-
Can download files from a URL.
-
Can unzip
.tar.gzfiles automatically.
-
-
Recommendation: Use only if you specifically need these features.
-
run (the Builder)
Command: RUN <linux_command>
-
Description: Runs a command during the build process.
-
Use Cases: Installing libraries, updating OS, setting permissions.
-
Example:
RUN pip install flask -
Note: Each
RUNcommand creates a permanent layer in the image.
env (the Configuration)
Command: ENV KEY=VALUE
-
Description: Sets environment variables that persist when the container runs.
-
Example:
ENV APP_ENV=production
expose (the Documentation)
Command: EXPOSE <port>
-
Description: A note to the developer that this app listens on a specific port.
-
Important: It does NOT publish the port. You still need
-pwhen runningdocker run.
user (the Security)
Command: USER <username>
-
Description: Switches to a non-root user for security.
-
Example:
USER appuser
cmd vs entrypoint (the Startup Logic)
Both tell the container what to do when it starts. The difference is how they handle arguments.
cmd (the Default)
-
Purpose: Provides defaults.
-
Behavior: Easily overridden.
-
Example:
CMD ["python", "app.py"]-
docker run myapp-> Runspython app.py. -
docker run myapp echo hello-> Runsecho hello(Ignores Python).
-
entrypoint (the Executable)
-
Purpose: Makes the container run like a binary executable.
-
Behavior: Hard to override. Arguments are appended.
-
Example:
ENTRYPOINT ["echo"]docker run myapp Hello-> Runsecho Hello.
The Best Practice (combine Them)
ENTRYPOINT ["python", "app.py"]
CMD ["--help"]-
If you run
docker run myapp, it executes:python app.py --help. -
If you run
docker run myapp --version, it executes:python app.py --version.
Building Images: docker Build
The command that turns your Dockerfile into an image.
Command:
docker build -t myapp:v1 .Important Arguments Breakdown
| Argument | Full Name | Explanation | Is it Important? |
|---|---|---|---|
-t |
--tag |
Names the image (name:tag). If skipped, you get a random ID. |
Critical |
. |
Context | Tells Docker where to find files for COPY. It means "Current Directory". |
Critical |
-f |
--file |
Points to a specific Dockerfile name (e.g., -f Dockerfile.dev). |
Useful |
--no-cache |
Forces Docker to rebuild all layers from scratch. Useful if apt-get update is stuck on old data. |
Advanced | |
--build-arg |
Passes variables meant only for the build phase (not runtime). | Advanced |
Managing Images on Your Machine
Listing Images
Command: docker images
-
Details: Shows Repository, Tag, Image ID, Created Date, Size.
-
Argument:
docker images -a(Show all, including intermediate layers). -
Argument:
docker images -q(Quiet: returns only IDs).
Removing Images
Command: docker rmi <image_id_or_name>
-
Argument:
-f(Force): Deletes the image even if it's tagged multiple times or if a stopped container is using it.- Warning: Don't use
-funless you are sure.
- Warning: Don't use
Cleaning Up (pruning)
Over time, your disk fills up with "Dangling Images" (Images with no name <none>:<none>). Command: docker image prune
-
Action: Deletes only dangling images.
-
Argument:
-a(All): Deletes ALL images not currently used by any running container. (Powerful cleanup).
Inspecting Image Details
Command: docker inspect <image_name>
-
Output: A JSON object.
-
Why use it? To see the default environment variables, the entrypoint, the OS architecture, and layers.
Viewing Image History
Command: docker history <image_name>
-
Output: Shows the layers of the image.
-
Why use it? To find out which command (
RUN,COPY) made the image size so big.
Image Distribution (sharing)
Method A: Docker Hub (online Registry)
-
Login:
bash docker login -
Tagging: You must rename the image to include your Docker Hub username.
bash docker tag myapp:v1 username/myapp:v1 -
Pushing:
bash docker push username/myapp:v1
Method B: Offline Transfer (tarball)
This is crucial for "Air-gapped" systems (servers with no internet).
-
Save (Export): Pack the image into a
.tarfile.bash docker save -o myapp.tar myapp:v1-o: Output file path.
-
Transfer: Copy
myapp.tarto the other machine via USB or SCP. -
Load (Import): Load the image into Docker on the new machine.
bash docker load -i myapp.tar-i: Input file path.
Advanced Concept: Layers & Caching
How Layers Work
Docker images are built like a sandwich. Each instruction (RUN, COPY) adds a new layer on top of the previous one.
-
Read-Only: All image layers are read-only.
-
The Container Layer: When you run a container, Docker adds a thin "Read/Write" layer on top.
Docker Build Cache (the Speed Secret)
Docker remembers the result of every line in your Dockerfile.
-
If you change line 5, Docker reuses the layers for lines 1-4 (Instant).
-
It rebuilds line 5 and everything after it.
Optimization Tip: Put things that change often (like your Source Code) at the bottom of the Dockerfile. Put things that rarely change (like pip install requirements) at the top.
Bad Example:
COPY . . # <--- Source code changes often
RUN pip install flask # <--- This will re-run every time code changes! (Slow)Good Example:
COPY requirements.txt .
RUN pip install flask # <--- Cached! Won't run unless requirements change.
COPY . . # <--- Only this runs when code changes. (Fast)Full Real-world Example (line-by-line Explanation)
Let's imagine we are building a Python Web App using Flask.
The App Code (app.py)
This is the simple program we want to run.
# app.py
from flask import Flask
app = Flask(__name__)
@app.route("/")
def hello():
return "Hello from Docker!"
if __name__ == "__main__":
app.run(host="0.0.0.0", port=5000)The Dockerfile
This is the recipe we write to containerize the app above.
# 1. Base Image
FROM python:3.9-slim
# 2. Working Directory
WORKDIR /app
# 3. Dependencies
COPY requirements.txt .
RUN pip install flask
# 4. Source Code
COPY . .
# 5. Documentation
EXPOSE 5000
# 6. Startup Command
CMD ["python", "app.py"]Explanation of the Code (what Each Line Does)
-
FROM python:3.9-slim:-
What it does: Tells Docker to download a pre-built OS that already has Python 3.9 installed.
-
Why
slim? It's a lightweight version (smaller download size) compared to the full Python image.
-
-
WORKDIR /app:-
What it does: Creates a folder named
/appinside the image and moves the "cursor" there. -
Why? So we don't dump our files in the root folder (
/). It keeps things organized.
-
-
COPY requirements.txt .:-
What it does: Copies only the requirements file from your computer to the
/appfolder inside the image. -
Why? We copy this before the rest of the code to use Docker Caching (as explained in Section 7).
-
-
RUN pip install flask:-
What it does: Runs the command to download the Flask library from the internet inside the image.
-
Result: The image now has Python AND Flask installed.
-
-
COPY . .:-
What it does: Copies all your other files (like
app.py) from your computer to the image. -
Why now? Because source code changes often. If we change the code, Docker only rebuilds from this line onwards.
-
-
EXPOSE 5000:-
What it does: Documentation only. It tells the user "Hey, this app runs on port 5000".
-
Note: You still need to use
-p 5000:5000when running the container.
-
-
CMD ["python", "app.py"]:-
What it does: This is the final instruction. It tells the container: "When you start up, run this command automatically".
-
Result: The web server starts listening for requests.
-
Comments
Loading the discussion…