Migrating a Docker volume between hosts moves persistent container data without depending on the source host's local storage layout. The usual pattern is to stop the writer, export the named volume to a portable tar archive, copy that archive to the destination host, and restore it into a new volume there.
Named volumes are local resources on each Docker host, so the destination machine cannot read the source host's volume directly. A short-lived helper container can mount the volume with --mount, write the archive into a host directory, and then unpack that same archive into a destination volume without needing the application image itself.
The flow below was verified against current Docker Engine volume behavior using a standard local named volume and an alpine helper container. Stop every container that writes to the source volume before exporting it, restore only into a new or empty destination volume, and keep the same container mount path on the receiving workload so the migrated files appear where the application expects them.
$ docker container stop notes-db notes-db
If multiple containers write to the same volume, stop all of them before exporting it.
Leaving an active writer running during the export can capture half-written files or application state that does not replay cleanly on the destination host.
$ mkdir -p /srv/docker-backups
The helper container mounts this directory as a bind mount so the tarball lands on the host instead of inside a disposable container filesystem.
$ docker run --rm --mount source=notes-data,target=/from --mount type=bind,src=/srv/docker-backups,dst=/backup alpine tar -czf /backup/notes-data.tar.gz -C /from .
Docker documentation currently prefers --mount over -v because it is more explicit and supports the full set of volume options.
$ tar -tzf /srv/docker-backups/notes-data.tar.gz ./ ./app.sqlite ./settings.ini ./uploads/ ./uploads/readme.txt
This quick check confirms that the tarball contains the expected volume paths before the transfer step begins.
$ ssh admin@docker-b.example.com mkdir -p /srv/docker-backups
$ scp /srv/docker-backups/notes-data.tar.gz admin@docker-b.example.com:/srv/docker-backups/
Replace admin, docker-b.example.com, and the host path with the real destination values used in the environment.
$ docker volume create notes-data notes-data
Restore into a new or empty volume only. Reusing a volume that already contains data can leave stale files mixed with the migrated copy.
$ docker run --rm --mount source=notes-data,target=/to --mount type=bind,src=/srv/docker-backups,dst=/backup alpine tar -xzf /backup/notes-data.tar.gz -C /to
$ docker run --rm --mount source=notes-data,target=/data alpine ls -al /data total 20 drwxr-xr-x 3 root root 4096 Apr 16 08:49 . drwxr-xr-x 1 root root 4096 Apr 16 08:49 .. -rw-r--r-- 1 root root 14 Apr 16 08:49 app.sqlite -rw-r--r-- 1 root root 13 Apr 16 08:49 settings.ini drwxr-xr-x 2 root root 4096 Apr 16 08:49 uploads
Docker copies files from the container destination path into an empty volume by default when that volume is first mounted by a workload container. Restoring the archive before the application starts avoids mixing those seed files with migrated data, and the volume-nocopy mount option is available when an empty first mount must not copy image-side content.
The application start command is workload-specific, but the mount path must stay the same or the restored data will not appear where the container expects it.