Managing Docker Containers and Volumes

SA
StudyAI
AI-generated study notes
· Published Updated Pro Plus model

From the Make notes on docker an GitHub curriculum

TL;DR

You'll learn how containers move through their lifecycle (create, start, stop, remove), how to inspect and debug running containers, and how volumes keep your data alive after a container dies. You'll see real commands, a state diagram, and the exact pitfalls that bite beginners with data loss.

1. The Mental Model

A container is a process with its own filesystem view, not a tiny virtual machine. When the container stops, its writable layer still exists until you remove it — but anything written there dies the moment you delete it. Volumes are Docker-managed storage that lives outside the container's lifecycle, so data survives even when the container doesn't. Containers are disposable; your data shouldn't be.

2. The Core Material

2.1 The Container Lifecycle

A vibrant display of stacked shipping containers forming an arch under a clear blue sky.
Photo by Jan van der Wolf on Pexels

Every container moves through a small set of states. Knowing the exact command for each transition saves you from guessing.

docker create --name web nginx          # creates, doesn't start
docker start web                        # created -> running
docker pause web                        # running -> paused (freezes processes)
docker unpause web                      # paused -> running
docker stop web                         # running -> stopped (SIGTERM, then SIGKILL after 10s)
docker kill web                         # running -> stopped (SIGKILL immediately)
docker restart web                      # stopped/running -> running again
docker rm web                           # stopped -> gone (filesystem deleted)

docker run is really docker create + docker start glued together, which is why most tutorials only teach run and skip the rest. But understanding the split matters once you're debugging a container that exited unexpectedly — you can inspect it before removing it.

docker run -d --name api -p 8080:80 nginx   # -d = detached, -p = port mapping
docker ps                                    # running containers
docker ps -a                                 # all containers, including stopped
docker logs -f api                           # follow logs live
docker exec -it api /bin/bash                # shell into a running container
docker inspect api                           # full JSON metadata: mounts, network, env

docker exec only works on a running container — it attaches a new process to an already-running namespace. If the container already exited, exec fails; you need docker start first, or docker logs to see why it died.

2.2 Why Containers Lose Data (and Volumes Fix It)

Close-up of a rusted blue Hanjin shipping container displaying weight and size information.
Photo by Simon on Pexels

Every container gets a writable layer on top of its read-only image layers (this is Docker's union filesystem, overlay2 on Linux). Any file you create or modify inside the container goes into that writable layer. The problem: docker rm deletes that layer permanently.

flowchart LR
    A["Image layers (read-only)"] --> B["Writable container layer"]
    B --> C{"docker rm"}
    C -->|"no volume mounted"| D["Data gone forever"]
    C -->|"volume mounted"| E["Data persists in volume/host dir"]

Docker gives you three storage options:

Type Where it lives Use case
Writable layer (default) Inside container, managed by storage driver Throwaway scratch data
Bind mount A specific path on the host Local dev, sharing source code into a container
Named volume Docker-managed area (/var/lib/docker/volumes/ on Linux) Production persistent data (databases, uploads)
# Named volume — Docker manages the path, portable across hosts
docker volume create db_data
docker run -d --name postgres \
  -v db_data:/var/lib/postgresql/data \
  -e POSTGRES_PASSWORD=secret postgres:16

# Bind mount — you control the exact host path
docker run -d --name app \
  -v "$(pwd)/src:/usr/src/app" node:20 node server.js

docker volume ls                       # list volumes
docker volume inspect db_data          # see mountpoint, driver
docker volume rm db_data                # delete — only if unused
docker volume prune                     # delete ALL unused volumes

The -v syntax is host_source:container_target. If host_source contains a slash or starts with ./ or /, Docker treats it as a bind mount. If it's a plain name like db_data, Docker treats it as a named volume and creates it automatically if it doesn't exist.

You can also mount volumes read-only so a container can read config but never corrupt it:

docker run -v config_vol:/etc/app/config:ro myimage

2.3 Cleaning Up Without Shooting Yourself

An Asian woman performing cleaning chores by sweeping the floor indoors.
Photo by RDNE Stock project on Pexels

Docker never deletes anything automatically, so unused containers, images, and volumes pile up fast. The commands below are safe in roughly increasing order of aggressiveness.

docker container prune              # remove all STOPPED containers
docker image prune                  # remove dangling (untagged) images
docker image prune -a               # remove ALL images not used by a running container
docker volume prune                 # remove volumes not attached to any container
docker system prune -a --volumes    # the nuclear option: containers + images + volumes + networks

Run docker system df first to see how much space each category is using — it tells you whether pruning is even worth doing before you nuke anything.

stateDiagram-v2
    [*] --> Created: docker create
    Created --> Running: docker start
    Running --> Paused: docker pause
    Paused --> Running: docker unpause
    Running --> Stopped: docker stop / kill
    Stopped --> Running: docker start
    Stopped --> [*]: docker rm
    Created --> [*]: docker rm

3. Worked Example

Say you're running a Postgres database for local development and want it to survive container restarts and rebuilds.

# Step 1: create a named volume for persistent data
docker volume create pg_data

# Step 2: run Postgres, mounting the volume at Postgres's data directory
docker run -d --name mydb \
  -e POSTGRES_PASSWORD=devpass \
  -v pg_data:/var/lib/postgresql/data \
  -p 5432:5432 \
  postgres:16

# Step 3: create a table and insert data
docker exec -it mydb psql -U postgres -c \
  "CREATE TABLE users(id serial, name text); INSERT INTO users(name) VALUES ('alice');"

# Step 4: prove it's running
docker exec -it mydb psql -U postgres -c "SELECT * FROM users;"
# id | name
# ----+-------
#  1 | alice

# Step 5: destroy the container entirely
docker stop mydb && docker rm mydb

# Step 6: spin up a FRESH container, same volume
docker run -d --name mydb2 \
  -e POSTGRES_PASSWORD=devpass \
  -v pg_data:/var/lib/postgresql/data \
  -p 5432:5432 \
  postgres:16

docker exec -it mydb2 psql -U postgres -c "SELECT * FROM users;"
# id | name
# ----+-------
#  1 | alice     <-- data survived container deletion

The container mydb is completely gone — a different name, different ID, different writable layer. But alice is still there, because she lived in pg_data, not in the container. That's the whole point of volumes.

4. Production Pitfalls & Best Practices

4.1 Real-World Best Practices

Three women practicing yoga indoors at a studio in Rishikesh, India.
Photo by Yoga Vidya Mandiram on Pexels

  • Name your volumes explicitly (-v db_data:/data) instead of letting Docker generate anonymous hash names — anonymous volumes are nearly impossible to identify later with docker volume ls.
  • Use bind mounts for source code in dev, named volumes for data in prod. Bind mounts give instant file sync for hot-reload; named volumes give Docker-managed, backup-friendly storage.
  • Always check docker ps -a before prune commands — a stopped container you forgot about might hold logs you still need.
  • Back up volumes with a throwaway container, don't touch the host filesystem directly:
    bash docker run --rm -v pg_data:/data -v "$(pwd)":/backup alpine \ tar czf /backup/pg_backup.tar.gz -C /data .
  • Set restart: unless-stopped (or --restart unless-stopped) on long-running services so they survive host reboots without needing cron jobs.
  • Use docker inspect --format to script around state, e.g. docker inspect -f '{{.State.Running}}' mydb in CI health checks instead of parsing docker ps text output.
  • Tag volumes by project with Compose (project_db_data) so docker volume ls stays readable across multiple projects on one machine.

4.2 Common Bugs & Anti-Patterns

Bug 1 — Writing data inside the container with no volume, then wondering where it went:

# BAD
docker run -d --name db postgres:16
# ...weeks later...
docker rm -f db   # all data silently destroyed
# FIXED
docker run -d --name db -v pg_data:/var/lib/postgresql/data postgres:16

Bug 2 — Confusing bind mount source paths (relative paths resolve from the wrong directory):

# BAD — depends on where you happened to run the command from
docker run -v ./data:/app/data myimage
# FIXED — always use an absolute path, or $(pwd) explicitly
docker run -v "$(pwd)/data":/app/data myimage

Bug 3 — Running docker system prune --volumes on a shared host:

# BAD — wipes every unused volume, including a teammate's "temporarily stopped" service data
docker system prune -a --volumes
# FIXED — prune scoped and inspect first
docker volume ls -f dangling=true
docker volume prune   # review the list it prints before confirming

Bug 4 — Expecting docker stop to be instant, then force-killing too eagerly:

# BAD — sends SIGKILL immediately, DB may not flush writes
docker kill mydb
# FIXED — give the app time to shut down cleanly
docker stop -t 30 mydb   # waits 30s for graceful shutdown before SIGKILL

4.3 When to Use What (Decision Matrix

Situation Right approach
Local dev, want live code reload Bind mount source folder into container
Database / persistent app state Named volume
Sharing config files read-only Named or bind volume with :ro flag
Temporary scratch space, disposable Default writable layer, no volume
Backing up production data docker run --rm + tar through a volume mount
Multi-service app needing shared data Named volume referenced by multiple containers in Compose

5. Now Try It

Right here, in your terminal: create a named volume called notes_vol, run an alpine container that mounts it at /data and writes a file (echo "hello" > /data/note.txt), then remove the container completely and start a new alpine container mounting the same volume to cat /data/note.txt. Then run docker volume inspect notes_vol and note the Mountpoint field.

Success looks like: the second, brand-new container prints hello even though the first container no longer exists — proving you understand that data lives in the volume, not the container.

Frequently asked about Managing Docker Containers and Volumes

You'll learn how containers move through their lifecycle (create, start, stop, remove), how to inspect and debug running containers, and how volumes keep your data alive after a container dies. Read the full notes above for the details.

Managing Docker Containers and Volumes is a core topic in Make notes on docker an GitHub. Most exam papers test it via a mix of definitions, worked examples, and applied problems. The notes above cover the high-yield sub-topics, common pitfalls, and the kind of questions examiners typically set.

Yes — every note in the StudyAI Campus Hub is free to read in full, right here on this page, with no account needed. If you clone the plan into your own dashboard, the free plan shows a preview of each note there; Basic and above unlock the full notes in your dashboard, along with practice quizzes, flashcards and offline study. You can always come back here to read the complete note for free.

Study this next


Get the full Make notes on docker an GitHub curriculum

Clone the complete plan to your dashboard for unlimited AI-generated notes, practice quizzes, and a personalised revision schedule.

Save this course free