Docker Volumes and Data Persistence: Managing State in Containers πŸ’Ύ

Source: Dev.to | Dec 23, 2025 (UTC)

One of the most challenging aspects of working with Docker has been figuring out data persistence. Containers are ephemeral by nature, but most real-world applications need to store data. In this post, I’ll share what I’ve learned about managing persistent data with Docker.

The Ephemeral Nature of Containers

First, let’s understand the problem. Docker containers have a virtual file system that resets when a container is removed. Here’s what happens:

# Start a container and create a file docker run -it --name temp ubuntu bash # (Inside container) touch /test.txt # (Inside container) exit # Start the container again - file still exists docker start -i temp # (Inside container) ls /test.txt # Output: /test.txt # Now remove and recreate the container docker rm temp docker run -it --name temp ubuntu bash # (Inside container) ls /test.txt # Output: ls: cannot access '/test.txt': No such file or directory

Enter fullscreen mode

Exit fullscreen mode

When the container is removed, all data inside is lost. This is a big problem for databases, user uploads, or any stateful application.

Docker Volumes

Docker volumes are the solution to this problem. They’re specially designed locations outside of the container’s filesystem where data can persist.

Creating and Managing Volumes

# Create a volume docker volume create my-data # List volumes docker volume ls # Inspect a volume docker volume inspect my-data # Remove a volume docker volume rm my-data # Remove all unused volumes docker volume prune

Enter fullscreen mode

Exit fullscreen mode

Volumes are stored in a location managed by Docker, typically /var/lib/docker/volumes/ on Linux systems.

Using Volumes with Containers

# Run a container with a volume docker run -v my-data:/app/data nginx # Run a container with an anonymous volume docker run -v /app/data nginx

Enter fullscreen mode

Exit fullscreen mode

In the first example, my-data is the volume name, and /app/data is the mount point inside the container. Any data written to /app/data will persist in the my-data volume.

Types of Docker Storage

There are three main ways to persist data with Docker:

1. Named Volumes

docker run -v my-logs:/var/lib/mysql/data mysql

Enter fullscreen mode

Exit fullscreen mode

How it works: Docker creates and manages this volume. The data is stored in /var/lib/docker/volumes/my-logs/_data on the host, but you typically don’t need to access it directly.

Use cases:

Advantages:

2. Bind Mounts

docker run -v /home/host/data:/var/lib/mysql/data mysql

Enter fullscreen mode

Exit fullscreen mode

How it works: Maps a directory on your host machine directly into the container. Any files in /home/host/data will be available inside the container at /var/lib/mysql/data and vice versa.

Use cases:

Advantages:

3. Anonymous Volumes

docker run -v /var/lib/mysql/data mysql

Enter fullscreen mode

Exit fullscreen mode

How it works: Similar to named volumes but with a randomly generated name. Docker itself takes care of the volume creation in host, we just mention the path in the Docker container.

Use cases:

Bind Mounts vs. Named Volumes: Choosing the Right Option

I spent a long time figuring out when to use which option. Here’s what I learned:

Feature

Bind Mounts

Named Volumes

Location

Any directory on host

Managed by Docker in /var/lib/docker/volumes

Path Specification

Full host path required

Just the volume name

Portability

Less portable (host-dependent)

More portable

Host Modification

Can be modified directly on host

Requires Docker commands

Performance

Depends on host filesystem

Optimized by Docker

Usage

Development, config files

Production data

I generally use:

Volume Drivers

Docker supports volume drivers that extend storage capabilities:

# Create a volume with a specific driver docker volume create --driver=local my-volume # Create a volume with driver options docker volume create --driver=local \ --opt type=nfs \ --opt o=addr=192.168.1.1,rw \ --opt device=:/path/to/dir \ my-nfs-volume

Enter fullscreen mode

Exit fullscreen mode

Common volume drivers:

Data Management Strategies

Stateless vs. Stateful Containers

Stateless containers don’t store persistent data:

Stateful containers need to store data:

I’ve found it’s best to:

  1. Make as many components stateless as possible
  2. Use volumes only for truly stateful parts of the application
  3. Consider using managed services for stateful components (e.g., RDS for databases)

Data Backup and Recovery

Backing up volume data is essential. Here’s how I do it:

# Backup a volume to a tar file docker run --rm -v my-volume:/source -v $(pwd):/backup \ alpine tar -czf /backup/my-volume-backup.tar.gz -C /source . # Restore from a backup docker run --rm -v my-volume:/target -v $(pwd):/backup \ alpine sh -c "tar -xzf /backup/my-volume-backup.tar.gz -C /target"

Enter fullscreen mode

Exit fullscreen mode

For automated backups, I put this in a cron job or CI/CD pipeline.

Sharing Data Between Containers

There are two main ways to share data between containers:

1. Using a shared volume:

# Create a shared volume docker volume create shared-data # Use it in multiple containers docker run -v shared-data:/app/data container1 docker run -v shared-data:/app/data container2

Enter fullscreen mode

Exit fullscreen mode

Real-World Examples

Running a Database with Persistent Storage

# Create a volume for the database docker volume create postgres-data # Run PostgreSQL with the volume docker run -d \ --name postgres \ -e POSTGRES_PASSWORD=mysecretpassword \ -v postgres-data:/var/lib/postgresql/data \ -p 5432:5432 \ postgres

Enter fullscreen mode

Exit fullscreen mode

Now, even if the container is removed, the data will persist in the postgres-data volume.

Development Environment with Code Mounting

# Mount current directory for development docker run -d \ --name node-app \ -v $(pwd):/app \ -w /app \ -p 3000:3000 \ node:14 \ npm start

Enter fullscreen mode

Exit fullscreen mode

This mounts your current directory into the container at /app. When you change code on your host, it’s immediately reflected in the container.

Sharing Configuration Files

# Mount a specific config file docker run -d \ --name nginx \ -v $(pwd)/nginx.conf:/etc/nginx/nginx.conf:ro \ -p 80:80 \ nginx

Enter fullscreen mode

Exit fullscreen mode

The :ro suffix makes the mount read-only, preventing the container from modifying your config file.

Best Practices I’ve Learned

  1. Use named volumes for important data

  2. Use bind mounts during development

  3. Make containers as stateless as possible

  4. Be careful with permissions

  5. Always back up volumes

  6. Consider volume labels for organization

docker volume create --label project=myapp myapp-data

Enter fullscreen mode

Exit fullscreen mode

  1. Clean up unused volumes regularly

docker volume prune

Enter fullscreen mode

Exit fullscreen mode

Managing Volume Permissions

One issue I frequently ran into was permission problems with volumes. Here’s how I solved it:

# Set permissions before mounting docker run --rm -v my-volume:/data alpine chmod 777 /data

Enter fullscreen mode

Exit fullscreen mode

Conclusion

Understanding Docker volumes has been essential for my containerized applications. The ephemeral nature of containers makes volumes necessary for any application that needs to store data.

To summarize:

In the next post, I’ll cover Docker networking – how containers communicate with each other and the outside world.

Next up: “Docker Networking: Connecting Containers”

For further actions, you may consider blocking this person and/or reporting abuse

Π—Π°Π»ΠΈΡˆΠΈΡ‚ΠΈ Π²Ρ–Π΄ΠΏΠΎΠ²Ρ–Π΄ΡŒ

Π’Π°ΡˆΠ° e-mail адрСса Π½Π΅ ΠΎΠΏΡ€ΠΈΠ»ΡŽΠ΄Π½ΡŽΠ²Π°Ρ‚ΠΈΠΌΠ΅Ρ‚ΡŒΡΡ. ΠžΠ±ΠΎΠ²β€™ΡΠ·ΠΊΠΎΠ²Ρ– поля ΠΏΠΎΠ·Π½Π°Ρ‡Π΅Π½Ρ– *

Π§ΠΈΡ‚Π°ΠΉΡ‚Π΅ Ρ‚Π°ΠΊΠΎΠΆ