LFCS 154 ๐ง Docker โ Images and Containers
The previous chapter covered the basic commands: pulling images, running containers, and inspecting the state. This chapter focuses on the lifecycle of the images and the containers: how the image is built, how it is layered, how the container adds the writable layer, and how the data is preserved or lost. The LFCS exam tests the image layer model, the docker commit command, the docker tag command, the docker save and docker load commands, and the distinction between the container filesystem (ephemeral) and the volume (persistent). The critical trap is the container filesystem: everything written inside the container that is not in a volume is lost when the container is removed. The developers who build the application inside the container and then remove the container discover that the build is gone.
Key point: An image is a stack of read-only layers. Each Dockerfile instruction creates a layer. The container adds a single writable layer on top. The writable layer is ephemeral: it is deleted with the container. The data that must survive is stored in a volume or a bind mount. The docker commit command creates a new image from the container’s writable layer. The docker tag command assigns a new name to the existing image. The docker save and docker load commands export and import the images as tar archives.
Why images and containers require a specific mental model
The layer problem. An image is not a single file. It is a stack of layers. The base layer is the operating system (e.g., ubuntu:22.04). The next layer is the package installation. The next layer is the application copy. Each layer is read-only and is shared between the images that use the same base. The docker history <image> command shows the layers. The docker inspect shows the layer diff IDs. The shared layers are the reason that ten images based on ubuntu do not consume ten times the disk space.
The writable layer problem. When the container starts, Docker adds a thin writable layer on top of the image stack. The writes in the container go to this layer. The reads go to the top layer first, then the layers below. The copy-on-write mechanism means that modifying a file from a lower layer copies the file to the top layer first. The writable layer is deleted with the container. The image layers are unchanged.
The persistence problem. The container filesystem is ephemeral. The database data, the application logs, and the user uploads that are written to the container filesystem are lost on the container removal. The volume (docker volume create + docker run -v) or the bind mount (docker run -v /host/path:/container/path) is the persistence mechanism. The volume is managed by Docker; the bind mount is managed by the host.
The commit problem. The docker commit <container> <image> command creates a new image from the container’s writable layer. The image has the container’s state at the time of the commit. The command is useful for the debugging and the experimental changes, but it is not the recommended way to build the images. The Dockerfile is the reproducible, version-controlled, and reviewable way.
The tag problem. The docker tag <source> <target> command assigns a new name to the existing image. The image ID does not change. The tag is the label; the image is the content. The same image can have multiple tags (e.g., nginx:latest, nginx:1.25, myregistry/nginx:prod). The docker push uses the tag to determine the registry and the repository.
The save and load problem. The docker save command exports the image to a tar archive. The docker load command imports the image from the archive. The commands are used to transfer the images between the hosts without the registry. The docker export is the different command: it exports the container filesystem, not the image. The docker import imports the filesystem as a new image.
The trade-off. The image layer model is the efficiency and the caching. The Dockerfile builds the layers, and the unchanged layers are reused. The docker commit is the quick fix but breaks the reproducibility. The volume is the persistence but adds the complexity. The save/load is the transfer without the registry but does not preserve the multi-arch or the metadata.
a. The image layers and the docker history
The image is a stack of layers. The docker history shows the layers and the instructions that created them.
# Show the image layers
docker history nginx:1.25
# Inspect the image metadata
docker inspect nginx:1.25 | grep -A 5 "RootFS"
The docker history output has the IMAGE, CREATED, CREATED BY, SIZE, and COMMENT columns. The CREATED BY is the Dockerfile instruction. The SIZE is the layer size. The 0B entries are the metadata-only instructions (ENV, CMD, EXPOSE), which do not create a layer.
The docker inspect RootFS section shows the layer diff IDs. The layer IDs are the content hashes. The identical layers are shared between the images.
b. The container filesystem and the volume
The container adds the writable layer. The writes to the container filesystem are lost when the container is removed. The volume is the persistence.
# Create a volume
docker volume create appdata
# Run a container with the volume
docker run -d --name db -v appdata:/var/lib/postgresql/data postgres:15
# Inspect the volume
docker volume inspect appdata
# Remove the container, the volume is preserved
docker rm -f db
docker volume ls
# appdata still exists
# The volume is removed explicitly
docker volume rm appdata
The docker volume create appdata creates the named volume. The -v appdata:/var/lib/postgresql/data mounts the volume at the container path. The database writes go to the volume, which persists after the container removal.
The bind mount is the alternative.
# Bind mount a host directory
docker run -d --name web -v /var/www/html:/usr/share/nginx/html:ro nginx:1.25
The -v /var/www/html:/usr/share/nginx/html:ro mounts the host directory in the read-only mode. The changes to the host directory are visible in the container. The :ro flag prevents the container from writing to the host.
c. The commit, the tag, and the save/load
The docker commit creates a new image from the container. The docker tag assigns a new name. The docker save and docker load transfer the images.
# Commit the container to an image
docker commit web mynginx:v1
# Tag the image with the registry prefix
docker tag mynginx:v1 myregistry.example.com/mynginx:v1
# Save the image to a tar file
docker save -o mynginx-v1.tar mynginx:v1
# Load the image on another host
docker load -i mynginx-v1.tar
The docker commit web mynginx:v1 captures the container’s writable layer as a new image. The image has the container’s state at the commit time.
The docker tag mynginx:v1 myregistry.example.com/mynginx:v1 assigns the registry-prefixed name. The docker push myregistry.example.com/mynginx:v1 uses the tag to determine the destination.
The docker save -o mynginx-v1.tar mynginx:v1 exports the image to the tar file. The docker load -i mynginx-v1.tar imports the image on the other host. The image is the same, including the layers and the metadata.
The docker export and docker import are the different commands.
# Export the container filesystem (flattened)
docker export web -o web-fs.tar
# Import as a new image
docker import web-fs.tar mynginx-flat:v1
The docker export exports the container filesystem as a single layer. The layer history is lost. The docker import creates the new image from the flattened filesystem. The result is a single-layer image, not the original stack.
Complete Example Session
# ============================================
# PART 1: INSPECT THE IMAGE LAYERS
# ============================================
docker history nginx:1.25
# Lists the layers and the Dockerfile instructions.
# ============================================
# PART 2: RUN THE CONTAINER WITH A VOLUME
# ============================================
docker volume create pgdata
docker run -d --name db -v pgdata:/var/lib/postgresql/data postgres:15
# ============================================
# PART 3: WRITE TO THE CONTAINER FILESYSTEM
# ============================================
docker exec db bash -c "echo 'test' > /tmp/test.txt"
docker exec db cat /tmp/test.txt
# test
# ============================================
# PART 4: REMOVE THE CONTAINER, CHECK THE VOLUME
# ============================================
docker rm -f db
docker volume ls
# pgdata still exists
# ============================================
# PART 5: THE FILESYSTEM IS GONE
# ============================================
# The /tmp/test.txt is gone with the container.
# The /var/lib/postgresql/data is in the volume.
# ============================================
# PART 6: COMMIT THE CONTAINER
# ============================================
docker run -d --name web nginx:1.25
docker exec web bash -c "echo 'modified' > /usr/share/nginx/html/index.html"
docker commit web mynginx:modified
# ============================================
# PART 7: TAG THE IMAGE
# ============================================
docker tag mynginx:modified myregistry.example.com/mynginx:prod
docker images | grep mynginx
# ============================================
# PART 8: SAVE AND LOAD
# ============================================
docker save -o mynginx-modified.tar mynginx:modified
docker load -i mynginx-modified.tar
# ============================================
# PART 9: CLEANUP
# ============================================
docker rm -f web
docker volume rm pgdata
docker rmi mynginx:modified myregistry.example.com/mynginx:prod
The nine parts trace the image and container lifecycle: image history, volume, write, remove, ephemeral filesystem, commit, tag, save/load, and cleanup.
Quick Reference
Image Commands
| Command | Purpose |
|---|---|
docker history <image> | Show the layers |
docker inspect <image> | Image metadata |
docker commit <ctr> <img> | Container โ image |
docker tag <src> <dst> | Assign new name |
docker save -o <file> <img> | Export to tar |
docker load -i <file> | Import from tar |
docker rmi <image> | Remove image |
Container Filesystem
| Command | Purpose |
|---|---|
docker export <ctr> -o <file> | Export filesystem |
docker import <file> <img> | Import filesystem |
docker cp <ctr>:<path> <host> | Copy from container |
Volume Commands
| Command | Purpose |
|---|---|
docker volume create <name> | Create volume |
docker volume ls | List volumes |
docker volume inspect <name> | Volume details |
docker volume rm <name> | Remove volume |
docker volume prune | Remove unused |
Mount Flags
| Flag | Purpose |
|---|---|
-v name:/path | Named volume |
-v /host:/path | Bind mount |
-v /host:/path:ro | Read-only bind |
--mount | Modern alternative |
Best Practices
โ Do This:
docker run -v pgdata:/var/lib/postgresql/data postgres
<!-- Use the volume for the persistence --> # โ
docker tag myapp:v1 myregistry.example.com/myapp:v1
<!-- Tag with the registry prefix before the push --> # โ
docker save -o app.tar myapp:v1
<!-- Use save/load to transfer the images --> # โ
โ Don’t Do This:
# Store the database data in the container filesystem
<!-- The data is lost on the container removal --> # โ
# Rely on docker commit for the production images
<!-- The Dockerfile is the reproducible way --> # โ
# Use docker export for the multi-layer images
<!-- The history and the metadata are lost --> # โ
Common Pitfalls
| Pitfall | Why It Happens | Fix |
|---|---|---|
| Data lost on the removal | Container filesystem is ephemeral | Use the volume |
| Volume not mounted | Wrong path | Check the -v path |
| Image too large | Too many layers | Use the multi-stage build |
| Tag not found on the push | No registry prefix | docker tag with the full path |
| Commit not reproducible | No Dockerfile | Use the Dockerfile |
Real-World Examples
1. Image History
docker history nginx:1.25
2. Create Volume
docker volume create appdata
3. Run with Volume
docker run -v appdata:/data alpine
4. Bind Mount
docker run -v /host/path:/container/path alpine
5. Read-Only Mount
docker run -v /host/path:/container/path:ro alpine
6. Commit
docker commit web myapp:v1
7. Tag
docker tag myapp:v1 myregistry/myapp:v1
8. Save
docker save -o app.tar myapp:v1
9. Load
docker load -i app.tar
10. Prune Volumes
docker volume prune
Visual
Image Layers
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ Container (writable layer) โ
โ โ Writes go here โ
โ โ Lost on the removal โ
โโโโโโโโโโโโโโโโโโโโฌโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ
โผ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ Image (read-only layers) โ
โ โโโ Layer 3: COPY app โ
โ โโโ Layer 2: RUN apt install โ
โ โโโ Layer 1: Base ubuntu โ
โ โ Shared between the images โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
Volume Persistence
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ Container โ
โ /var/lib/postgresql/data โ
โโโโโโโโโโโโโโโโโโโโฌโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ
โ -v pgdata:/...
โผ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ Volume: pgdata (Docker-managed) โ
โ โ Survives the container removal โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
Summary
| Item | Value |
|---|---|
| Image | Read-only layer stack |
| Container writable layer | Ephemeral |
| Persistence | Volume or bind mount |
| Create image from container | docker commit |
| Assign name | docker tag |
| Export image | docker save |
| Import image | docker load |
| Export filesystem | docker export |
| Import filesystem | docker import |
Key takeaways:
- The image is the layer stack. Each Dockerfile instruction creates a layer. The layers are shared between the images. The
docker historyshows the layers and the instructions. - The container adds the writable layer. The writes are in the top layer. The layer is deleted with the container. The data that must survive is in the volume or the bind mount.
- The
docker commitcreates the image from the container. It is useful for the debugging and the quick experiments. The Dockerfile is the reproducible, version-controlled way. - The
docker tagassigns the name. The same image can have multiple tags. The registry prefix is required for the push. - The
docker saveanddocker loadtransfer the images. The export and the import are the different commands: thedocker exportflattens the container filesystem and loses the layer history.
Remember: The image is the build. The container is the run. The writable layer is the ephemeral. The volume is the persistent. The docker commit is the capture. The docker tag is the name. The docker save is the transfer. The exam tests the layer model and the volume persistence.
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!