0

Image vs container — the template vs the running instance

Beginner5 min read·eng-18-004

Concept

Image vs container — an image is the blueprint; a container is the running instance. The same image can run as many containers simultaneously.

Docker image:

  • A read-only, layered snapshot of a filesystem and configuration.
  • Built from a Dockerfile with docker build.
  • Stored in a registry (Docker Hub, GitHub Container Registry, AWS ECR).
  • Tagged: myapp:latest, myapp:v1.2.3, php:8.4-fpm-alpine.
  • Immutable: once built, an image doesn't change.

Docker container:

  • A running (or stopped) instance of an image.
  • Created with docker run.
  • Has its own writable layer on top of the image layers.
  • Ephemeral: when deleted, any changes to its filesystem are lost.
  • You can run 10 containers from the same image simultaneously.

The analogy: Image = class definition. Container = object instance. The same class (image) can be instantiated (run) many times into separate objects (containers).

Layers: Images are built in layers. Each RUN, COPY, ADD in the Dockerfile adds a layer. Layers are cached — if nothing in a layer changed, Docker reuses the cache. Ordering matters: put frequently changing steps (COPY source code) last, stable steps (install dependencies) first.

Image tags: Used for versioning. :latest is the default tag but not recommended in production — always pin to a specific version tag for reproducible builds.

Code Example

bash
# BUILD an image from Dockerfile
docker build -t myapp:latest .
docker build -t myapp:v2.1.0 -f Dockerfile.prod .

# LIST images
docker images
# REPOSITORY   TAG       IMAGE ID       CREATED       SIZE
# myapp        latest    abc123         2 hours ago   142MB
# php          8.4-fpm   def456         3 days ago    445MB

# PUSH to registry
docker tag myapp:latest ghcr.io/username/myapp:latest
docker push ghcr.io/username/myapp:latest

# RUN a container from an image
docker run -d \
  --name myapp-1 \
  -p 8080:80 \
  -e APP_ENV=production \
  -v /data/storage:/var/www/storage \
  myapp:latest

# Run another instance from the same image (different port)
docker run -d --name myapp-2 -p 8081:80 myapp:latest

# LIST running containers
docker ps
# CONTAINER ID   IMAGE           STATUS          PORTS
# xyz789         myapp:latest    Up 2 hours      0.0.0.0:8080->80/tcp
# abc123         myapp:latest    Up 2 hours      0.0.0.0:8081->80/tcp

# Container is just a process from an image
docker exec -it myapp-1 bash     # shell into running container
docker stop myapp-1              # stop container
docker rm myapp-1                # remove container (image still exists)

# LAYERS — inspect image layer cache behavior
# Dockerfile optimized for caching:
FROM php:8.4-fpm-alpine
# Layer 1: system packages (changes rarely → cached)
RUN apk add --no-cache libpng-dev
# Layer 2: PHP extensions (changes rarely → cached)
RUN docker-php-ext-install pdo pdo_mysql gd
# Layer 3: composer dependencies (changes when composer.lock changes)
COPY composer.json composer.lock ./
RUN composer install --no-dev
# Layer 4: application code (changes most often → rebuilds from here)
COPY . .

# If you only change PHP files, layers 1-3 use cache → fast rebuild