Docker Full Tutorial — Part 4: Volumes & Data Persistence

By Suraj Ahir November 13, 2025 6 min read

Docker — Dockerfile
Docker — Dockerfile
← Part 3 Docker Tutorial · Part 4 of 12 Part 5 →

Here is a problem every Docker beginner runs into sooner or later: you run a database container, add some data, stop the container, start it again — and the data is gone. Everything you stored has vanished. This happens because containers are stateless by default. Docker volumes are the solution to this problem, and understanding them is essential for any real-world Docker usage.

Why Containers Are Stateless

When you create a container, Docker gives it a writable layer on top of the read-only image layers. Any files you write inside the container go into this writable layer. When the container is stopped and removed, that writable layer is deleted along with it. The image itself is unchanged — it is the container's individual state that disappears.

For stateless applications like web servers that do not store data locally, this is fine. But for databases, log files, uploaded files, or any data that must survive container restarts, you need a way to store data outside the container's lifecycle. That is exactly what volumes provide.

Three Ways to Persist Data in Docker

Docker gives you three storage options, each suited for different situations:

Named Volumes

Named volumes are the recommended approach for production data. Docker manages their location and lifecycle independently of containers.

Working with Named Volumes
# Create a named volume
docker volume create my-data

# List all volumes
docker volume ls

# Inspect a volume (find where it is stored)
docker volume inspect my-data

# Run container with named volume
docker run -d   --name my-postgres   -e POSTGRES_PASSWORD=mysecretpassword   -v my-data:/var/lib/postgresql/data   postgres:15

# The format is: -v volume_name:container_path

Now if you stop and remove the container, then create a new one using the same volume, all your database data will still be there. The volume persists independently of any container.

Bind Mounts

Bind mounts map a host directory directly into the container. This is extremely useful during development because any change you make to files on your host is immediately visible inside the container — no rebuild required.

Bind Mount for Development
# Map current directory into container
docker run -d   -p 5000:5000   -v $(pwd):/app   --name dev-server   my-python-app:1.0

# On Windows PowerShell
docker run -d -p 5000:5000 -v ${PWD}:/app my-python-app:1.0

With this setup, you edit code on your local machine using your preferred editor, and the changes are instantly available inside the running container. This makes local development with Docker smooth and fast.

Real Example — MySQL with Persistent Volume

Let us run a MySQL database with persistent storage:

MySQL with Volume
# Run MySQL with named volume
docker run -d   --name mysql-db   -e MYSQL_ROOT_PASSWORD=rootpass   -e MYSQL_DATABASE=myapp   -e MYSQL_USER=appuser   -e MYSQL_PASSWORD=apppass   -p 3306:3306   -v mysql-data:/var/lib/mysql   mysql:8.0

# Connect to MySQL inside the container
docker exec -it mysql-db mysql -u root -p

# Stop and remove the container
docker stop mysql-db
docker rm mysql-db

# Start a NEW container using the SAME volume
docker run -d   --name mysql-db-new   -e MYSQL_ROOT_PASSWORD=rootpass   -p 3306:3306   -v mysql-data:/var/lib/mysql   mysql:8.0

# Your data is still there!

Sharing Data Between Containers

Multiple containers can mount the same volume, enabling them to share data. This is useful for scenarios like a web application and a background worker both needing access to uploaded files.

Shared Volume Between Containers
# Both containers share the same uploads volume
docker run -d --name web-app -v uploads:/app/uploads my-web:1.0
docker run -d --name worker -v uploads:/app/uploads my-worker:1.0

Volume Backup and Restore

Since volumes contain important data, knowing how to back them up is critical. Docker does not have a built-in backup command, but this pattern works reliably:

Backup Volume
# Backup: create a tar archive of the volume
docker run --rm   -v my-data:/source   -v $(pwd):/backup   ubuntu tar czf /backup/my-data-backup.tar.gz -C /source .

# Restore: extract archive back into volume
docker run --rm   -v my-data:/target   -v $(pwd):/backup   ubuntu tar xzf /backup/my-data-backup.tar.gz -C /target

Cleaning Up Volumes

Volume Cleanup
# Remove a specific volume (only if no container uses it)
docker volume rm my-data

# Remove all unused volumes
docker volume prune

Warning: Removing a volume permanently deletes all data it contains. Always back up before removing production volumes.

In Part 5, we will learn Docker Networking — how containers talk to each other and to the outside world. This is essential for multi-container applications.

Bind Mounts vs Named Volumes

Docker supports two main types of mounts, each suited to different use cases. Bind mounts map a specific path on the host machine into the container: docker run -v /host/path:/container/path image. They are useful for development — mounting your source code directory into a container so code changes are reflected immediately without rebuilding. Named volumes are managed entirely by Docker: docker run -v myvolume:/container/path image. Docker creates and manages the storage location. Named volumes are the right choice for persistent application data in production — they are not dependent on host directory structure and are easier to backup and migrate.

Volume Usage Examples
# Named volume for database persistence
docker run -d   --name postgres_db   -v postgres_data:/var/lib/postgresql/data   -e POSTGRES_PASSWORD=secret   postgres:15

# Bind mount for development (reflects local changes immediately)
docker run -d   --name myapp_dev   -v $(pwd)/src:/app/src   -p 3000:3000   myapp:dev

# Inspect a volume
docker volume inspect postgres_data

# Backup a volume
docker run --rm -v postgres_data:/data -v $(pwd):/backup   alpine tar czf /backup/postgres_backup.tar.gz /data

Practice Exercise

Run a PostgreSQL container with a named volume. Connect to it and create a database with some test data. Stop and remove the container. Start a new PostgreSQL container using the same named volume and verify your data is still there. This exercise demonstrates the fundamental purpose of volumes — data that outlives the container that created it.

Disclaimer: This content is for educational purposes only. SRJahir Tech does not guarantee any specific outcome or job placement.