Definition
- Docker image – an immutable, read-only template that packages a base OS layer, runtime, dependencies, the application, and metadata like the entrypoint.
- Docker container – a running (or stopped) instance of an image with a thin writable layer on top where runtime changes live.
- Layers – each Dockerfile instruction creates a filesystem diff; layers are content-addressed, cached, and shared across images.
- Union filesystem – merges the read-only image layers and the writable container layer into one coherent view.
- Immutability – once built, an image's contents never change; a new build produces a new image with a new digest.
Think of the National Bank of India's standard branch blueprint. The image is the sealed, approved blueprint plus a kit containing the building shell (base OS), furniture and machines (runtime and dependencies), the staff handbook (application), and a note on how to open for business (entrypoint). A container is an actual branch built from that blueprint and now open, with the day's cash drawer and paperwork as the thin writable layer. You can open ten branches from the same blueprint.
Understanding the Concepts
What is a Docker image?
An image is immutable and read-only. Once built, its contents never change. If you need a change, you rebuild and get a new image with a new content digest (SHA256). This immutability makes deployments reproducible: the exact image tested in QA is byte-for-byte the one that runs in Prod.
# Build an image for the banking transfer service
docker build -t registry.bank.local/transfer-service:1.2.0 .
# The image is identified by a content digest
docker inspect registry.bank.local/transfer-service:1.2.0 \
--format '{{.Id}}'
# sha256:a1b2c3d4e5f6...
What is a Docker container?
A container is a runtime instance of an image. When you run an image, the engine stacks a thin writable layer on top of the read-only layers and starts the process defined by the entrypoint.
# Run a container from the image
docker run -d --name transfer-1 \
-p 8080:8080 \
registry.bank.local/transfer-service:1.2.0
# Run another container from the same image
docker run -d --name transfer-2 \
-p 8081:8080 \
registry.bank.local/transfer-service:1.2.0
# Both containers share the same read-only image layers.
# Each has its own thin writable layer.
Anything the process writes, such as temp files or logs, goes into the writable layer. When the container is removed, the writable layer is discarded.
How do image and container compare?
| Aspect | Image | Container |
|---|---|---|
| State | Immutable, read-only | Running, writable layer |
| Analogy | Class definition | Object instance |
| Identified by | Content digest (SHA256) + tags | Container ID + name |
| Created by | docker build | docker run or docker create |
| Stored in | Registry (e.g., Docker Hub, Nexus) | Local runtime |
| Lifecycle | Built once, pulled many times | Created, started, stopped, removed |
| Shareable | Yes, pushed to a registry | No, local to the host |
What is inside an image?
An image contains ordered layers, each produced by a Dockerfile instruction. A typical banking-service image has:
# Layer 1: base OS + Java runtime
FROM eclipse-temurin:21-jre
# Layer 2: working directory
WORKDIR /app
# Layer 3: application artifact
COPY target/transfer-service.jar app.jar
# Layer 4: exposed port (metadata)
EXPOSE 8080
# Layer 5: entrypoint (metadata)
ENTRYPOINT ["java", "-jar", "app.jar"]
# Inspect the layers of an image
docker history registry.bank.local/transfer-service:1.2.0
# IMAGE CREATED SIZE CREATED BY
# a1b2c3d4e5f6 2 hours ago 45MB COPY target/transfer-service.jar
# f6e5d4c3b2a1 3 days ago 180MB FROM eclipse-temurin:21-jre
How does layer caching work?
Layers are content-addressed and cached. Rebuilding only re-executes layers whose inputs changed. This is why Dockerfile ordering matters.
# WRONG: code change invalidates dependency layer.
FROM eclipse-temurin:21-jre
COPY . /app # Any code change invalidates this layer
RUN mvn package # Forces re-download of all dependencies
# CORRECT: resolve dependencies before copying source.
FROM maven:3.9-eclipse-temurin-21 AS build
WORKDIR /app
COPY pom.xml . # Dependency manifest (changes rarely)
RUN mvn dependency:resolve # Cached unless pom.xml changes
COPY src/ src/ # Source code (changes often)
RUN mvn package -DskipTests # Only this layer re-runs on code change
Layers are also shared across images. If two banking services use the same base image, that base is stored and pulled once.
Why must secrets and build tools stay out of layers?
Every layer is readable by anyone who can pull the image. A file created in one layer and deleted in a later layer still occupies space in the earlier layer.
# WRONG: secret in a layer persists even after deletion.
FROM eclipse-temurin:21-jre
COPY secrets.properties /app/
RUN cat /app/secrets.properties && rm /app/secrets.properties
# The file is STILL in the COPY layer. Anyone can extract it.
# CORRECT: never copy secrets into any layer.
# Inject them at runtime via Kubernetes Secrets.
How should persistent state be handled?
Persistent state belongs in mounted volumes, not in the container's writable layer, so containers stay disposable.
# WRONG: writing data to the container's writable layer.
# Data is lost when the container is removed.
docker run -d registry.bank.local/transfer-service:1.2.0
# Logs written inside the container are gone on docker rm.
# CORRECT: mount a volume for persistent data.
docker run -d \
-v /data/transfer-logs:/app/logs \
registry.bank.local/transfer-service:1.2.0
# Logs persist in /data/transfer-logs even after container removal.
What should a senior engineer know?
- Pin base images by digest in banking for provenance:
FROM eclipse-temurin:21-jre@sha256:abc123... - Scan images for vulnerabilities before deployment.
- Sign images to prove they came from your trusted pipeline.
- The writable layer should stay small and ephemeral. Persistent state goes in volumes or external stores.
# Pin base image by digest for provenance
FROM eclipse-temurin:21-jre@sha256:a1b2c3d4e5f6...
# Exact same base image on every build, regardless of tag changes.
Visual Overview
Image (immutable, read-only):
┌──────────────────────────────────┐
│ Layer 4: ENTRYPOINT java -jar │ (metadata)
│ Layer 3: COPY app.jar │ (application)
│ Layer 2: JRE libraries │ (runtime)
│ Layer 1: Base OS (Alpine/Debian)│ (base image)
└──────────────────────────────────┘
Container (running instance):
┌──────────────────────────────────┐
│ Writable layer (thin) │ ← temp files, logs
├──────────────────────────────────┤
│ Layer 4: ENTRYPOINT │ (read-only)
│ Layer 3: COPY app.jar │ (read-only)
│ Layer 2: JRE libraries │ (read-only)
│ Layer 1: Base OS │ (read-only)
└──────────────────────────────────┘
One image, many containers:
┌───────┐ ┌───────┐ ┌───────┐
│ Ctr 1 │ │ Ctr 2 │ │ Ctr 3 │ ← Each has its own writable layer
└───┬───┘ └───┬───┘ └───┬───┘
└──────────┼──────────┘
│
┌──────────┴──────────┐
│ Shared Image Layers │ ← Read-only, stored once
└─────────────────────┘
Key Takeaway
A Docker image is an immutable, read-only template built from ordered layers that packages everything needed to run an application. A container is a running instance of that image with a thin writable layer on top. The relationship is like a class and an object: the image is the definition built once, and containers are live instances created from it.
What is inside the image is a stack of layers, each being the filesystem change from one Dockerfile instruction. A typical banking-service image has a base layer with the Java runtime, a layer copying the application JAR, and metadata recording how to start it. Layers are content-addressed and cached, so rebuilding only re-executes layers whose inputs changed, and shared base layers are stored once.
The immutability is the practical payoff. Because the image never changes and is identified by a content digest, the exact image tested in QA is byte-for-byte the one running in Prod. Persistent state belongs in volumes, not the writable layer, so containers stay disposable.
Remember one sentence: the image is the immutable template with layered content, and the container is a running instance with a thin writable layer on top.
---