Challenges and Solutions in Docker Container Migration Across Architectures

About Docker Container Migration

In this article, we will explain how to migrate Docker containers and images to a different virtual machine. Using an SSH-connected Ubuntu 24.04 as an example, we’ll introduce the specific steps for transferring containers and images between two Linux instances. The same procedure can also be applied in environments like WSL or cloud services. For instance, in WSL, Docker can be conveniently used in a local development environment, while in cloud services, it allows for efficient migration between remote servers.

Prerequisites: Docker should already be installed, but no containers or images are present yet. From this state, we will create a sample container and perform the migration process.

What’s the Difference Between Docker Export and Docker Save?

When you want to migrate Docker containers or images, you can use either the docker export or docker save command. However, these commands serve different purposes, and the results will vary significantly depending on which one you use. Here, we will briefly explain the differences between these commands.

1. Docker Export: Migrate Only the File System

The docker export command exports only the file system of a container. This means that all the files and directories inside the container are saved, but container configurations, such as entry points (e.g., which service to start), are not included.

Use case: This is useful when you only want to transfer the data and files from inside the container to another environment.

Limitation: You will need to manually configure and restart the services on the destination environment.

Example:

docker export -o backup.tar mycontainer

This command exports the file system of mycontainer into a file named backup.tar.


2. Docker Save: Migrate the Entire Image

The docker save command saves the entire Docker image. This includes not only the file system but also container configurations, entry points, and environment variables. As a result, when you import the image on the destination environment, you can restart the container with its original settings intact.

Use case: This is used when you want to fully replicate a container, including its settings and the services it runs.

Advantage: Since the configuration and entry points are included, the container can be restarted as is on the destination environment.

Example:

docker save -o myimage.tar myimage:latest

This command saves the image myimage:latest into a file named myimage.tar.


Summary

  • docker export migrates only the file system, and you’ll need to manually start the services on the destination environment.
  • docker save allows for migrating a complete image, including the container’s configuration and service startup information, making it easier to replicate the container on the destination environment.

Step 1: Create a Sample Container

Let’s start by creating a sample container. In this case, we’ll use an Nginx web server container. Use the following command to create and launch the Nginx container:

docker run --name mynginx -d -p 8888:80 nginx
  • --name mynginx specifies the name of the container.
  • -d runs the container in the background.
  • -p 8888:80 maps port 8888 on the host to port 80 inside the container.

The following image shows the configuration for port 8080.

Now, if you access http://<server-ip-address>:8888 in your browser, you should see the Nginx welcome page.

The following image shows the configuration for port 8080.

Step 2: Export the Container

Next, we will export the container we created for migration to another virtual machine. Use the following command to output the container’s file system as an archive:

docker export -o bk.tar mynginx

This command exports the container named mynginx to a file named bk.tar.

What is Exporting?: Exporting consolidates the container’s file system into a single file (in tar format). By transferring this file to another machine and re-importing it, you can recreate the same environment.


Step 3: Set File Permissions

Next, you need to set the file’s permissions. If the permissions are not set correctly, you may not be able to download the file.

sudo chmod 666 bk.tar

This command grants read and write permissions to all users for the file bk.tar. By using sudo, general users can perform necessary operations on system files and folders. The backup file will be created in the directory where this command is executed.

If you want to save the file to a specific directory, you can specify the location. For example, to save it in the /backup directory, use the following command:

docker export -o /backup/bk.tar mynginx

This command saves the backup file bk.tar in the /backup directory. It’s a good idea to make sure the directory exists beforehand. However, despite these steps, the migration process failed on the destination machine. Below, I’ll explain why it failed and share my experience.


Failure with Docker Export: A Misunderstanding

After creating the sample Nginx container, I tried to migrate it using docker export. Since it was my first migration attempt, I mistakenly believed that docker export would migrate everything in the container.


Steps that Failed

  1. Exporting the Nginx Container I used the following command to export the container’s file system:
    docker export -o /backup/bk.tar mynginx
    The file seemed to have been created correctly, and I thought the entire file system had been saved.
  2. Importing on the Destination Machine On the destination server, I imported the exported file using this command:
    docker import /tmp/bk.tar
    Everything proceeded smoothly up to this point, and the image was imported.
  3. Starting the Container I attempted to start the container using the following command:
    docker run --name mynginx -d -p 8888:80 <image ID>
    However, when I tried to access http://<server-ip-address>:8888 in the browser, the Nginx welcome page did not appear. No matter how many times I tried, the result was the same.

Reason for the Failure: Missing Entry Point

The main problem here was that I didn’t realize that docker export only exports the container’s file system. While the file system was migrated successfully, crucial information like the entry point to start Nginx (such as docker-entrypoint.sh) and the commands to execute were not included. As a result, Nginx didn’t start automatically on the destination machine.

Specifically, the following information was not exported:

  • The command to start Nginx (/usr/sbin/nginx -g 'daemon off;')
  • Environment variables and configurations inside the container

Due to this, Nginx didn’t start on the destination machine, and I couldn’t access it from the browser.


Lesson Learned: Understanding the Difference Between Docker Export and Docker Save

From this experience, I learned that docker export only exports the file system, so the container’s configuration and running services need to be manually set up. If you want to automatically start a service like Nginx, you need to use docker save to migrate the entire image.

I will now explain the correct method using docker save, which ultimately worked. Before that, I’ll explain how to transfer files between the source and destination machines, as well as how to handle authentication keys.


When using both docker export and docker save, files are created, and these files need to be transferred to the destination machine. One way to download the file to your local PC is by using TeraTerm’s SSH SCP feature. If the file is large, the download may take some time.

Using VS Code’s SFTP Extension for Easier File Transfers

While you can use TeraTerm’s SSH SCP function, a simpler method is to use the SFTP extension in VS Code.


Steps for Downloading Files Using VS Code

  1. Installing VS Code First, ensure that VS Code is installed. You can download it from the official website.
  2. Installing the SFTP Extension Next, search for “SFTP” in the VS Code marketplace and install the extension.
  3. Connecting via SFTP and Downloading Files Once the extension is installed, you can easily connect to the remote server. In the file explorer on the left sidebar, select the file on the server, right-click, and choose “Download.”

Note: Normally, you would need to configure the sftp.json file, but in some cases, the SFTP extension automatically applies the correct settings, allowing you to download files without needing to modify the configuration.

Sending Files Using rsync

If the file transfer is large and time-consuming, you can use the rsync command to send files efficiently. In this section, we’ll introduce how to send files from the source machine to the destination machine.

Step 1: Creating a Private Key

First, generate a private key that will be used for SSH connections. Use the following command to generate both the private and public keys:

ssh-keygen -t rsa -b 4096 -f ~/.ssh/id_rsa
  • -t rsa specifies the use of RSA encryption.
  • -b 4096 sets the key size in bits (a larger value is recommended for better security).
  • -f ~/.ssh/id_rsa designates the location where the key files will be saved.

When this command is executed, a private key (id_rsa) and a public key (id_rsa.pub) will be generated. Below is the actual output of the command.

Generating public/private rsa key pair.
Enter passphrase (empty for no passphrase):
Enter same passphrase again:
Your identification has been saved in /home/mamu/.ssh/id_rsa
Your public key has been saved in /home/mamu/.ssh/id_rsa.pub
The key fingerprint is:
SHA256:21WN0159kkk9RDkWhnNdxMWA39ua0xWEB4RPJXD56oA mamu@MB-T500
The key’s randomart image is:
+—[RSA 4096]—-+
| .+*@%X|
| .oBo#*|
| o.#.O|
| o.*+|
| S . . . =|
| E o . .o|
| . . o +.|
| .+ .|
| . |
+—-[SHA256]—–+

Step 2: Deploying the Public Key to the Destination Server

Next, copy the generated public key to the destination server. Use the ssh-copy-id command to deploy the public key:

ssh-copy-id -i ~/.ssh/id_rsa.pub user@<destination-IP-address>

With this, SSH connections from the source to the destination server will be enabled. Below is an example of the actual command:

ssh-copy-id -i ~/.ssh/id_rsa.pub mamu@192.168.0.91

Here is the actual output from the command:

/usr/bin/ssh-copy-id: INFO: Source of key(s) to be installed: “/home/mamu/.ssh/id_rsa.pub”
The authenticity of host ‘192.168.0.91 (192.168.0.91)’ can’t be established.
ED25519 key fingerprint is SHA256:azZSnRoh+vPHX+0kmjK0VaPFfkItGxFuZx4S9LIPR8Q.
This key is not known by any other names.
Are you sure you want to continue connecting (yes/no/[fingerprint])? yes
/usr/bin/ssh-copy-id: INFO: attempting to log in with the new key(s), to filter out any that are already installed
/usr/bin/ssh-copy-id: INFO: 1 key(s) remain to be installed — if you are prompted now it is to install the new keys
mamu@192.168.0.91’s password:

Number of key(s) added: 1

Now try logging into the machine, with: “ssh ‘mamu@192.168.0.91′”
and check to make sure that only the key(s) you wanted were added.

Step 3: Sending the File

Next, use rsync to transfer the file from the source machine to the destination machine. Execute the following command. In this example, we are sending a file named bk.tar:

rsync -e 'ssh -i ~/.ssh/id_rsa' -av /tmp/bk.tar user@<destination-IP-address>:/tmp
  • -e 'ssh -i ~/.ssh/id_rsa' specifies the private key to be used for the SSH connection.
  • /tmp/bk.tar is the path of the file to be sent.
  • user@<destination-IP-address>:/tmp indicates the directory on the destination machine where the file will be transferred.

Here’s the actual command and output example:

rsync -e 'ssh -i ~/.ssh/id_rsa' -av /tmp/bk.tar mamu@192.168.0.91:/tmp

sending incremental file list
bk.tar

sent 190,200,253 bytes received 35 bytes 10,868,587.89 bytes/sec
total size is 190,153,728 speedup is 1.00

Note: What if the Machines Are Not on the Same Network Segment?

Even if the machines are not on the same network segment, file transfers via rsync are still possible as long as communication over the internet is enabled. As long as external IP addresses and appropriate firewall settings are configured, files can be transferred without issues, even across different network segments. In this case, the file was successfully transferred to the destination.

mamu@proxub24:~$ ls /tmp
bk.tar


Tasks on the Destination: Installing Docker

After the file is transferred from the source, you will import the Docker image on the destination machine using the file. Before doing so, ensure that Docker is installed on the destination machine. If you’re unsure how to install Docker, please refer to the guide on the following page.

This is Where the docker save Command Comes In

Using the docker commit and save Commands in Docker

Here, we will introduce how to back up a Docker image using the docker save command. This process explains how to create a new image without stopping the container and save that image. Return to the source machine and input the following commands.


Step 1: Convert the Container into an Image

First, use the docker commit command to create an image from the currently running container. In the following example, the container is named mycontainer, and the image is named myimage.

docker commit mycontainer myimage:version1
  • mycontainer is the name of the container.
  • myimage is the repository name of the created image, and version1 is the tag.

Now, a new image has been created from the container. You can verify that the image has been added by using the docker images command.


Step 2: Save the Image as a tar File

Next, save the created image using the docker save command. This command allows you to save one or more images into a tar archive.

docker save -o myimage_backup.tar myimage:version1
  • myimage_backup.tar is the file name where the image will be saved.
  • myimage:version1 is the name and tag of the image to be saved.

Step 3: Verify and Adjust Permissions

Usually, there’s no need to change the permissions of the file using chmod 666 myimage_backup.tar. However, if you encounter permission issues when accessing the file, you can modify the permissions with the following command:

sudo chmod 666 myimage_backup.tar

This command grants read and write permissions to all users.

If no permission changes are needed, you can skip this step.


Additional Notes

With the docker save command, you can specify a particular directory, such as /tmp, to save the file. Simply specify the desired path as follows:

docker save -o /tmp/myimage_backup.tar myimage:version1

This command saves the image myimage:version1 as myimage_backup.tar in the /tmp directory.

In other words, you can specify any directory as the file path with the -o option, and the file will be created there.

How to Send Files from the Source Machine to the Destination (Using rsync)

In this procedure, we will use the rsync command on the source machine to send files to the destination. Below are the specific steps for transferring files from the source to the destination.


Step 1: Execute the rsync Command on the Source Machine

First, run the following command on the source machine. This command will send the file from the source to the destination.

rsync -e 'ssh -i ~/.ssh/id_rsa' -av /tmp/myimage_backup.tar user@<destination-IP-address>:/tmp
  • Source: This command is executed on the source machine.
  • -e 'ssh -i ~/.ssh/id_rsa': Specifies the SSH connection and the private key used for transferring the file.
  • /tmp/myimage_backup.tar: The path to the file on the source machine that will be sent.
  • user@<destination-IP-address>: Specifies the username and IP address of the destination.
  • :/tmp: Specifies the directory on the destination machine where the file will be saved.

Step 2: Example of the Actual Command

Here’s an example using the actual IP address and username. Run this on the source machine.

rsync -e 'ssh -i ~/.ssh/id_rsa' -av /tmp/myimage_backup.tar mamu@192.168.0.91:/tmp
  • mamu: The username on the destination machine.
  • 192.168.0.91: The IP address of the destination server.
  • /tmp: The directory on the destination machine where the file will be saved.

Step 3: Verify the File on the Destination Machine

After the file has been successfully sent from the source machine, run the following command on the destination machine to confirm that the file was received correctly.

ls -lh /tmp/myimage_backup.tar

This will verify that the file exists in the /tmp directory on the destination machine.


Task on the Destination: Loading the Docker Image

Using the backup file transferred from the source, you will load the Docker image on the destination machine. Follow these steps to proceed.


Step 1: Load the Docker Image

On the destination machine, use the file that was transferred to load the Docker image. Run the following command on the destination terminal:

docker load -i /tmp/myimage_backup.tar
  • docker load: This command loads a Docker image from a tar archive.
  • -i /tmp/myimage_backup.tar: Specifies the backup file myimage_backup.tar located in the /tmp directory.

Here’s the actual message displayed by the above command:

8e2ab394fabf: Loading layer [==================================================>] 77.83MB/77.83MB
67796e30ff04: Loading layer [==================================================>] 114MB/114MB
eda13eb24d4c: Loading layer [==================================================>] 3.584kB/3.584kB
0fc6bb94eec5: Loading layer [==================================================>] 4.608kB/4.608kB
2bdf51597158: Loading layer [==================================================>] 2.56kB/2.56kB
16907864a2d0: Loading layer [==================================================>] 5.12kB/5.12kB
11de3d47036d: Loading layer [==================================================>] 7.168kB/7.168kB
d78fa06b2cb7: Loading layer [==================================================>] 10.24kB/10.24kB

Step 2: Verify the Image Loading

Once the image has been loaded, the loaded image ID will be displayed. To verify that the image was loaded correctly, run the following command to display the list of images:

docker images

After running this command, the image loaded from myimage_backup.tar should appear in the list. If the repository name and tag are not set, you can assign them later.


Step 3: Assign a Name to the Image (If Necessary)

If the imported image doesn’t have a repository name or tag, you can use the docker tag command to assign one later. This is important for easier image management.


How to Assign a Name to the Image

First, use the docker images command to check the image ID. For example, you might get the following output:

REPOSITORY TAG IMAGE ID CREATED SIZE  
<none> <none> d69fd7664ed7 5 hours ago 188MB

Next, use the docker tag command to assign a name (repository name and tag) to the image ID:

docker tag d69fd7664ed7 myimage:version1
  • d69fd7664ed7: This is the ID of the loaded image.
  • myimage:version1: The repository name (myimage) and tag (version1) you want to assign.

After assigning the name, run the docker images command again to verify:

docker images

You should now see the image with the new name:

REPOSITORY TAG IMAGE ID CREATED SIZE  
myimage version1 d69fd7664ed7 5 hours ago 188MB

Step 4: Start a Container with docker run

You can use the docker run command to start a container from the image you created. This command will create a new container from the image and start it with the specified settings.


Basic Usage of docker run

Here’s an example of how to start an Nginx container using the image you created:

docker run --name mynginx -d -p 8888:80 myimage:version1
  • --name mynginx: Assigns the name “mynginx” to the container, making it easier to manage.
  • -d: Runs the container in the background. Without this, the container will run in interactive mode.
  • -p 8888:80: Maps port 8888 on the host to port 80 inside the container, allowing access via http://<server-IP-address>:8888.
  • myimage:version1: The image from which the container is started. This is the repository name and tag assigned earlier.

Verifying the Container is Running

To check if the container is running correctly, use the following command:

docker ps

This will display a list of running containers. If mynginx is listed, the container is running properly.


Accessing the Container

With Nginx running, you can access it in your browser by navigating to the following URL:

http://<server-IP-address>:8888

If the Nginx welcome page appears, the container has started successfully.


Error Due to Architecture Differences Between the Source and Destination Machines

In some cases, errors may occur when migrating containers between machines with different hardware architectures. In this case, the source instance used ARM architecture (linux/arm64/v8), while the destination instance used AMD architecture (linux/amd64), causing an error.

Example of the error message:

WARNING: The requested image's platform (linux/arm64/v8) does not match the detected host platform (linux/amd64) and no specific platform was requested
6f101cdc855e330fbd648388c2610790f2a164ca861da275e83882f6b9e17be1

Error Due to Architecture Differences

When migrating Docker containers, errors may occur if the source and destination machines have different hardware architectures. In this case, the source instance was using ARM architecture (linux/arm64/v8), while the destination instance was using AMD architecture (linux/amd64), leading to the error.

WARNING: The requested image’s platform (linux/arm64/v8) does not match the detected host platform (linux/amd64) and no specific platform was requested

This error occurs because the image created for the ARM architecture is being run on an AMD (x86_64) architecture.


Solution: Specifying the Platform

If you need to run Docker images across different architectures, you can explicitly specify the target platform using Docker’s multi-platform support feature. This ensures that the image runs correctly even on different platforms.


How to Resolve the Issue

You can specify the platform when starting the container by using the docker run command as follows:

docker run --platform linux/amd64 --name mynginx -d -p 8888:80 myimage:version1
  • --platform linux/amd64: This specifies that the image should be run for AMD (x86_64) architecture. This allows an ARM-based image to run on an AMD architecture host.

If you want to run the image on an ARM architecture instead, you can specify --platform linux/arm64.


Multi-Architecture Builds

For smoother image migration between different platforms in the future, consider building multi-platform-compatible images. You can use Docker’s extended tool called buildx to create multi-architecture-compatible images.