Skip to main content

Docker Images vs Containers

A Docker image is a static, read-only package that contains your app and its dependencies, while a Docker container is a runnable instance of that image – a live process with a writable layer.

Concept explained

  • Image: layered filesystem, immutable, stored by name:tag or ID. Built with a Dockerfile.
  • Container: a running (or stopped) instance of an image with its own filesystem overlay, process namespace, and networking.
  • Relationship: you build an image, then create one or many containers from that image.

Common commands to inspect both:

# list images
docker images

# list running containers
docker ps

# list all containers (including stopped)
docker ps -a

Step-by-step example

  1. A minimal Node app and Dockerfile. Create three files:
  • package.json

    {
    "name": "simple-app",
    "version": "1.0.0",
    "scripts": { "start": "node app.js" },
    "dependencies": {}
    }
  • app.js

    const http = require("http");
    const port = process.env.PORT || 3000;
    http.createServer((req, res) => res.end("Hello from Docker!")).listen(port);
  • Dockerfile

    FROM node:18-slim
    WORKDIR /app
    COPY package.json ./
    COPY app.js ./
    EXPOSE 3000
    CMD ["npm", "start"]
  1. Build the image:
docker build -t myapp:1.0 .
  1. Verify the image exists:
docker images myapp
# REPOSITORY TAG IMAGE ID SIZE
# myapp 1.0 <image-id> ...
  1. Run a container from that image:
docker run -d --name myapp-01 -p 3000:3000 myapp:1.0
  1. Inspect running containers:
docker ps
# CONTAINER ID IMAGE NAMES
# <id> myapp:1.0 myapp-01
  1. Inspect container filesystem or run a shell:
docker exec -it myapp-01 sh
# make changes inside the container (ephemeral)
  1. Persist changes back to a new image only if needed (not recommended for builds):
docker commit myapp-01 myapp:1.1
  1. Cleanup:
docker stop myapp-01
docker rm myapp-01
docker rmi myapp:1.1 # remove image
tip

Prefer rebuilding images via a Dockerfile and CI instead of using docker commit. Commits make builds harder to reproduce.

Variations & gotchas

  • Containers have a writable top layer; changes there are ephemeral unless you use volumes. Use volumes for persistent data (databases, uploads).
  • Multiple containers can be created from the same image simultaneously.
  • Image layers are cached; small changes can invalidate layer cache and increase build time.
  • Removing an image that still has dependent containers will fail until containers are removed.
caution

Don't rely on container filesystem changes for production data. If the container is removed, those changes are lost unless stored in a volume or external DB.

Common mistakes

  • Expecting docker run changes to automatically update the image – they don't. Rebuild the image via Dockerfile.
  • Confusing IMAGE ID and CONTAINER ID when running docker commands.
  • Forgetting to map ports (-p) and assuming a container is reachable from outside.
  • Leaving unused images and stopped containers, which consume disk space.

Best practices

  • Tag images clearly (semantic or build IDs): myapp:1.0 or myapp:sha256-...
  • Use .dockerignore to keep build context small.
  • Keep images small (alpine or multi-stage builds).
  • Use volumes for data and config; images should be immutable artifacts.
  • Clean up dangling images and stopped containers regularly (docker system prune).
  • Automate builds in CI and push to a registry instead of relying on local images.

When to use / when not to use

When to use images:

  • To package your app and dependencies for reproducible builds and distribution.
  • To deploy consistent runtime artifacts across environments.

When to use containers:

  • To run, debug, scale, and test your application instances.
  • For transient or horizontally scalable workloads.

When not to use:

  • Don't use containers as permanent storage for app data without volumes.
  • Don't use docker commit as a replacement for proper image builds in CI.

Key takeaways

  • An image is a static blueprint; a container is a running instance of that blueprint.
  • Build and version images with Dockerfile and CI; run containers from those images.
  • Use volumes for persistent data – container filesystem is ephemeral.
  • Use clear tags for images and remove old images/containers to save space.