Quickie: Move docker container to a new machine

Recently I re-organized Prantas’s servers, abandoning the old one-box-to-rule-them-all setup. That meant some Docker containers had to move from machines scheduled for deletion to their new homes. For anyone facing the same headache, here’s the quick(ish) way I did it.

Quickie: Move docker container to a new machine
Image Generated in ChatGPT

Prerequisites

Before we start, it is presumed that you have

  1. two servers (the source and the destination) up and running
  2. docker installed on both machines
  3. set up the source server to have ssh access to the destination server

On the SOURCE server

1. Get the image name

docker inspect my-awesome-container --format 'IMAGE={{.Config.Image}}'
Response:
IMAGE=httpd:latest

2. Get the bound ports

docker inspect my-awesome-container --format 'PORTS={{json .HostConfig.PortBindings}}'
Response:
PORTS={"80/tcp":[{"HostIp":"","HostPort":""}]}

3. Get the enviroment variables

docker inspect my-awesome-container --format 'ENV={{json .Config.Env}}'
Response
TH=/usr/local/apache2/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin","HTTPD_PREFIX=/usr/local/apache2","HTTPD_VERSION=2.4.62","HTTPD_SHA256=674188e7bf44ced82da8db522da946849e22080d73d16c93f7f4df89e25729ec","HTTPD_PATCHES="]

4. Get the volumes

docker inspect my-awesome-container --format '{{range .Mounts}}{{println .Type}} {{println .Source}} {{println .Destination}}{{end}}'
Response:
volume
 /var/lib/docker/volumes/258869c20bedb3e8c63950e6d92805fcdbb67f27999fb76168403baa824c3c10/_data
 /usr/local/apache2/htdocs

5. Stop the container if running

docker stop my-awesome-container

6. Create a temp image to stream to the destination

docker commit my-awesome-container my-awesome-container-temp

7. Stream the image to the destination

docker save my-awesome-container-temp | ssh 192.168.0.1 'docker load'
Response
Loaded image: my-awesome-container-temp:latest
💡
In this example you will need to change the 192.168.0.1 to the server IP you want to use. If the user of the destination server is different than the source server, you need to prepend the command with the username (e.g. root@192.168.0.1)
💡
For IPv6, you need the -6 flag after the ssh command (e.g ssh -6 192.168.0.1)

8. Archive the volumes

Remember the command that returned the volume? For my case the response was:

volume
 /var/lib/docker/volumes/258869c20bedb3e8c63950e6d92805fcdbb67f27999fb76168403baa824c3c10/_data
 /usr/local/apache2/htdocs

which means that the /var/lib/docker/volumes/258869c20bedb3e8c63950e6d92805fcdbb67f27999fb76168403baa824c3c10/_data path in the host machine (the one the docker runs) is linked as a volume to the /usr/local/apache2/htdocs path in the guest machine (the docker container)

We want to archive this volume, stream it to the destination server and then bind it to the new container created in the destination server.

To do so, run:

docker run --rm -v 258869c20bedb3e8c63950e6d92805fcdbb67f27999fb76168403baa824c3c10:/v \
  busybox tar -C /v -czf - . \
  > my-awesome-container-data.tar.gz

this command will temporarily create a container named busybox to run the tar that creates the topiki-istoria-data.tar.gz archive

Response

Unable to find image 'busybox:latest' locally
latest: Pulling from library/busybox
80bfbb8a41a2: Pull complete
Digest: sha256:d82f458899c9696cb26a7c02d5568f81c8c8223f8661bb2a7988b269c8b9051e
Status: Downloaded newer image for busybox:latest
💡
Don't let the --rm flag spook you. The volume itself is not deleted — only the throwaway container (busybox) is.

9. Copy the archive to destination server

scp my-awesome-container-data.tar.gz pandor1an@10.0.0.4:/home/pandor1an
Response
my-awesome-container-data.tar.gz          100%  195   126.5KB/s   00:00

On the Destination Server

10. Create a new volume

I am going to give it a sane name instead of the hash

docker volume create my-awesome-container-data

11. Extract the backup into the new volume

cat /home/pandor1an/my-awesome-container-data.tar.gz | docker run --rm -i -v my-awesome-container-data:/v busybox tar -C /v -xzf -

Response

Unable to find image 'busybox:latest' locally
latest: Pulling from library/busybox
80bfbb8a41a2: Pull complete
Digest: sha256:d82f458899c9696cb26a7c02d5568f81c8c8223f8661bb2a7988b269c8b9051e
Status: Downloaded newer image for busybox:latest

12. Run the container

docker run -d --name my-awesome-container \
  -p 1080:80 \
  -v my-awesome-container:/usr/local/apache2/htdocs \
  my-awesome-container-temp

Mission accomplished!

Image generated in ChatGPT