DevOps

Docker Images and Containers Explained: Understanding the Foundation EP03

KaveeshaGMar 25, 202611 min read
Docker Images and Containers Explained: Understanding the Foundation EP03

Master the relationship between images and containers, and understand what’s really happening under the hood

Welcome to week three of our Docker series! You’ve installed Docker and run your first containers. Now it’s time to understand what’s actually happening when you execute those commands. This is where Docker starts to click for most people.

Today, we’re demystifying the two most fundamental concepts in Docker—images and containers—and how they work together.

The Image-Container Relationship: A Clear Mental Model

Let’s start with the simplest explanation that clarifies everything:

An image is a blueprint. A container is a running instance of that blueprint.

Think of it like this:

  • Image = Class (in programming terms)
  • Container = Object/Instance (of that class)

Or if you prefer a real-world analogy:
  • Image = Recipe for chocolate cake
  • Container = The actual cake you bake from that recipe
You can bake multiple cakes (containers) from the same recipe (image). Each cake is independent — frosting one doesn’t affect the others. If you mess up a cake, you can throw it away and bake a new one from the same recipe.
This is exactly how Docker works.

What Is a Docker Image, Really?

A Docker image is a read-only template that contains:
  • Application code — Your program files
  • Runtime environment — Node.js, Python, Java, etc.
  • System libraries — dependencies your code needs
  • Environment variables — Configuration settings
  • Commands — What to run when the container starts
The images are:
  • Immutable — Once created, they never change
  • Portable — Works the same on any Docker host
  • Layered—Built in stackable layers (more on this soon)
  • Shareable — Can be distributed via registries

What Is a Docker Container, Really?

A container is a runnable instance of an image. When you start a container, Docker:

1. Takes the image (read-only layers)
2. Adds a thin writable layer on top
3. Allocates resources (CPU, memory, networking)
4. Runs the specified command
5. Isolates it from other containers and the host
Containers are:

  • Ephemeral – Designed to be disposable
  • Isolated – Have their own filesystem, network, processes
  • Lightweight – Share the host OS kernel
  • Stateful – Changes are stored in the writable layer

The Magic of Layers: How Docker Images Work

Here’s where Docker gets really clever. Images aren’t monolithic files — they’re built from layers stacked on top of each other.
Let’s visualize this with an example. Imagine building a Node.js application:
Why layers matter:
1. Efficiency — Layers are reused across images. If ten images use Ubuntu, that layer is stored once
2. Speed — Only changed layers need to be downloaded or rebuilt
3. Caching — Docker caches layers during builds, making rebuilds lightning fast
Let’s see this in action:
# Pull an image and watch the layers download 
docker pull nginx 
# You'll see output like: 
# latest: Pulling from library/nginx 
# a2abf6c4d29d: Pull complete ← Layer 1 
# a9edb18cadd1: Pull complete ← Layer 2 
# 589b7251471a: Pull complete ← Layer 3 
# …

Each line represents a layer being downloaded.

Inspecting Images: Looking Under the Hood

Let’s explore an image in detail:

# Pull an image
docker pull alpine

# View detailed information
docker image inspect alpine

This shows you everything about the image: layers, environment variables, exposed ports, entry points, and more.

For a more readable view of layers:

docker history alpine

You’ll see something like the following:

IMAGE          CREATED       CREATED BY                                      SIZE
c1aabb73d233   2 weeks ago   /bin/sh -c #(nop)  CMD ["/bin/sh"]              0B
<missing>      2 weeks ago   /bin/sh -c #(nop) ADD file:abc123... in /       7.05MB

Each row is a layer, showing what command created it and its size.

The Container Lifecycle: From Creation to Deletion

Containers move through several states during their lifetime:
Created → Running → Paused → Stopped → Removed
Let’s explore each state with commands:

Creating a Container

# Create a container without starting it
docker create --name my-nginx nginx

# Check its status
docker ps -a
# STATUS: Created

Running a Container

# Start the container
docker start my-nginx

# Or create and start in one command
docker run --name my-nginx2 nginx

# Check status
docker ps
# STATUS: Up

Pausing a Container

# Pause a running container (freezes all processes)
docker pause my-nginx

# Unpause it
docker unpause my-nginx

Stopping a Container

# Gracefully stop (sends SIGTERM, waits, then SIGKILL)
docker stop my-nginx

# Force stop immediately
docker kill my-nginx

Removing a Container

# Remove a stopped container
docker rm my-nginx

# Force remove a running container
docker rm -f my-nginx

Hands-On Exercise: Understanding Image Layers

Let’s see layer efficiency in action:

Step 1: Pull two related images

docker pull ubuntu:20.04
docker pull ubuntu:22.04

Watch how some layers are shared! You’ll see “Already exists” for common layers.

Step 2: Check the disk space used

docker system df

Notice that the total space used is less than the sum of both images. That’s layer sharing at work!

Step 3: Examine the layers

docker history ubuntu:20.04
docker history ubuntu:22.04

Compare the layers — you’ll see similarities and differences.

Hands-On Exercise: Container Modifications

Let’s understand the writable layer:

Step 1: Run an Ubuntu container interactively

docker run -it --name test-container ubuntu bash

Step 2: Make changes inside the container

# Create a file
echo "Hello from container" > /hello.txt

# Install something
apt-get update
apt-get install -y curl

# Exit
exit

Step 3: Check the changes

# See what changed in the container
docker diff test-container

You’ll see:

  • A = Added files
  • C = Changed files
  • D = Deleted files

Step 4: Start the container again

docker start test-container
docker exec -it test-container bash
# Your file is still there!
cat /hello.txt
exit

Step 5: Create a new container from the same image

docker run -it --name test-container2 ubuntu bash
# Try to read the file
cat /hello.txt
# It doesn't exist! Each container has its own writable layer
exit

Cleanup:

docker rm -f test-container test-container2

Finding Images: Docker Hub and Beyond

Docker Hub is like GitHub for Docker images — a public registry where you can find images for almost anything.

Searching for Images

Via command line:

docker search python

Via Docker Hub website: Visit hub.docker.com and search for what you need.

Understanding Image Tags

Images have tags to identify different versions:

# Pull specific versions
docker pull python:3.11
docker pull python:3.10
docker pull python:latest  # Usually the newest stable version

# Pull by digest (exact version, immutable)
docker pull nginx@sha256:abc123...

Best practices:

  • Avoid latest in production – it changes over time
  • Use specific versionspython:3.11.4 instead of python:3.11
  • Check official images — Look for the “Official Image” badge

Understanding Official vs Community Images

Official Images:
  • Maintained by Docker or the software vendor
  • Thoroughly reviewed and tested
  • Use simple names like nginx, python, redis
  • Recommended for production use
Community Images:

  • Created by users like you
  • Include username like user/my-image
  • Varying quality — check downloads and stars
  • Review the Dockerfile before using

Working with Multiple Containers from the Same Image

One powerful Docker feature is running multiple containers from the same image:

# Run three nginx containers on different ports
docker run -d -p 8081:80 --name web1 nginx
docker run -d -p 8082:80 --name web2 nginx
docker run -d -p 8083:80 --name web3 nginx

# Check them all
docker ps

# Access them
# http://localhost:8081
# http://localhost:8082
# http://localhost:8083

# Clean up
docker rm -f web1 web2 web3

Each container is completely isolated. They all use the same image (stored once), but have separate writable layers.

Container Names and IDs: What’s the Difference?

Every container has:

  • A Container ID (unique, auto-generated): a1b2c3d4e5f6
  • A Name (human-readable, optional): my-web-server

You can use either to reference containers:

# Using ID
docker stop a1b2c3d4e5f6

# Using name
docker stop my-web-server

# Docker accepts shortened IDs (first 3+ chars)
docker stop a1b

Pro tip: Always name your containers in production for easier management.

docker run -d --name production-api my-api-image

Understanding Image and Container Storage

Where does Docker store all this data?

Linux: /var/lib/docker/  Windows: C:\ProgramData\DockerDesktop macOS: Inside the Docker Desktop VM

You generally don’t need to access these directly — use Docker commands instead.

Check storage usage:

# See space used by images, containers, volumes
docker system df

# Detailed view
docker system df -v

Common Confusions Clarified

If I delete an image, do my containers stop?” No! Containers continue running. The image data is still there; Docker just untagged it. You can’t delete an image while containers from it exist.

If I stop a container, is my data lost?” No! A stopped container preserves all changes in its writable layer. Data is only lost when you remove the container with docker rm.

Can I run the same container on multiple ports?” No. One container, one port mapping. But you can run multiple containers from the same image on different ports.

Why does pulling an image take so long the first time but is fast after?” Docker caches layers. The first pull downloads everything. Subsequent pulls of similar images reuse cached layers.

Quick Reference: Essential Commands

# Images
docker images                    # List images
docker pull <image>              # Download image
docker image inspect <image>     # Detailed image info
docker history <image>           # View image layers
docker rmi <image>               # Remove image

# Containers
docker ps                        # List running containers
docker ps -a                     # List all containers
docker create <image>            # Create container
docker start <container>         # Start container
docker stop <container>          # Stop container
docker restart <container>       # Restart container
docker rm <container>            # Remove container
docker diff <container>          # See container changes
docker exec -it <container> bash # Execute command in container

# System
docker system df                 # Show disk usage
docker system prune              # Clean up unused resources

Resources for Deeper Learning


Share this story:

Comments (...)

You must be signed in to join the discussion.

Loading comments...