You have a container image. You run it. Now you have a running container. What's the relationship? Is an image just a pre-compressed container? Can you have 100 containers from one image? What happens when a container modifies a file? Does it change the image? This guide explains the relationship between images and containers — how static artifacts become processes, how multiple containers from one image remain independent, and what happens to data when containers stop.
Table of Contents
- The Relationship: Blueprint and Instance
- What Happens When You Run docker run
- The Container Lifecycle
- The Writable Container Layer
- Multiple Containers from One Image
- Container State: What Persists, What Doesn't
- Immutable Images, Mutable Containers
- Container IDs, Names, and Runtime Tracking
- Snapshots and Copying Container State
- Next Steps
The Relationship: Blueprint and Instance
An image is a static artifact — a stack of filesystem layers and metadata. It's immutable. Once built and pushed to a registry, it never changes.
A container is a running (or stopped) instance of an image — a process with an isolated filesystem, network, and resource limits. It can be created, started, stopped, and deleted.
The relationship:
graph TB Image["Image<br/>Static<br/>Immutable<br/>sha256:abc123"] Image -->|docker run| C1["Container 1<br/>Running<br/>Writable layer"] Image -->|docker run| C2["Container 2<br/>Running<br/>Writable layer"] Image -->|docker run| C3["Container 3<br/>Stopped<br/>Preserved state"] C1 -->|Write file| W1["Writable<br/>Container Layer 1"] C2 -->|Write file| W2["Writable<br/>Container Layer 2"] C3 -->|Write file| W3["Writable<br/>Container Layer 3"] style Image fill:#fff9c4 style C1 fill:#c8e6c9 style C2 fill:#c8e6c9 style C3 fill:#f0f4c3An image is a static blueprint consisting of Layer 1 (Base OS), Layer 2 (Dependencies), Layer 3 (Application), and Config metadata (entrypoint, environment variables, working directory).
When you execute docker run to instantiate the image, you create containers. Container 1 (a running instance) includes the read-only layers from the image, a writable container layer unique to this instance, and a running Process (PID 1). Container 2 (another instance of the same image) has the same read-only layers (shared with Container 1), its own unique writable container layer, and its own running Process. Container 3 (a stopped instance) has the shared read-only layers, a persisted writable container layer on disk, and a stopped Process.
You can have unlimited containers from one image. Each is independent. **Analogy**: An image is like a class definition in programming. A container is like an instance of that class. You can instantiate a class 100 times; each instance is independent. --- ## What Happens When You Run docker run When you execute `docker run -d -e DEBUG=true myapp:1.0`, here's the exact sequence: ### Step 1: Resolve Image ```bashdocker run myapp:1.0Docker checks:
- Is
myapp:1.0in local image cache? If yes, use it. - If not, pull from registry (specified or default Docker Hub)
- Verify image digest (sha256) matches manifest
Result: Image is available locally at /var/lib/docker/image/overlay2/... (exact path depends on storage driver).
Step 2: Create Container
When a container is created, Docker stacks image layers (which are read-only) containing system binaries like /bin/sh, system libraries in /lib/, system utilities in /usr/, and application code like /app/app.py using OverlayFS. This union filesystem presents all these layers as a unified filesystem to the container, so everything appears normal inside the container even though the underlying layers are read-only, with only a top writable layer allowing modifications.
Docker creates a container layer (writable) on top of the image layers (read-only) using OverlayFS.
Docker also creates:
- Namespace directory: For process, network, IPC, UTS isolation
- cgroup directory: For resource limits
- Container metadata: Configuration, state, logs
Step 3: Configure Container
Docker reads the image's config blob and applies settings:
- Environment variables:
-e DEBUG=true+ image-defined ENV vars - Working directory: WORKDIR from image
- Entrypoint and Cmd: How to start the process
- User: Who runs the process (root by default)
- Volumes: Mount paths
Step 4: Start Container
Docker calls the container runtime (containerd or runc) with the config.json file:
- Create namespaces (PID, network, filesystem, etc)
- Set up cgroups (CPU, memory limits)
- Mount filesystem (stacked image layers + container layer)
- Set environment variables
- Change working directory
- Drop capabilities (if specified)
- Fork a new process (PID 1 inside container)
- Exec the entrypoint
Result: Process is running inside the container, isolated.
Step 5: Return Container ID
Docker outputs the container ID (sha256 hash of container config) and returns.
Example:
$ docker run -d myapp:1.0a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6 ← Container ID $ docker psCONTAINER ID IMAGE STATUSa1b2c3d4 myapp:1.0 Up 2 minutesThe container ID uniquely identifies this container. The image ID is separate.
The Container Lifecycle
A container goes through states:
A container progresses through states: created (initialized but not running), running (process executing), paused (suspended), stopped (process exited), and deleted (removed). The container layer persists throughout these state transitions, allowing containers to be stopped and restarted while retaining their data.
State Transitions
Created (not started):
docker create myapp:1.0Container is set up but not running. Container layer exists (empty). Useful for inspecting the filesystem before starting.
Running (process is active):
docker start <container># ordocker run <image>Process is executing. Container layer accumulates changes (files written, modified).
Paused (frozen):
docker pause <container>docker unpause <container>Process is suspended. Useful for checkpoint/restore or freezing state without stopping.
Stopped (process exited):
docker stop <container># or process exits naturallyProcess is no longer running, but container layer is preserved. You can restart it.
Deleted (removed):
docker rm <container>Container layer is deleted. Cannot restart.
Container Persistence
When a container stops, does it persist?
$ docker run myapp:1.0# Container runs and exits# Container still exists! $ docker ps -aCONTAINER ID IMAGE STATUSa1b2c3d4 myapp:1.0 Exited (0) 2 minutes ago $ docker restart a1b2c3d4# Container starts again from where it left offThe container layer is preserved even after the process stops. You can restart it, inspect its filesystem, or commit it to a new image.
The Writable Container Layer
Every running (or stopped) container has a writable layer on top of the image's read-only layers.
How Writes Work
When a process inside a container writes a file:
Process: echo "hello" > /app/output.txt OverlayFS behavior:1. Check lower layers (image layers): /app/output.txt doesn't exist2. Write to upper layer (container layer): /app/output.txt ← written here3. Next read of /app/output.txt: Found in upper layer, return it Result: File exists in container layer, not in image layersIf the image already has a file:
Image has: /etc/config.yaml (from base OS) Process: echo "newval" > /etc/config.yaml OverlayFS behavior:1. Check upper layer (container): /etc/config.yaml doesn't exist2. Check lower layers (image): /etc/config.yaml exists3. OverlayFS copies the file to upper layer (copy-on-write)4. Process modifies the copy in upper layer5. Next read: Found in upper layer, return it (modified version) Result: Image's /etc/config.yaml is unchanged Container has its own /etc/config.yamlCopy-on-Write Efficiency
This mechanism (copy-on-write) is why containers from the same image don't interfere:
Image layers (1 GB total): Layer 1: Base OS (800 MB) Layer 2: Dependencies (200 MB) Container A: Write 1 file (100 KB) → container layer A Container B: Write 1 file (100 KB) → container layer B Storage used: Image: 1 GB (shared) Container A: 100 KB (unique) Container B: 100 KB (unique) Total: 1.0002 GB Without copy-on-write, it would be: Total: 1 GB + 1 GB + 1 GB = 3 GBMultiple Containers from One Image
You can create as many containers as you want from one image:
docker run -d --name web-1 myapp:1.0docker run -d --name web-2 myapp:1.0docker run -d --name web-3 myapp:1.0Now you have 3 containers, all from the same image:
A single image myapp:1.0 (1 GB total size) can support multiple containers simultaneously: Container web-1 running with an additional 50 MB in its container layer, Container web-2 running with 40 MB in its layer, and Container web-3 running with 60 MB in its layer. The total disk space required is just 1.15 GB (the 1 GB base image plus the per-container layers), demonstrating the efficiency of layer sharing across containers.
Key points:
- Shared layers: All 3 containers share the 1 GB image
- Independent container layers: Each container has its own writable layer
- Isolation: Changes in web-1's container layer don't affect web-2 or web-3
- Scalability: You can run 100 containers from one image with ~5-10 GB total disk
This is why containers are efficient. VMs can't share kernels or base layers; each VM needs 500+ MB just for the OS.
Container State: What Persists, What Doesn't
What Persists (Saved in Container Layer)
Files written by the process:
$ docker run ubuntu:22.04 sh -c "echo hello > /tmp/file.txt"# /tmp/file.txt is in the container layer# When container stops, it persists $ docker exec <container> cat /tmp/file.txthello ← File is still there even after container stoppedFilesystem modifications:
$ docker run ubuntu:22.04 sh -c "mkdir /newdir && touch /newdir/file"# /newdir and /newdir/file are in the container layer $ docker start <container>$ docker exec <container> ls /newdirfile ← Directory persistsDatabase writes (if your app writes to local disk):
$ docker run sqlite:latest sh -c "sqlite3 /app/data.db 'INSERT INTO users VALUES (...)'"# /app/data.db changes are in the container layer# Persist across stop/startWhat Doesn't Persist (Lost When Container Deleted)
Everything in the container layer:
$ docker run ubuntu:22.04 sh -c "echo secret > /tmp/file.txt"$ docker rm <container> ← Deletes the container layer$ docker exec <container> cat /tmp/file.txt ← Error: container doesn't exist# File is lostRAM (ephemeral): Processes write to RAM; it's lost when process stops. Only data written to disk persists.
Network changes: iptables, routing, network configuration — lost when container stops.
Immutable Images, Mutable Containers
This is a critical design principle:
Images are immutable:
docker run ubuntu:22.04 sh -c "echo modified > /bin/bash"# You're not modifying the image's /bin/bash# You're writing to the container layer (copy-on-write)# The image's /bin/bash is unchanged docker run ubuntu:22.04 cat /bin/bash# Returns original /bin/bash from image# Your modification is only in that container's layerContainers are mutable:
# Container 1docker run -d --name c1 ubuntu:22.04 sleep 1000docker exec c1 sh -c "echo data > /app/state.txt" # Container 1 now has /app/state.txt in its layer# Container 2docker run -d --name c2 ubuntu:22.04 sleep 1000docker exec c2 sh -c "cat /app/state.txt" ← Error: file doesn't exist# Container 2 doesn't see c1's filesWhy This Matters
Immutability of images:
- Same image produces same behavior (reproducibility)
- You can trust the image hasn't changed since you pulled it
- Easy to rollback: run an old image version
- Security: digest (sha256) uniquely identifies image content
Mutability of containers:
- Containers can be updated without rebuilding images (for legacy apps)
- Useful for debugging: create a container, inspect its state
- Can snapshot a container's state and commit it to a new image
Container IDs, Names, and Runtime Tracking
Every container has an ID and optional name.
Container ID
The container ID is the sha256 hash of the container's configuration:
$ docker run myapp:1.0a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6 ← Full container ID (64 chars)The ID is:
- Unique: No two containers have the same ID
- Deterministic: Same config produces same ID
- Trackable: Runtime uses ID to manage the container
Short ID (first 12 characters) is often used:
$ docker psCONTAINER IDa1b2c3d4Container Name
You can assign a friendly name:
docker run -d --name my-web-server myapp:1.0docker exec my-web-server ls /appdocker stop my-web-serverNames are:
- Aliases:
my-web-serveris easier to remember thana1b2c3d4e5f6g7h8 - Must be unique: Can't have two containers with the same name
- Reusable after deletion: Delete a container, you can reuse its name
Tracking
The container runtime (Docker, containerd, CRI-O) maintains metadata:
$ docker inspect a1b2c3d4[ { "Id": "a1b2c3d4e5f6...", "Created": "2024-03-22T10:30:45.123Z", "Path": "/bin/sh", "Args": ["-c", "python3 app.py"], "State": { "Status": "running", "Pid": 12345, "ExitCode": 0, "StartedAt": "2024-03-22T10:30:46.456Z" }, "Image": "sha256:abc123...", "Mounts": [ { "Type": "bind", "Source": "/home/user/data", "Destination": "/app/data" } ] }]The runtime knows:
- Process ID (Pid: 12345)
- Image (which image this container came from)
- Mounts (what volumes are attached)
- State (running, stopped, created)
Snapshots and Copying Container State
If you want to preserve a container's changes (convert a container back to an image), use docker commit:
# Container has been running, written datadocker run -d --name my-app myapp:1.0docker exec my-app sh -c "echo config > /etc/myapp.conf" # Snapshot the container's state into a new imagedocker commit my-app myapp:1.0.1 # Now myapp:1.0.1 contains all the changesdocker run myapp:1.0.1 cat /etc/myapp.confconfig ← Change persisted in new imageBehind the scenes: When using docker commit, the container's filesystem (combining all image layers and the container's writable layer) is flattened into a single tarball with all layers merged together, then that tarball becomes a new layer in a new image, which is tagged with the new version (myapp:1.0.1).
This is useful for:
- Capturing configuration changes
- Creating a snapshot of a debug container
- Building images from running containers (not recommended for production)
Next Steps
Understanding the relationship between images and containers is foundational:
Key takeaways:
- Image is a static blueprint; container is a running instance
- Multiple containers from one image share read-only layers, have independent writable layers
- Changes in one container don't affect others (isolation)
- Container layer persists even when container stops
- Image is immutable; container is mutable
- Container IDs are unique, names are aliases
docker commitconverts a container back to an image (snapshots state)
Practical next steps:
- Create a container from an image and modify a file; verify changes are in container layer only
- Create 3 containers from one image; verify they're independent
- Stop a container, start it again; verify changes persisted
- Delete a container; verify its layer is gone
- Use
docker inspectto understand container metadata - Use
docker committo snapshot a container into a new image
Key Concepts Summary
- Image: Static artifact (layers + config); immutable and reusable
- Container: Running instance of an image; mutable with isolated filesystem
- Relationship: Image is blueprint, container is instance (class vs object)
- Multiple containers: Can create many containers from one image; they're independent
- Writable layer: Each container has a unique writable layer on top of shared image layers
- Copy-on-write: Efficient sharing via kernel filesystem features (OverlayFS)
- Persistence: Container layer persists when container stops; deleted when container is removed
- Immutability: Image never changes; containers are mutable
- Tracking: Container IDs are unique hashes; names are aliases
- Snapshots:
docker commitconverts a container's state to a new image
