Working with Docker Images
Working with Docker Images
Docker images are the blueprints for containers. This chapter explores how images work, how to find and pull them, and how to manage them effectively on your system.
What Is a Docker Image?
A Docker image is a read-only template containing instructions for creating a container. Images include:
- A base operating system or runtime
- Application code and dependencies
- Configuration files
- Environment variables
- Default commands to run
Note
Think of an image as a snapshot or template. You can create many containers from a single image, just like you can create many instances from a class in object-oriented programming.
Image Layers
Docker images are built in layers. Each layer represents a set of filesystem changes:
┌─────────────────────────────────────┐
│ Layer 5: CMD ["npm", "start"] │ ← Metadata layer
├─────────────────────────────────────┤
│ Layer 4: COPY . . │ ← Application code
├─────────────────────────────────────┤
│ Layer 3: RUN npm install │ ← Dependencies
├─────────────────────────────────────┤
│ Layer 2: COPY package.json ./ │ ← Package file
├─────────────────────────────────────┤
│ Layer 1: FROM node:20-alpine │ ← Base image
└─────────────────────────────────────┘Layer Benefits
Reusability: Common layers are shared between images. If you have 10 Node.js apps using node:20-alpine, that base layer is stored only once.
Caching: When building images, unchanged layers are cached. Only modified layers need to be rebuilt.
Efficiency: Downloading an image only requires fetching layers you don't already have.
# When pulling an image, Docker shows layer progress
$ docker pull node:20-alpine
20-alpine: Pulling from library/node
4abcf2066143: Already exists # Shared layer
5c95156aab8b: Pull complete
...Layer Sharing
# Two images might share the same base layers:
Image A (node-app:1.0) Image B (node-app:2.0)
├── app code v1.0 ├── app code v2.0
├── npm dependencies ├── npm dependencies ← Same if unchanged
├── node:20-alpine ├── node:20-alpine ← Shared
└── alpine:3.19 └── alpine:3.19 ← Shared
# Only the different layers need additional storageFinding Images
Docker Hub
Docker Hub is the default public registry with millions of images:
# Search from command line
docker search nginx
# Search with filters
docker search --filter is-official=true nginx
docker search --filter stars=100 pythonSearch Results
$ docker search --filter is-official=true postgres
NAME DESCRIPTION STARS OFFICIAL
postgres The PostgreSQL object-relational database sy… 13245 [OK]Image Types on Docker Hub
| Type | Description | Example |
|---|---|---|
| Official | Maintained by Docker or vendors | nginx, postgres, node |
| Verified Publisher | From verified organizations | bitnami/nginx |
| Community | User-contributed images | username/myapp |
Warning
Only use official or verified publisher images for production. Community images may contain outdated software, security vulnerabilities, or even malicious code.
Reading Image Documentation
Each image on Docker Hub has documentation explaining:
- Supported tags and versions
- How to use the image
- Environment variables
- Volume mount points
- Default commands
Pulling Images
Download images from a registry:
# Pull the latest version
docker pull nginx
# Pull a specific version (tag)
docker pull nginx:1.25
# Pull from a specific registry
docker pull gcr.io/google-samples/hello-app:1.0
# Pull all tags (rarely needed)
docker pull -a nginxUnderstanding Image Tags
Tags identify specific versions of an image:
# Image name format
registry/repository:tag
# Examples
nginx:latest # Latest version (default)
nginx:1.25 # Version 1.25
nginx:1.25.4 # Specific patch version
nginx:1.25-alpine # Alpine-based variant
node:20-slim # Slim Debian variant
python:3.12-bookworm # Specific Debian versionNote
Always specify explicit version tags in production. The latest tag can
change without notice, breaking your deployments.
Common Tag Patterns
| Pattern | Meaning | Example |
|---|---|---|
latest | Most recent build (avoid in prod) | nginx:latest |
X.Y.Z | Semantic version | node:20.11.0 |
X.Y | Minor version (gets patch updates) | node:20.11 |
X | Major version (gets minor updates) | node:20 |
alpine | Alpine Linux base (smaller) | node:20-alpine |
slim | Minimal Debian (smaller) | python:3.12-slim |
bookworm | Debian version name | python:3.12-bookworm |
Managing Local Images
Listing Images
# List all images
docker images
# Alternative command
docker image ls
# Show all images (including intermediate layers)
docker images -a
# Show only image IDs
docker images -q
# Filter images
docker images --filter "dangling=true"
docker images --filter "before=nginx:1.24"
docker images nodeImage List Output
$ docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
node 20-alpine abcd1234ef56 2 days ago 182MB
nginx 1.25 7890abcd1234 1 week ago 187MB
postgres 16 ef56abcd7890 2 weeks ago 432MBImage Details
# Detailed image information
docker inspect nginx:1.25
# View image history (layers)
docker history nginx:1.25
# Show only layer sizes
docker history --no-trunc nginx:1.25Removing Images
# Remove an image
docker rmi nginx:1.25
# Remove by image ID
docker rmi abcd1234ef56
# Force remove (even if containers use it)
docker rmi -f nginx:1.25
# Remove multiple images
docker rmi nginx redis postgres
# Remove dangling images (untagged)
docker image prune
# Remove all unused images
docker image prune -a
# Remove all images (careful!)
docker rmi $(docker images -q)Warning
You cannot remove an image if a container (running or stopped) is using it.
Stop and remove the container first, or use -f to force removal.
Image Variants
Most official images offer multiple variants:
Full Images
Based on Debian/Ubuntu with common tools:
# Full Debian-based image
docker pull python:3.12
# Includes: bash, apt, common utilities
# Size: ~1GBSlim Images
Minimal Debian with reduced packages:
# Slim variant
docker pull python:3.12-slim
# Smaller but still Debian-based
# Size: ~150MBAlpine Images
Based on Alpine Linux, extremely small:
# Alpine variant
docker pull python:3.12-alpine
# Minimal, uses musl libc instead of glibc
# Size: ~50MBImage Size Comparison
| Image | Size | Use Case |
|---|---|---|
python:3.12 | ~1 GB | Full development environment |
python:3.12-slim | ~150 MB | Production without extras |
python:3.12-alpine | ~50 MB | Minimal production images |
Note
Alpine uses musl libc instead of glibc, which can cause compatibility issues with some packages. Test thoroughly if using Alpine for complex applications.
Working with Image Tags
Tagging Images
Create additional tags for images:
# Tag an existing image
docker tag nginx:1.25 myregistry.com/nginx:1.25
docker tag nginx:1.25 myregistry.com/nginx:latest
# Tag with a new name
docker tag myapp:latest myapp:v1.0.0
docker tag myapp:latest production/myapp:v1.0.0Tag Best Practices
# Use semantic versioning
myapp:1.0.0
myapp:1.0
myapp:1
# Include build information
myapp:1.0.0-build.123
myapp:1.0.0-abc1234 # Git commit hash
# Environment-specific tags
myapp:1.0.0-dev
myapp:1.0.0-staging
myapp:1.0.0-prodSaving and Loading Images
Export Images to Files
# Save an image to a tar archive
docker save -o nginx.tar nginx:1.25
# Save multiple images
docker save -o images.tar nginx:1.25 redis:7 postgres:16
# Compress while saving
docker save nginx:1.25 | gzip > nginx.tar.gzImport Images from Files
# Load an image from a tar archive
docker load -i nginx.tar
# Load from compressed file
gunzip -c nginx.tar.gz | docker load
# Load from stdin
cat nginx.tar | docker loadNote
Use docker save/load to transfer images between machines without a registry.
This is useful for air-gapped environments or quick transfers.
Export vs Save
There's a difference between docker save and docker export:
# docker save: Saves an image with layers and metadata
docker save myapp:1.0 > myapp-image.tar
# docker export: Saves a container's filesystem (flat, no layers)
docker export mycontainer > mycontainer-fs.tarImage Digests
Digests provide immutable references to specific image builds:
# Pull by digest (immutable reference)
docker pull nginx@sha256:abc123def456...
# View digest of local image
docker inspect --format='{{.RepoDigests}}' nginx:1.25
# View digests when listing
docker images --digestsWhy Use Digests?
# Tags can be overwritten
docker pull myapp:1.0 # Could be different builds on different days
# Digests are immutable
docker pull myapp@sha256:abc123... # Always the exact same image
# Use digests for reproducible deploymentsInspecting Images
Get detailed information about images:
# Full inspection output (JSON)
docker inspect nginx:1.25
# Extract specific information
docker inspect --format='{{.Config.Env}}' nginx:1.25
docker inspect --format='{{.Config.ExposedPorts}}' nginx:1.25
docker inspect --format='{{.Config.Cmd}}' nginx:1.25
# View image architecture
docker inspect --format='{{.Architecture}}' nginx:1.25Image History
See how an image was built:
$ docker history node:20-alpine
IMAGE CREATED CREATED BY SIZE
abc123def456 2 days ago CMD ["node"] 0B
<missing> 2 days ago ENTRYPOINT ["docker-entrypoint.sh"] 0B
<missing> 2 days ago COPY docker-entrypoint.sh /usr/local/bin/ #… 388B
<missing> 2 days ago RUN /bin/sh -c apk add --no-cache --virtual … 7.85MB
...Multi-Architecture Images
Modern images support multiple CPU architectures:
# Docker automatically pulls the right architecture
docker pull nginx:1.25
# Pulls amd64 on Intel/AMD, arm64 on Apple Silicon
# View available architectures
docker manifest inspect nginx:1.25
# Explicitly specify architecture
docker pull --platform linux/amd64 nginx:1.25
docker pull --platform linux/arm64 nginx:1.25Note
Docker Desktop on Apple Silicon (M1/M2/M3) can run both arm64 and amd64 images. The amd64 images run via emulation and may be slower.
Disk Space Management
Images consume significant disk space. Monitor and manage it:
# View Docker disk usage
docker system df
# Detailed breakdown
docker system df -v
# Clean up everything unused
docker system prune
# Clean up everything, including unused images
docker system prune -a
# Clean up with volume removal (careful!)
docker system prune -a --volumesDisk Usage Output
$ docker system df
TYPE TOTAL ACTIVE SIZE RECLAIMABLE
Images 15 5 4.523GB 3.129GB (69%)
Containers 8 3 234.5MB 125.3MB (53%)
Local Volumes 12 4 892.1MB 456.7MB (51%)
Build Cache 45 0 1.234GB 1.234GB (100%)Best Practices
Choose the Right Base Image
# Development: Use full images for debugging
FROM python:3.12
# Production: Use slim or alpine for smaller images
FROM python:3.12-slim
# or
FROM python:3.12-alpinePin Specific Versions
# Bad: Can change unexpectedly
FROM node:latest
# Better: Major version
FROM node:20
# Best: Specific version
FROM node:20.11.0-alpineUse Official Images
# Prefer official images
FROM nginx:1.25-alpine
# Over community images
# FROM some-user/nginx-customRegularly Update Images
# Pull latest versions of your images
docker pull nginx:1.25
docker pull node:20-alpine
# Check for security updates
docker scout cves nginx:1.25Quick Reference
| Command | Purpose |
|---|---|
docker pull | Download an image |
docker images | List local images |
docker rmi | Remove an image |
docker tag | Create a new tag for an image |
docker save | Save image to tar archive |
docker load | Load image from tar archive |
docker inspect | View image details |
docker history | View image layers |
docker image prune | Remove unused images |
docker search | Search Docker Hub |
In the next chapter, you'll learn to create your own images using Dockerfiles.