If the container uses a Docker volume for data (say, a MySQL/PostgreSQL database, uploaded files, logs...), migration can't just be "docker save + docker load," because the real data lives in a volume on the host, not inside the image.
This post walks through two common ways to migrate a container with a volume to a new host while keeping the data intact:
- Using a .tar archive to back up and restore the data.
- Using rsync to sync data between two hosts — good for cases needing short downtime or continuously changing data.
How does Docker manage and store volume data on the host?
When you create a volume, Docker stores the data in a system directory on the host — by default:
/var/lib/docker/volumes/<volume_name>/_data
For example, with this docker-compose.yml:
services:
db:
image: mysql:8.0
volumes:
- db_data:/var/lib/mysql
volumes:
db_data:
Docker will create a volume named db_data, and all the MySQL data actually lives in the folder:
/var/lib/docker/volumes/db_data/_data
A few characteristics of Docker volumes:
- Volumes are managed by Docker independently of the container.
- When a container is removed (
docker rm), the data in the volume still remains. - A volume is only lost when you actually run
docker volume rm <volume_name>.
So if you want to move the data, you just need to move the entire _data directory of the volume to the new host and re-attach it to the corresponding container.
Solution 1: Compress the volume's data into a .tar file and restore
You can probably already guess how this works from the name. We compress the current host's volume data into an archive, then copy it to the new host. Here are the steps:
- On the old host, identify the volume you need to back up:
docker volume ls
- Say we see the volume
db_data. Create a .tar.gz backup file:
docker run --rm \
-v db_data:/volume_data \
-v $(pwd):/backup \
alpine sh -c "cd /volume_data && tar czf /backup/db_data_backup.tar.gz ."
- -v db_data:/volume_data: mounts the volume to back up into a temporary container.
- -v $(pwd):/backup: mounts the current directory to store the archive.
- The archive will be created at $(pwd)/db_data_backup.tar.gz.
- Transfer the archive to the new host:
scp db_data_backup.tar.gz user@new-host:/home/user/backup/
- On the new host, create an empty volume with the same name:
docker volume create db_data
- Extract the data into the new volume:
docker run --rm \
-v db_data:/volume_data \
-v $(pwd):/backup \
alpine sh -c "cd /volume_data && tar xzf /backup/db_data_backup.tar.gz"
- Restart the container on the new host:
docker compose up -d
- Verify the data:
docker exec -it <container_name> ls /var/lib/mysql
Pros
- Simple, easy to do.
- No network configuration needed between the two hosts.
- Good fit for small environments needing periodic backup/archival.
Cons
- You have to stop the container during backup to avoid new writes.
- Compressing/extracting takes time if the data is large.
- Not suitable for environments requiring low downtime.
Solution 2: Use rsync to sync data between two hosts
This approach syncs two folders — a good fit if you want to gradually move a large amount of data, reduce downtime, or handle continuously changing data. Here's how:
Step 1. Identify the volume's data directory:
docker volume inspect db_data | grep Mountpoint
Example result:
"Mountpoint": "/var/lib/docker/volumes/db_data/_data"
Step 2. Use rsync to sync the data:
On the new host, run:
rsync -avz -e ssh user@old-host:/var/lib/docker/volumes/db_data/_data/ \
/var/lib/docker/volumes/db_data/_data/
Or if you'd rather do it from the old host:
rsync -avz /var/lib/docker/volumes/db_data/_data/ \
user@new-host:/var/lib/docker/volumes/db_data/_data/
- -a: preserves all permissions, files, and directories.
- -v: shows detailed progress.
- -z: compresses data during transfer to save bandwidth.
- -e ssh: transfers securely over SSH.
Step 3. Verify the data after syncing:
ssh user@new-host "ls -lh /var/lib/docker/volumes/db_data/_data/"
Step 4. Minimize downtime with incremental rsync
The first sync can transfer all the data (which takes time), but afterward you can re-run rsync multiple times to sync just the small changes.
For example:
# First sync (while the system is still running)
rsync -avz user@old-host:/var/lib/docker/volumes/db_data/_data/ /tmp/db_sync/
# Once the new host is ready, stop the container on the old host
docker compose down
# Run rsync one final time to catch the latest data
rsync -avz --delete user@old-host:/var/lib/docker/volumes/db_data/_data/ \
/var/lib/docker/volumes/db_data/_data/
# Start the container on the new host
docker compose up -d
Pros
- Can reduce downtime to nearly zero thanks to continuous syncing.
- Good fit for large data sets or systems that are still running.
- Can be run multiple times to ensure the latest data.
Cons
- Requires an SSH network connection between the two hosts.
- Must ensure compatible versions and permissions.
- If the volume is being written to continuously, a short downtime may still be needed for the final rsync.
A few more notes
- Always check the mount point path: Use
docker volume inspect <volume_name>to make sure you're moving the correct data. - Be careful with permissions and users: If the container runs as a specific user (e.g. a mysql UID different from root), make sure the data keeps its access permissions. You may need to
chownagain after restoring. - Check the project name when using Docker Compose: Volumes may be named with a prefix (e.g.
myapp_db_data) — make sure the name matches thedocker-compose.ymlon the new host. - Downtime and consistency: Stop the container before backing up or doing the final rsync to avoid losing data.
- Docker/image version: Make sure both hosts use the same Docker version and the same image tag (e.g.
mysql:8.0) to avoid data incompatibility issues.
Wrapping up
Migrating a container with a volume to a new host isn't just about copying an image — the most important part is making sure the data in the volume moves safely and consistently.
- If you want something simple, a one-time backup, no network needed between the two hosts → use the .tar solution.
- If you need continuous syncing, reduced downtime, large data → go with rsync.
Depending on your needs and the scale of your system, you can combine both: use rsync to sync ahead of time, then .tar for long-term storage.
Understanding how Docker manages data will help you migrate confidently and avoid losing data unintentionally.
If this post was useful, feel free to Follow and Upvote to support me. Thank you! ❤️
If you're running into technical challenges, need help with systems, DevOps tools, or career direction, I'm confident I can help. Get in touch at hoangviet.io.vn — I'd love to chat and collaborate with you.