Your container stops and data disappears. It happens when a PHP app writes uploads to an ephemeral filesystem, or when a MySQL container inside a container wipes everything on restart. If you're using Docker in production, you know data must outlive the container lifecycle. We, at Meteora Web, have been through this with clients losing orders and invoices because the volume was misconfigured. In this guide, we break down how Docker Volumes work, when to use them, and how to manage them without losing sleep — or revenue.
Why Does a Container Without a Volume Lose Data on Every Restart?
Containers are designed to be ephemeral: lightweight, reproducible, replaceable. The container's filesystem is temporary. When you stop and delete it with docker rm, everything you wrote inside — uploads, logs, sessions, SQLite databases — is gone. For a real application, that's a disaster. Imagine a WooCommerce ecommerce losing all product images on every deploy. Or a Laravel ERP that wipes monthly reports. We've seen it: a client had a MariaDB instance running in a container with no volume. Every WordPress update wiped the database. We had to recover from a manual backup. A lesson learned the hard way.
The solution is called a volume. Docker offers three persistence mechanisms: volumes (managed by Docker), bind mounts (direct mount of a host directory), and tmpfs (in RAM, for temporary data only). The first two are what you use in production. The third we only use for session caches or temporary files.
What you can do right now: check your docker-compose.yml. If there's no volumes: section under services, your data probably isn't persistent. Stop everything, add a volume, and test with a dummy file. Do it now, not after the next crash.
Sponsored Protocol
How Do Docker Volumes Work and Which One to Choose?
Docker volumes are directories created and managed by Docker itself, stored in /var/lib/docker/volumes/ on the host. They are the recommended choice for performance and portability. You can back them up, move them between hosts, manage them with remote drivers (NFS, cloud). Bind mounts mount a host directory (e.g., /home/user/app/uploads) into the container. They are convenient in development for live code reloading, but in production they create host filesystem dependencies — less portable, more risky for security.
We, at Meteora Web, almost always use named volumes in production. Here's why: if you need to move the container to another server, just copy the volume folder. With a bind mount, you must recreate the exact directory structure on the new host. A wrong path and the app can't see the data. We've seen the damage: a team migrating a Laravel project forgot to create the storage folder — 500 errors for days.
Here's a practical example with a docker-compose.yml for WooCommerce with MariaDB:
version: '3.8'
services:
db:
image: mariadb:10.11
volumes:
- db_data:/var/lib/mysql
environment:
MYSQL_ROOT_PASSWORD: rootpass
MYSQL_DATABASE: woocommerce
MYSQL_USER: wpuser
MYSQL_PASSWORD: wppass
wordpress:
image: wordpress:latest
volumes:
- wp_content:/var/www/html/wp-content
ports:
- "8080:80"
depends_on:
- db
volumes:
db_data:
wp_content:
Here db_data and wp_content are named volumes. If you run docker-compose down -v, be careful: it removes the volumes too. To stop without data loss, use docker-compose down without -v.
Sponsored Protocol
What you can do right now: in your project, declare volumes at the service level and create their names in the volumes: block at the bottom. Then do docker-compose up -d and verify with docker volume ls that they appear.
Bind Mount or Named Volume — Which to Use in Production for Storage?
The choice depends on the use case. Named volumes are safer: you don't expose the host filesystem to the container, and Docker handles permissions automatically. Bind mounts are useful when you need to share files directly between host and container, like SSL certificates or configuration files. But beware: with bind mounts, if you modify files from the container, you modify them on the host. A bug in the code can delete critical data.
We, at Meteora Web, have a practical rule: named volumes for application data (databases, uploads, storage), bind mounts only for configurations and development. For example, on a Laravel platform we manage, the storage volume is a named volume, while the certs folder with SSL certificates is a bind mount. This way application data is portable and safe, while configuration is easily updatable from outside.
What you can do right now: review your services. If you have bind mounts for database or upload data, consider migrating to named volumes. To do this, create the volume with docker volume create myapp_data and mount it instead of the bind mount.
How to Backup and Restore Docker Volumes?
Docker volumes are not backups. If the host disk fails, you lose everything. The good news is that backing up a volume is straightforward: just archive its contents. Here's a script we use internally for daily client volume backups:
Sponsored Protocol
#!/bin/bash
# backup_volume.sh — backup a Docker volume
# Usage: ./backup_volume.sh volume_name [container_name]
VOLUME=$1
CONTAINER=$2
BACKUP_DIR="/backup/volumes"
DATE=$(date +%Y%m%d_%H%M%S)
mkdir -p $BACKUP_DIR
echo "Backing up volume $VOLUME..."
docker run --rm --volumes-from $CONTAINER \
-v $BACKUP_DIR:/backup \
busybox tar -czf /backup/${VOLUME}_${DATE}.tar.gz /data
echo "Backup saved to ${BACKUP_DIR}/${VOLUME}_${DATE}.tar.gz"
To restore: docker run --rm -v volume_name:/data -v $(pwd):/backup busybox tar -xzf /backup/backup_file.tar.gz -C /data. This mounts the volume and the backup file, then extracts the contents.
Caution: hot backups (with the container running) can cause corruption for databases. For MariaDB or PostgreSQL, use native tools like mysqldump or pg_dump. We schedule SQL dumps before the volume backup.
What you can do right now: create a /backup/volumes directory on the server, set up an automated backup script via cron (0 3 * * * /path/to/backup_volume.sh db_data mysql), and test a restore on a staging environment. If you don't test the restore, the backup doesn't exist.
Which Storage Drivers to Use for Shared Volumes Across Multiple Hosts?
If you have multiple Docker servers in a cluster (Swarm, Kubernetes, or just separate machines), local volumes aren't enough. You need a driver that makes storage shared. Docker supports drivers like NFS, CIFS/SMB, Amazon EFS, Azure File. We, for Italian clients with tight budgets, recommend NFS on a dedicated Linux server. It's free, stable, and integrates well with Docker.
Sponsored Protocol
Here's how to create a Docker volume based on NFS:
docker volume create \
--driver local \
--opt type=nfs \
--opt o=addr=192.168.1.100,rw,nfsvers=4 \
--opt device=:/exported/path \
shared_volume
Then mount it like a normal volume: -v shared_volume:/app/storage.
Watch out for network latency: a database on NFS can become very slow. For databases, stick with local volumes or block storage (iSCSI, EBS). We use shared storage for static files and uploads, not for transactional data.
What you can do right now: if you have a cluster, set up an NFS server (just apt install nfs-kernel-server), export a directory, and create a Docker volume with the command above. Then mount it on one or more services.
Common Mistakes and How to Avoid Them in Volume Management
We've seen plenty. Here are the three worst:
1. Volume not declared in the root block. Writing volumes: db_data: under the service is not enough: you must also declare it in the volumes: block at the bottom of the file, otherwise Docker creates it as anonymous and loses it on down.
2. Wrong permissions. If the container writes as user www-data (uid 33) but the volume has root permissions, the app can't write. Solution: in the Dockerfile or entrypoint, set the user or use chown at startup.
3. Volume never cleaned. Unremoved volumes take up space. Use docker system prune -a --volumes cautiously only in development environments. In production, monitor space with du -sh /var/lib/docker/volumes/.
We, at Meteora Web, solved a case where a client had TB of accumulated logs in a WordPress volume because the logging plugin didn't rotate. We added a cron that for log volumes applies a 7-day retention with find /path -type f -mtime +7 -delete.
Sponsored Protocol
What you can do right now: check space usage per volume with docker system df. If a volume exceeds expectations, inspect its contents with docker run --rm -it -v volume_name:/data alpine ls -la /data.
What to Do Now to Secure Your Docker Data
- Audit every production service: verify that databases, uploads, and storage use named volumes, not bind mounts for critical data.
- Set up automated backups: schedule a script that dumps databases and archives volumes to a location outside the server (NFS, cloud, remote backup).
- Test a restore: stop a container, delete the volume, restore from backup, and restart. If it works in staging, it will work in production.
- Monitor disk space: configure an alert if the volume disk exceeds 80%. Use Prometheus + Grafana or even a simple bash script with mail.
- Document the configuration: keep a README listing every volume with its purpose and backup policy. When a new developer arrives, they know exactly what to do.
We, at Meteora Web, apply these same principles to every project we manage. Docker volumes are like accounting: if you neglect them, the final balance is always in the red. Treat them with the same rigor you use to manage client invoices — because when data is lost, the cost is real.
For a deeper dive into the entire Docker ecosystem from prototyping to production for SMEs, visit our pillar page on Docker and containerization.