| | |

LFCA 77 🐧 Docker Images vs Containers

The distinction between an image and a container is the foundation of everything Docker does. New users conflate the two, and the confusion persists until they hit a problem that forces them apart: an image that cannot be deleted because a container is using it, a container that loses data because the image is read-only, or a docker ps output that shows nothing when they expect to see something.

The LFCA exam places containers under DevOps Fundamentals, which carries 12–16% of the total weight . The study plan lists “Containers” and “Docker basics: images, containers, Dockerfiles, registries” as core topics . The exam expects you to understand the relationship between the two, how they are created, how they differ at the filesystem level, and how they are managed independently.

Key point: An image is a read-only template that contains an application and everything it needs to run. A container is a running instance of an image with a writable layer on top. The image is the class. The container is the object. The image lives on disk indefinitely. The container has a lifecycle that starts when you run it and ends when you remove it. Multiple containers can be created from the same image, and each one has its own writable layer.


Why the distinction matters

The image and the container are two different things, and Docker treats them differently. Misunderstanding the relationship leads to a set of specific problems.

The data loss problem. A container’s writable layer is ephemeral. When the container is removed, everything written to that layer is lost. Users who store data inside a container are surprised when the data disappears. The solution is volumes, but understanding the problem requires understanding that the container’s filesystem is built on top of the image’s read-only layers .

The image deletion problem. You cannot delete an image while a container created from it exists. Docker refuses with an error: “conflict: unable to delete, image is being used by running container” . The container holds a reference to the image. To delete the image, you must first remove the containers.

The storage problem. Images consume disk space. Containers consume a small amount of additional space for their writable layer. The docker system df command shows both. The docker image prune command removes unused images. The docker container prune command removes stopped containers. They are separate commands because images and containers are separate resources .

The sharing problem. Multiple containers can be created from the same image. Each container has its own writable layer, so changes in one container do not affect the others. The image remains unchanged. This is how Docker achieves density: one image on disk, many containers running from it.

The trade-off. The image-and-container model adds a layer of indirection. You cannot simply run an application the way you would on a virtual machine. You must build an image first, or use an existing one. The indirection is what enables portability and reproducibility. The same image runs identically on any host with Docker installed.


a. The Image

A Docker image is a read-only, layered filesystem. Each layer represents a set of filesystem changes — files added, modified, or removed. The layers are stacked, and the container sees the union of all of them .

The layered structure is what makes images efficient. If two images share a base layer, that layer is stored once on disk and shared between them. When you pull a new version of an image that only changed one layer, Docker only downloads the changed layer. The rest are reused from the local cache.

An image is built from a Dockerfile. Each instruction in the Dockerfile creates a layer. The FROM instruction selects the base image. The RUN instruction executes a command and commits the result as a new layer. The COPY instruction adds files from the build context. The CMD instruction defines the default command for containers created from the image.

Images are named and tagged. The format is repository:tag. The repository is the name of the image, and the tag is a version identifier. The default tag is latest. An image name like nginx:1.25 refers to the nginx image at version 1.25. The nginx:latest tag refers to the most recent version .

Images are stored in registries. Docker Hub is the default public registry. When you run docker pull nginx, Docker pulls the nginx:latest image from Docker Hub. Private registries can be run for internal images. The docker push command uploads an image to a registry .

The essential image commands are:

docker images                          # List local images
docker pull nginx                      # Pull an image
docker build -t my-app:1.0 .           # Build an image from a Dockerfile
docker tag my-app:1.0 my-app:latest    # Tag an image
docker push my-app:1.0                 # Push an image to a registry
docker rmi my-app:1.0                  # Remove an image
docker image prune                     # Remove unused images

b. The Container

A container is a running instance of an image. When Docker creates a container, it adds a thin writable layer on top of the image’s read-only layers. The container’s process sees the union of all layers: the image layers below and the writable layer above .

The writable layer is where the container stores any filesystem changes. If the process writes a file, the file is stored in the writable layer. If it modifies an existing file from the image, Docker uses a copy-on-write mechanism: the file is copied from the read-only layer to the writable layer, and the modification is applied there. The original file in the image remains unchanged .

This copy-on-write behavior is why containers are fast to start and cheap to create. The image layers are shared. Only the changes consume new space. A container that does nothing but run a process consumes almost no additional disk space beyond the image.

The container’s writable layer is temporary. When the container is removed, the writable layer is discarded. Any data stored there is lost. To persist data, you use a volume or a bind mount. A volume is a directory managed by Docker that is stored outside the container’s filesystem. A bind mount is a directory on the host that is mounted into the container. Both survive the container’s lifecycle .

Containers are named. If you do not provide a name with --name, Docker generates a random one. The name must be unique among containers on the same host. The container ID is a 64-character hexadecimal string that uniquely identifies the container. The first 12 characters are usually sufficient for commands .

The essential container commands are:

docker run nginx                       # Create and start a container
docker ps                              # List running containers
docker ps -a                           # List all containers
docker stop my-nginx                   # Stop a running container
docker start my-nginx                  # Start a stopped container
docker rm my-nginx                     # Remove a stopped container
docker logs my-nginx                   # View container logs
docker exec -it my-nginx bash          # Run a command inside a container
docker inspect my-nginx                # Detailed container information
docker container prune                 # Remove all stopped containers

c. The Relationship in Practice

The relationship between images and containers becomes concrete when you examine how they are created, listed, and removed.

When you run docker run nginx, Docker performs these steps:

  1. Check whether the nginx:latest image exists locally.
  2. If not, pull it from Docker Hub.
  3. Create a new container from the image, adding a writable layer.
  4. Start the container’s main process.
  5. Attach the container’s output to the terminal (unless -d is used).

After the command, the image is on disk. The container is running. The docker images command shows the image. The docker ps command shows the container. They are separate resources with separate commands.

When you remove the container with docker rm, the container and its writable layer are deleted. The image remains. You can create a new container from the same image. The new container starts from the image’s original state, with no memory of the previous container’s changes.

When you remove the image with docker rmi, Docker checks whether any containers were created from it. If a stopped container still references the image, Docker refuses. You must remove the container first. The docker rmi -f flag forces the removal, but it leaves the container in a broken state.

The docker system df command shows the disk usage of images, containers, volumes, and the build cache. The docker system prune command removes unused resources — stopped containers, unused images, unused volumes, and the build cache — in a single command.


Complete Example Session

This session demonstrates the image-and-container relationship through creation, modification, and removal.

# ============================================
# PART 1: PULL AN IMAGE
# ============================================

docker pull alpine

# Output:
# Using default tag: latest
# latest: Pulling from library/alpine
# ...
# Status: Downloaded newer image for alpine:latest

# ============================================
# PART 2: LIST IMAGES
# ============================================

docker images

# Output:
# REPOSITORY   TAG       IMAGE ID       CREATED       SIZE
# alpine       latest    a1b2c3d4e5f6   2 weeks ago   7.8MB

# ============================================
# PART 3: RUN A CONTAINER FROM THE IMAGE
# ============================================

docker run -d --name my-alpine alpine sleep 3600

# The container is created from the image.
# The image remains unchanged.

# ============================================
# PART 4: MODIFY THE CONTAINER
# ============================================

docker exec -it my-alpine sh

# Inside the container:
echo "This file is in the container's writable layer" > /tmp/note.txt
cat /tmp/note.txt
# This file is in the container's writable layer

exit

# The file is stored in the container's writable layer.
# The image is not modified.

# ============================================
# PART 5: PROVE THE IMAGE IS UNCHANGED
# ============================================

# Create a second container from the same image
docker run --rm alpine cat /tmp/note.txt

# Output:
# cat: can't open '/tmp/note.txt': No such file or directory

# The second container does not see the file.
# The file exists only in the first container's writable layer.

# ============================================
# PART 6: REMOVE THE CONTAINER
# ============================================

docker stop my-alpine
docker rm my-alpine

# The container and its writable layer are deleted.
# The file /tmp/note.txt is gone.

# ============================================
# PART 7: PROVE THE IMAGE STILL EXISTS
# ============================================

docker images

# Output:
# REPOSITORY   TAG       IMAGE ID       CREATED       SIZE
# alpine       latest    a1b2c3d4e5f6   2 weeks ago   7.8MB

# The image is unchanged.

# ============================================
# PART 8: TRY TO REMOVE THE IMAGE WHILE CONTAINER EXISTS
# ============================================

# Create a container
docker run -d --name test-container alpine sleep 3600

# Try to remove the image
docker rmi alpine

# Output:
# Error response from daemon: conflict: unable to delete a1b2c3d4e5f6
# (must be forced) - image is being used by stopped container test-container

# ============================================
# PART 9: REMOVE THE CONTAINER FIRST, THEN THE IMAGE
# ============================================

docker stop test-container
docker rm test-container
docker rmi alpine

# Output:
# Untagged: alpine:latest
# Deleted: sha256:a1b2c3d4e5f6...

# ============================================
# PART 10: CHECK DISK USAGE
# ============================================

docker system df

# Output:
# TYPE            TOTAL     ACTIVE    SIZE      RECLAIMABLE
# Images          3         1         1.2GB     800MB (66%)
# Containers      2         1         0B        0B
# Local Volumes   1         0         0B        0B
# Build Cache     0         0         0B        0B

The ten parts cover pulling an image, listing images, running a container, modifying the container, proving the image is unchanged, removing the container, proving the image still exists, trying to remove the image while a container exists, removing the container first and then the image, and checking disk usage.


Quick Reference

The Image and Container

AspectImageContainer
NatureRead-only templateRunning instance
FilesystemLayered, read-onlyWritable layer on top
LifecyclePersistent on diskCreated → running → stopped → removed
Deletiondocker rmidocker rm
Listingdocker imagesdocker ps -a
Creationdocker build or docker pulldocker run or docker create
ModificationNew buildWritable layer (ephemeral)

The Image Commands

CommandPurpose
docker imagesList local images
docker pull <image>Pull from registry
docker build -t <name> .Build from Dockerfile
docker tag <src> <dst>Tag an image
docker push <image>Push to registry
docker rmi <image>Remove an image
docker image pruneRemove unused images

The Container Commands

CommandPurpose
docker run <image>Create and start
docker psList running containers
docker ps -aList all containers
docker stop <container>Stop a container
docker start <container>Start a stopped container
docker rm <container>Remove a container
docker container pruneRemove stopped containers

The Filesystem Layers

LayerContentsPersistence
Image layersApplication, dependencies, base OSPermanent (until image removed)
Writable layerRuntime changesEphemeral (until container removed)
VolumePersistent dataUntil volume removed

Best Practices

✅ Do This:

# Use meaningful image tags
docker build -t my-app:1.0 .                                 # ✅
# Use volumes for persistent data
docker run -v my-data:/data my-app                            # ✅
# Clean up stopped containers
docker container prune                                        # ✅
# Remove unused images
docker image prune -a                                         # ✅
# Check disk usage before pruning
docker system df                                              # ✅

❌ Don’t Do This:

# Don't store data in the container's writable layer
docker exec my-app sh -c "echo data > /app/data.txt"          # ❌ lost on removal
# Don't force-remove images with dependent containers
docker rmi -f my-app                                          # ❌ broken containers
# Don't use the latest tag in production
FROM nginx:latest                                             # ❌ unpredictable
# Don't leave stopped containers indefinitely
# They consume disk space and clutter docker ps -a.           # ❌

Common Pitfalls

PitfallWhy It HappensFix
Data lost after container removalData stored in writable layerUse volumes
Cannot remove imageContainer still references itRemove containers first
Image rebuilt every timeNo layer cachingOrder Dockerfile instructions correctly
Disk fullUnused images and containersdocker system prune
Container changes not persistedWritable layer is ephemeralUse volumes or build a new image
docker ps shows nothingContainer stoppedUse docker ps -a

Real-World Examples

1. Pull an Image

docker pull postgres:16

2. Build an Image

docker build -t my-app:1.0 .

3. Tag an Image

docker tag my-app:1.0 my-registry/my-app:1.0

4. Push an Image

docker push my-registry/my-app:1.0

5. Run a Container

docker run -d --name db -p 5432:5432 postgres:16

6. Use a Volume

docker run -d --name db -v pgdata:/var/lib/postgresql/data postgres:16

7. Inspect a Container

docker inspect db

8. View Logs

docker logs db

9. Remove a Container

docker rm db

10. Remove an Image

docker rmi postgres:16

Visual

The Image and Container

┌──────────────────────────────────────────────┐
│  IMAGE                                       │
│    ┌────────────────────────────────────┐    │
│    │  Layer: CMD ["node", "server.js"]  │    │
│    ├────────────────────────────────────┤    │
│    │  Layer: COPY . .                   │    │
│    ├────────────────────────────────────┤    │
│    │  Layer: RUN npm install            │    │
│    ├────────────────────────────────────┤    │
│    │  Layer: FROM node:20-alpine        │    │
│    └────────────────────────────────────┘    │
│                                              │
│  Read-only. Stored on disk.                  │
│                                              │
│  CONTAINER                                   │
│    ┌────────────────────────────────────┐    │
│    │  Writable layer                    │    │
│    ├────────────────────────────────────┤    │
│    │  Image layers (read-only)          │    │
│    └────────────────────────────────────┘    │
│                                              │
│  Writable layer is ephemeral.                │
│                                              │
└──────────────────────────────────────────────┘

The Copy-on-Write Mechanism

┌──────────────────────────────────────────────┐
│  COPY-ON-WRITE                               │
│                                              │
│  Before write:                               │
│  Writable layer: (empty)                     │
│  Image layer:    /etc/nginx/nginx.conf       │
│                                              │
│  After write to /etc/nginx/nginx.conf:       │
│  Writable layer: /etc/nginx/nginx.conf (new) │
│  Image layer:    /etc/nginx/nginx.conf (orig)│
│                                              │
│  The original file in the image is unchanged.│
│  The modified file is in the writable layer. │
│                                              │
└──────────────────────────────────────────────┘

The Lifecycle of Images and Containers

┌──────────────────────────────────────────────┐
│  LIFECYCLE                                   │
│                                              │
│  Image:                                      │
│    docker build ──> Image exists on disk     │
│    docker pull  ──> Image exists on disk     │
│    docker rmi   ──> Image removed            │
│                                              │
│  Container:                                  │
│    docker run   ──> Container created + started│
│    docker stop  ──> Container stopped        │
│    docker rm    ──> Container removed        │
│                                              │
│  The image outlives the container.           │
│  The container depends on the image.         │
│                                              │
└──────────────────────────────────────────────┘

The Disk Usage

┌──────────────────────────────────────────────┐
│  docker system df                            │
│                                              │
│  TYPE            SIZE      RECLAIMABLE       │
│  Images          1.2GB     800MB (66%)       │
│  Containers      0B        0B                │
│  Local Volumes   0B        0B                │
│  Build Cache     0B        0B                │
│                                              │
│  Images dominate disk usage.                 │
│  Reclaimable = unused images.                │
│  docker image prune -a removes them.         │
│                                              │
└──────────────────────────────────────────────┘

Summary

ItemValue
ImageRead-only template
ContainerRunning instance of an image
Image filesystemLayered, read-only
Container filesystemWritable layer on top of image layers
Image persistencePermanent until removed
Container persistenceEphemeral until removed
Image listingdocker images
Container listingdocker ps -a
Image removaldocker rmi
Container removaldocker rm
Data persistenceVolumes (not the writable layer)
Disk usagedocker system df
Cleanupdocker system prune
LFCA weightDevOps Fundamentals, 12–16%

Key takeaways:

  • An image is a read-only template; a container is a running instance of an image. The image is the class. The container is the object. The image lives on disk indefinitely. The container has a lifecycle that starts when you run it and ends when you remove it.
  • The container’s writable layer is ephemeral. Any data written to the container’s filesystem is lost when the container is removed. To persist data, use a volume or a bind mount. The writable layer is for runtime changes, not for storage.
  • Copy-on-write makes containers efficient. When a container modifies a file from the image, the file is copied to the writable layer, and the modification is applied there. The original file in the image remains unchanged. This is why multiple containers can share the same image without interfering with each other.
  • You cannot remove an image while a container references it. Docker refuses with a “conflict” error. To remove the image, you must first remove the containers created from it.
  • Images and containers have separate commands. docker images lists images. docker ps -a lists containers. docker rmi removes images. docker rm removes containers. The commands are distinct because the resources are distinct.
  • Disk usage is dominated by images. The docker system df command shows the breakdown. The docker image prune command removes unused images. The docker container prune command removes stopped containers. Both are needed to reclaim disk space.
  • The image-and-container model is what enables portability. The same image runs identically on any host with Docker installed. The container is the runtime instance. The image is the artifact that is built once and deployed everywhere.

Remember: An image is a template. A container is an instance. The image is read-only and persistent. The container is writable and ephemeral. The image is built once and pulled many times. The container is created, runs, stops, and is removed. Data stored in the container is lost when the container is removed. Data stored in a volume persists. The two resources have separate commands, separate lifecycles, and separate disk usage. Understanding the distinction is the foundation of everything Docker does.


Stop using slow, ad-bloated tool sites! 🤮

🔎 Search “KandZ Tools” on Google to use many professional utilities for free.

KandZ.me is the ultimate minimalist hub for:
✅ Finance (Mortgage, Interest, Inflation)
✅ Tech (Base64, JSON, Dev Suite, IP)
✅ Health (BMI, BMR, TDEE)
✅ Productivity (Timer, Workspace, QR)

⚡️ Fast & Private
🔒 No data leaves your device
💎 100% Free

🔗 Use it now: https://tools.kandz.me
🔖 Bookmark it—you’ll need it later!