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

PHASE 1 — Topic 4: How Socket.IO Works Internally (Engine.IO, Transports, and Fallback)

We now understand what Socket.IO is and why it is useful. In this post, we go one level deeper and look at what actually happens behind the ...