KAHIBARO
Discord Login Register

21.3. Images

What Is a Docker Image?

A Docker image is a read-only template that contains everything needed to run a piece of software: code, runtime, libraries, system tools, and configuration.

When you start an image, Docker creates a container from it. You can think of:

You build an image once, then run it many times to create many containers.

Common examples:

Important rule:
You never modify a running image.
You modify the container, then if you want to keep those changes, you build a new image.


Image Layers and Union Filesystem

How layering works

Docker images are built in layers. Each instruction in a Dockerfile typically creates a new layer:

These layers stack on top of each other using a union filesystem. To Docker and to your app, they look like a single filesystem.

Example Dockerfile snippet:

dockerfile
FROM python:3.12-slim        # Layer 1
RUN apt-get update           # Layer 2
RUN pip install fastapi      # Layer 3
COPY . /app                  # Layer 4

This creates an image composed of 4 layers.

Why layers are useful

Layers give three big advantages:

  1. Caching builds

If only your code changes, Docker can reuse the base layers:

This makes rebuilds much faster.

  1. Sharing between images

Different images can share common layers.
For example, my-api:1.0 and my-api:1.1 can both reuse the same base Python layer and dependency layer.

  1. Smaller downloads

When pulling a new image from a registry, Docker only downloads layers it does not already have.

Read-only vs writable layers

Inside the container, this looks like a normal filesystem. But under the hood:

Table summary:


ConceptTypeLifetime
Image layerRead-onlyPersistent until you delete image
Container writableWritableExists only while container exists

Image Tags and Naming

Image name format

Images live in registries (like Docker Hub) and have a full name:

[registry/]repository[:tag]

Examples:

Full nameMeaning
pythonrepo python on Docker Hub, default tag latest
python:3.10repo python, tag 3.10
myuser/my-api:1.0.0repo myuser/my-api, tag 1.0.0
ghcr.io/myorg/app:prodregistry ghcr.io, repo myorg/app, tag prod

If you omit the tag, Docker uses :latest.

Important rule:
Do not rely on :latest in production.
Always use a specific tag like :1.0.3 so you know exactly which version you are running.

What is a tag?

A tag is just a label that points to a specific image.

Example:

bash
# Build a local image
docker build -t my-api:1.0.0 .
# Tag the same image as "latest"
docker tag my-api:1.0.0 my-api:latest
# Now both tags point to the same image ID
docker images my-api

Output (simplified):

text
REPOSITORY   TAG     IMAGE ID
my-api       latest  123abc...
my-api       1.0.0   123abc...

Inspecting and Managing Images

Listing images

To see all images on your machine:

bash
docker images
# or
docker image ls

Example output:

text
REPOSITORY    TAG       IMAGE ID       CREATED         SIZE
python        3.12      f8e1c1234abc   2 weeks ago     1.1GB
my-api        1.0.0     a7d9e8765def   5 minutes ago   350MB
postgres      16        9b2cdef12345   3 days ago      400MB

Key columns:

Inspecting image details

If you need deep details about an image:

bash
docker inspect my-api:1.0.0

You will see JSON output that includes:

You can also inspect by image ID:

bash
docker inspect a7d9e8765def

Checking image history

To see which instructions created which layers:

bash
docker history my-api:1.0.0

Example output (simplified):

text
IMAGE          CREATED        CREATED BY
a7d9e8765def   5 minutes ago  COPY . /app
a1b2c3d4e5f6   6 minutes ago  RUN pip install -r requirements.txt
...

This helps you understand:

Building, Pulling, and Pushing Images

Building an image from a Dockerfile

If you have a Dockerfile in the current directory:

bash
docker build -t my-api:1.0.0 .

Explanation:

Common pattern for backend services:

bash
docker build -t myuser/task-api:0.1.0 .

Then run it:

bash
docker run --rm -p 8000:8000 myuser/task-api:0.1.0

Pulling images

To download an image from a registry (often Docker Hub):

bash
docker pull python:3.12-slim
docker pull postgres:16

If you try to run a container from an image that is not present locally, Docker will attempt to pull it automatically:

bash
docker run -it --rm alpine sh
# If "alpine" is not present, Docker pulls it first

Pushing your own images

To share your image or deploy it to another machine, you push it to a registry.

Basic steps with Docker Hub:

  1. Create a repository on Docker Hub (for example myuser/task-api).
  2. Log in from the command line:
bash
   docker login
  1. Tag your image with the full repository name:
bash
   docker tag my-api:0.1.0 myuser/task-api:0.1.0
  1. Push the image:
bash
   docker push myuser/task-api:0.1.0

On a server, you can then pull and run:

bash
docker pull myuser/task-api:0.1.0
docker run -d -p 80:8000 myuser/task-api:0.1.0

Cleaning Up Images and Disk Space

Images and layers can take a lot of disk space, especially during backend development where you rebuild often.

Removing a single image

To remove an image:

bash
docker rmi my-api:0.1.0
# or by image ID
docker rmi a7d9e8765def

If Docker says the image is in use by a container, remove the container first:

bash
docker ps -a        # find the container
docker rm <container-id>
docker rmi my-api:0.1.0

Removing unused images

To remove dangling images (images without tags, usually leftovers from builds):

bash
docker image prune

Docker will ask for confirmation.

To remove all unused images, not just dangling ones:

bash
docker image prune -a

Important rule:
docker image prune -a removes all images not used by any container.
Be careful, you might need to rebuild or re-pull them later.

Cleaning everything

To reclaim space from containers, networks, and images:

bash
docker system prune
# more aggressive:
docker system prune -a

These commands are helpful on development machines after many experiments.


Practical Examples for Backend Developers

Example 1: Using an official image as a base

You want to create a FastAPI backend image using Python 3.12 slim:

dockerfile
FROM python:3.12-slim   # base image
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY . .
CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8000"]

Build and tag:

bash
docker build -t myuser/fastapi-backend:0.1.0 .

Run:

bash
docker run --rm -p 8000:8000 myuser/fastapi-backend:0.1.0

Example 2: Versioning your backend image

You can use tags to track versions:

bash
# First version
docker build -t myuser/task-api:0.1.0 .
# After changes
docker build -t myuser/task-api:0.2.0 .

Now you can run either:

bash
docker run -d --name task-api-v1 myuser/task-api:0.1.0
docker run -d --name task-api-v2 myuser/task-api:0.2.0

This is very helpful when:

Example 3: Testing with a database image

You can quickly start a PostgreSQL database using an image:

bash
docker pull postgres:16
docker run -d \
  --name my-postgres \
  -e POSTGRES_PASSWORD=secret \
  -p 5432:5432 \
  postgres:16

Your backend can connect to this local database for development or tests.


Summary

Understanding images is essential for packaging and shipping backend applications in a consistent, reproducible way.

Views: 6

Comments

Please login to add a comment.

Don't have an account? Register now!