Container Data Management (Volumes)

Now let's learn about one of the most important topics in Docker - how to manage data in containers.


Part 1: The Container Data Problem

Understanding Container Filesystem

Important Concept: Containers are EPHEMERAL (temporary)

What does this mean?

When you create a container:
├── It has its own filesystem
├── You can create/modify files inside
├── Everything works normally

When you delete the container:
├── ALL data inside is lost! ✗
├── Files gone forever
└── No way to recover

Demonstrating the Problem

Let's see this in action!

Step 1: Run Ubuntu container and create a file

docker run -it --name test-container ubuntu bash

Inside the container:

# You're now inside Ubuntu container
# Create a file
echo "Important data!" > /data.txt

# Verify it exists
cat /data.txt
# Output: Important data!

# Exit container
exit

Step 2: Start the same container again

docker start test-container
docker exec -it test-container bash

Inside container:

# Check if file still exists
cat /data.txt
# Output: Important data!

# File is still there! ✓
exit

Step 3: Remove and create new container

# Remove the container
docker rm test-container

# Create a new container (same image)
docker run -it --name test-container2 ubuntu bash

Inside new container:

# Try to find the file
cat /data.txt
# Error: No such file or directory ✗

# File is GONE! ✗

What happened?

Container 1:
├── Created data.txt
├── Data stored in container's writable layer
└── Removed → Data lost forever! ✗

Container 2:
├── Fresh container from same image
├── No data from Container 1
└── Starting from scratch

Problem: Data is tied to container lifecycle!

Real-World Problem Scenarios

Scenario 1: Database Container

Run MySQL container:
├── Create database
├── Add tables
├── Insert 1000 customer records

Container crashes:
├── Restart container → Data still there ✓

Accidentally delete container:
├── All data GONE! ✗
├── 1000 customer records lost!
└── Disaster! ✗

Scenario 2: Web Application

Upload feature:
├── Users upload photos
├── Photos saved in /uploads/ inside container

Update application (new container):
├── Deploy new version
├── Remove old container
├── All uploaded photos GONE! ✗
└── Users angry! ✗

Scenario 3: Log Files

Application writes logs:
├── Debug logs in /var/log/app/
├── Error logs accumulating

Container deleted:
├── All logs lost ✗
├── Can't debug past issues ✗
└── No audit trail ✗

The Solution: Docker Volumes

Volumes = Persistent storage outside the container

Think of it as:

Container = Temporary hotel room
├── You stay there temporarily
├── When you check out, room is cleaned
└── Your stuff is gone

Volume = Your storage unit
├── Permanent storage space
├── Exists outside the hotel
├── Your stuff stays even after checkout
└── Can access from any room (container)

Visual:

WITHOUT Volumes:
┌──────────────────┐
│   Container      │
│                  │
│  /data/          │ ← Data inside
│  └── files       │
└──────────────────┘
     ↓ Delete
    Data lost! ✗


WITH Volumes:
┌──────────────────┐     ┌──────────────┐
│   Container      │     │   Volume     │
│                  │────→│              │
│  /data/ (mount)  │     │  Real data   │
│                  │     │  stored here │
└──────────────────┘     └──────────────┘
     ↓ Delete                   ↓
  Container gone            Data safe! ✓

Part 2: What are Docker Volumes?

Simple Definition

Volume = A storage space managed by Docker that exists outside containers

Key Characteristics:

Volumes are:
├── Persistent (survive container deletion)
├── Managed by Docker
├── Can be shared between containers
├── Independent of container lifecycle
├── Stored on host machine
└── Easy to backup

How Volumes Work

Conceptual Model:

Host Machine (Your Computer):
├── Docker manages a special directory
├── /var/lib/docker/volumes/ (Linux)
├── This is where volume data is stored

Volume:
├── Named storage space
├── Like a hard drive managed by Docker

Container:
├── Mounts (connects to) the volume
├── Sees volume as a directory
└── Reads/writes to volume = permanent storage

Visual:

Your Computer Filesystem:
/var/lib/docker/volumes/
├── my-volume/
│   └── _data/
│       ├── file1.txt
│       └── file2.txt
└── db-volume/
    └── _data/
        └── database.db

Container:
/app/data/ ──(mounted)──→ my-volume
                          ↓
                    Actual storage location

Part 3: Creating and Using Volumes

Creating a Volume

Syntax:

docker volume create VOLUME_NAME

Example:

docker volume create my-data

Output:

my-data

That's it! Volume created! ✓


Listing Volumes

docker volume ls

Output:

DRIVER    VOLUME NAME
local     my-data

Inspecting a Volume

docker volume inspect my-data

Output:

[
    {
        "CreatedAt": "2026-02-24T10:30:00Z",
        "Driver": "local",
        "Labels": {},
        "Mountpoint": "/var/lib/docker/volumes/my-data/_data",
        "Name": "my-data",
        "Options": {},
        "Scope": "local"
    }
]

Important field:

Mountpoint: "/var/lib/docker/volumes/my-data/_data"
                    ↑
            Where data is actually stored on your computer

Using a Volume with Container

Mount volume when running container:

Syntax:

docker run -v VOLUME_NAME:/path/in/container IMAGE

Example:

docker run -it -v my-data:/data ubuntu bash

What this does:

-v my-data:/data
   ↑       ↑
   │       └── Path inside container
   └── Volume name

Container sees /data/ directory
/data/ is actually stored in my-data volume
Data persists even after container is deleted!

Practical Example: Persistent Data

Let's see volumes in action!

Step 1: Create a volume

docker volume create persistent-data

Step 2: Run container with volume

docker run -it --name container1 -v persistent-data:/data ubuntu bash

Inside container:

# Create some files
echo "This data will persist!" > /data/important.txt
echo "User database" > /data/users.db
echo "Configuration" > /data/config.json

# List files
ls /data/
# Output: important.txt  users.db  config.json

# Exit
exit

Step 3: Delete the container

docker rm container1

Step 4: Create NEW container with SAME volume

docker run -it --name container2 -v persistent-data:/data ubuntu bash

Inside new container:

# Check if data exists
ls /data/
# Output: important.txt  users.db  config.json

cat /data/important.txt
# Output: This data will persist!

# Data is still there! ✓
# Even though we deleted container1!

🎉 Volume preserved the data!


Multiple Containers Sharing a Volume

Volumes can be shared between containers!

Terminal 1:

docker run -it --name writer -v shared-data:/data ubuntu bash

Inside writer container:

# Write data
echo "Message from writer" > /data/message.txt

# Keep container running
sleep infinity

Terminal 2 (new terminal):

docker run -it --name reader -v shared-data:/data ubuntu bash

Inside reader container:

# Read data written by writer
cat /data/message.txt
# Output: Message from writer

# Data shared between containers! ✓

Use case:

Example: Microservices sharing data

Container 1 (Producer):
└── Writes log files to /logs

Container 2 (Analyzer):
└── Reads log files from /logs

Both mount same volume:
└── Data flows between them! ✓

Part 4: Bind Mounts

What are Bind Mounts?

Bind Mount = Mount a directory from YOUR computer into a container

Difference from Volumes:

Volume:
├── Managed by Docker
├── Stored in Docker's directory
└── docker volume create my-vol

Bind Mount:
├── You choose the directory
├── Any directory on your computer
└── Mount your own folder

Visual:

Volume (Managed by Docker):
Your Computer                Container
Docker manages:             
/var/lib/docker/volumes/    
└── my-vol/_data/     ────→ /data/
    └── files               

Bind Mount (You manage):
Your Computer                Container
Your directory:
C:\Users\You\project\       
└── code/             ────→ /app/
    └── files

Creating Bind Mounts

Syntax:

docker run -v /host/path:/container/path IMAGE

Windows example:

docker run -v C:\Users\YourName\myapp:/app ubuntu

Absolute path required!


Practical Example: Development Workflow

This is EXTREMELY useful for development!

Scenario: Developing a Python app

Step 1: Create project directory

mkdir C:\Users\YourName\python-app
cd C:\Users\YourName\python-app

Step 2: Create app.py

# app.py
print("Hello from Python!")
print("Version 1.0")

Step 3: Run with bind mount

docker run -it -v C:\Users\YourName\python-app:/app python:3.11 bash

Inside container:

cd /app
ls
# Output: app.py

python app.py
# Output: 
# Hello from Python!
# Version 1.0

# Keep container running
sleep infinity

Step 4: Edit file on YOUR computer (not in container)

Open app.py in your editor and change it:

# app.py
print("Hello from Python!")
print("Version 2.0 - Updated!")
print("New feature added!")

Save the file

Step 5: Run again in container (same container still running)

# Still inside the container
python app.py
# Output:
# Hello from Python!
# Version 2.0 - Updated!
# New feature added!

# Changes reflected immediately! ✓

What happened?

File on your computer:
C:\Users\YourName\python-app\app.py
                    ↓
                (bind mount)
                    ↓
File in container:
/app/app.py

They're the SAME file!
Edit on computer → Changes in container immediately! ✓

Bind Mount Benefits for Development

Traditional development:

Without bind mount:
1. Edit code on computer
2. Copy code into container (slow)
3. Test
4. Find bug
5. Exit container
6. Edit code again
7. Copy into container again (slow)
8. Repeat... ✗

With bind mount:

With bind mount:
1. Edit code on computer
2. Changes instantly in container ✓
3. Test immediately
4. Edit again
5. Test immediately
6. Fast iteration! ✓

Modern Bind Mount Syntax

Old syntax:

docker run -v C:\path:/container/path image

New syntax (recommended):

docker run --mount type=bind,source=C:\path,target=/container/path image

Example:

docker run --mount type=bind,source=C:\Users\YourName\myapp,target=/app python:3.11

Both work, but --mount is more explicit and clear.


Part 5: Volume vs Bind Mount - When to Use What?

Comparison

┌─────────────────────────────────────────────────────┐
│              VOLUMES vs BIND MOUNTS                 │
├─────────────────────────────────────────────────────┤
│                                                     │
│  VOLUMES:                                          │
│  ✓ Managed by Docker                              │
│  ✓ Better for production                          │
│  ✓ Works on all platforms                         │
│  ✓ Easy to backup                                 │
│  ✓ Can be shared easily                           │
│  ✗ Need docker volume commands to manage          │
│                                                     │
│  BIND MOUNTS:                                      │
│  ✓ Direct access to files                         │
│  ✓ Great for development                          │
│  ✓ Easy to edit files                             │
│  ✓ No docker commands needed                      │
│  ✗ Path must exist on host                        │
│  ✗ Platform-specific paths                        │
│                                                     │
└─────────────────────────────────────────────────────┘

When to Use Volumes

Use volumes for:

✓ Database data
  └── MySQL, PostgreSQL, MongoDB

✓ Production data
  └── Uploaded files, generated reports

✓ Data that must persist
  └── User data, configurations

✓ Shared data between containers
  └── Microservices communication

✓ Backups
  └── Easy to backup entire volume

Example: Database

docker run -d \
  --name mysql-db \
  -v mysql-data:/var/lib/mysql \
  -e MYSQL_ROOT_PASSWORD=secret \
  mysql:8.0

When to Use Bind Mounts

Use bind mounts for:

✓ Development
  └── Edit code on computer, test in container

✓ Configuration files
  └── nginx.conf, app config

✓ Source code during development
  └── Live reload

✓ When you need direct file access
  └── Easy to edit/view files

Example: Development

docker run -d \
  -v C:\Users\You\myapp:/app \
  -p 5000:5000 \
  python:3.11 \
  python /app/app.py

Part 6: Real-World Examples

Example 1: MySQL Database with Volume

Run MySQL with persistent data:

# Create volume for database
docker volume create mysql-data

# Run MySQL container
docker run -d \
  --name mysql-db \
  -v mysql-data:/var/lib/mysql \
  -e MYSQL_ROOT_PASSWORD=mypassword \
  -e MYSQL_DATABASE=myapp \
  -p 3306:3306 \
  mysql:8.0

What happens:

Container created:
├── MySQL running
├── Creates database files
└── Stored in mysql-data volume

Stop/Remove container:
├── Container gone
└── Data safe in volume ✓

Start new container with same volume:
├── All databases restored ✓
└── No data loss ✓

Test it:

# Connect to MySQL
docker exec -it mysql-db mysql -uroot -pmypassword

# Inside MySQL:
CREATE TABLE users (id INT, name VARCHAR(50));
INSERT INTO users VALUES (1, 'Alice');
SELECT * FROM users;
# Data created ✓

exit

# Remove container
docker stop mysql-db
docker rm mysql-db

# Create new container with same volume
docker run -d \
  --name mysql-db-new \
  -v mysql-data:/var/lib/mysql \
  -e MYSQL_ROOT_PASSWORD=mypassword \
  mysql:8.0

# Wait 10 seconds for MySQL to start
# Connect again
docker exec -it mysql-db-new mysql -uroot -pmypassword myapp

# Check data
SELECT * FROM users;
# Output: 1 | Alice
# Data persisted! ✓

Example 2: Web App Development with Bind Mount

Create a simple web app:

Directory structure:

my-website/
├── index.html
├── style.css
└── app.js

index.html:

<!DOCTYPE html>
<html>
<head>
    <title>My App</title>
    <link rel="stylesheet" href="style.css">
</head>
<body>
    <h1>Hello Docker!</h1>
    <p id="message">Loading...</p>
    <script src="app.js"></script>
</body>
</html>

style.css:

body {
    font-family: Arial;
    background-color: #f0f0f0;
    padding: 20px;
}

app.js:

document.getElementById('message').textContent = 'Version 1.0';

Run with bind mount:

docker run -d \
  --name web-dev \
  -v C:\Users\YourName\my-website:/usr/share/nginx/html \
  -p 8080:80 \
  nginx:alpine

Access: http://localhost:8080

Now edit files on your computer:

Change app.js:

document.getElementById('message').textContent = 'Version 2.0 - UPDATED!';

Refresh browser → Changes appear immediately! ✓

No need to rebuild or restart container!


Example 3: Sharing Data Between Containers

Scenario: Log producer and analyzer

Create shared volume:

docker volume create shared-logs

Container 1: Producer (generates logs)

docker run -d \
  --name log-producer \
  -v shared-logs:/logs \
  ubuntu \
  bash -c "while true; do echo \"Log entry: $(date)\" >> /logs/app.log; sleep 5; done"

Container 2: Analyzer (reads logs)

docker run -it \
  --name log-analyzer \
  -v shared-logs:/logs \
  ubuntu \
  bash

Inside analyzer:

# Watch logs in real-time
tail -f /logs/app.log

# Output:
# Log entry: Mon Feb 24 10:30:00 UTC 2026
# Log entry: Mon Feb 24 10:30:05 UTC 2026
# Log entry: Mon Feb 24 10:30:10 UTC 2026
# ... keeps updating

# Both containers accessing same volume! ✓

Part 7: Volume Commands Reference

Complete Volume Commands

# Create volume
docker volume create VOLUME_NAME

# List volumes
docker volume ls

# Inspect volume (see details)
docker volume inspect VOLUME_NAME

# Remove volume
docker volume rm VOLUME_NAME

# Remove all unused volumes
docker volume prune

# Remove volume with force
docker volume rm -f VOLUME_NAME

Using Volumes with Containers

# Run with named volume
docker run -v VOLUME_NAME:/path IMAGE

# Run with bind mount (absolute path)
docker run -v /host/path:/container/path IMAGE

# Run with bind mount (current directory)
docker run -v ${PWD}:/app IMAGE

# Multiple volumes
docker run -v vol1:/data1 -v vol2:/data2 IMAGE

# Read-only volume
docker run -v VOLUME_NAME:/path:ro IMAGE
# :ro = read-only

Modern Mount Syntax

# Volume mount
docker run --mount type=volume,source=VOLUME_NAME,target=/path IMAGE

# Bind mount
docker run --mount type=bind,source=/host/path,target=/path IMAGE

# Read-only mount
docker run --mount type=volume,source=VOL,target=/path,readonly IMAGE

Part 8: Anonymous Volumes

What are Anonymous Volumes?

Anonymous Volume = Volume without a name

Created automatically by Docker when you don't specify name:

docker run -v /data ubuntu
#             ↑
#        No name = anonymous volume

Docker generates random name:

VOLUME NAME
a1b2c3d4e5f6...

When Anonymous Volumes are Used

Example: Some images create anonymous volumes by default

# In Dockerfile
VOLUME /data

When container runs, creates anonymous volume automatically.


Problem with Anonymous Volumes

docker run image1
# Creates anonymous volume: abc123

docker run image1
# Creates ANOTHER anonymous volume: def456

docker run image1
# Creates ANOTHER anonymous volume: ghi789

Result:
├── 3 containers
├── 3 anonymous volumes
└── Hard to manage! ✗

Better: Use named volumes!

docker run -v my-data:/data image
# Same named volume reused ✓

Part 9: Backing Up and Restoring Volumes

Backup a Volume

Method: Use a temporary container to tar the volume

# Backup volume to tar file
docker run --rm -v VOLUME_NAME:/data -v ${PWD}:/backup ubuntu tar czf /backup/backup.tar.gz /data

Explanation:

--rm                        = Remove container after done
-v VOLUME_NAME:/data       = Mount volume to backup
-v ${PWD}:/backup          = Mount current directory
ubuntu                      = Use Ubuntu image
tar czf /backup/backup.tar.gz /data = Compress /data to backup file

Example:

# Backup mysql-data volume
docker run --rm \
  -v mysql-data:/data \
  -v C:\Users\You\backups:/backup \
  ubuntu \
  tar czf /backup/mysql-backup.tar.gz /data

Restore a Volume

# Create new volume
docker volume create restored-data

# Restore from backup
docker run --rm \
  -v restored-data:/data \
  -v ${PWD}:/backup \
  ubuntu \
  bash -c "cd /data && tar xzf /backup/backup.tar.gz --strip 1"

Part 10: Cleaning Up Volumes

Remove Single Volume

# Must stop/remove containers using it first
docker volume rm VOLUME_NAME

If volume is in use:

Error: volume is in use

Solution:
1. docker ps -a (find containers using volume)
2. docker rm CONTAINER (remove those containers)
3. docker volume rm VOLUME_NAME (now works)

Remove All Unused Volumes

docker volume prune

Output:

WARNING! This will remove all local volumes not used by at least one container.
Are you sure you want to continue? [y/N] y

Deleted Volumes:
volume1
volume2
anonymous-volume-abc123

Total reclaimed space: 2.5GB

Be careful! This removes data permanently!


Practice Exercises

Exercise 1: Persistent Counter

Create a container that counts:

# Create volume
docker volume create counter-data

# Run container
docker run -it -v counter-data:/data ubuntu bash

Inside container:

# Create counter file
echo "0" > /data/count.txt

# Increment counter
COUNT=$(cat /data/count.txt)
COUNT=$((COUNT + 1))
echo $COUNT > /data/count.txt
echo "Count: $COUNT"

exit

Run again (multiple times):

docker run -it -v counter-data:/data ubuntu bash

Inside container:

# Increment and show
COUNT=$(cat /data/count.txt)
COUNT=$((COUNT + 1))
echo $COUNT > /data/count.txt
echo "Count: $COUNT"

Each time, count increases! Data persists! ✓


Exercise 2: Development Environment

Setup:

# Create project directory
mkdir my-python-project
cd my-python-project

# Create app.py
echo print("Hello!") > app.py

# Run with bind mount
docker run -it -v ${PWD}:/app python:3.11 bash

Inside container:

cd /app
python app.py

Now edit app.py on your computer, run again in container → See changes!


Summary

What We Learned:

✅ Container data is temporary by default
✅ Volumes provide persistent storage
✅ Three types of mounts:
   ├── Named volumes (managed by Docker)
   ├── Anonymous volumes (random names)
   └── Bind mounts (your directories)
✅ When to use volumes vs bind mounts
✅ Creating and managing volumes
✅ Sharing data between containers
✅ Backing up and restoring
✅ Real-world examples

Key Takeaways:

Volumes:
├── Use for production data
├── Database storage
├── Persistent application data
└── Easy backups

Bind Mounts:
├── Use for development
├── Source code
├── Configuration files
└── Direct file access

No comments:

Post a Comment

Docker Compose

Docker Compose is one of the most powerful and useful Docker tools. Let's dive in! Part 1: What is Docker Compose? The Problem Docker Co...