1. Introduction

Docker is a platform for developing, deploying, and running containerized applications. Developers create these applications from executable blueprints called Docker images. Specifically, Docker images are lightweight, self-contained files that package everything needed to run a given application. Public registries like Docker Hub store collections of such images.

In this tutorial, we’ll explore how to download Docker images even if we don’t have the Docker client installed on the system.

All commands in this tutorial have been tested on Ubuntu 22.04.

2. Acquire Docker Images Without Docker

A few scenarios might prompt us to download a Docker image without Docker itself:

  • Strict security policies in certain secure environments may prohibit the installation of additional software, including Docker.
  • For offline deployment, we need to transfer a Docker image to a machine without Internet access. Hence, downloading the image itself might be necessary.

In the latter case, we can use a tool like scp or even a removable storage medium to export the image for later use.

However, there are several alternatives available if we can’t use the Docker client:

  • Moby’s download-frozen-image-v2.sh script
  • Skopeo
  • docker-pull.py script

In the following sections, we explain how to use each alternative to download Docker images.

3. Moby’s download-frozen-image.sh Script

The Moby project is a framework created by Docker to create container systems that are more specialized. This framework offers pre-built components, tools, and assemblies for creating custom container platforms.

Particularly, Moby provides a handy shell script called download-frozen-image-v2.sh available on GitHub. The script simplifies downloading Docker images over HTTP from Docker Hub in a format compatible with Docker for later use.

3.1. Installation and Usage

Since the script is hosted on GitHub, we can download it using wget:

$ wget https://raw.githubusercontent.com/moby/moby/master/contrib/download-frozen-image-v2.sh

The usage syntax for the script is fairly self-explanatory:

download-frozen-image-v2.sh target_dir image[:tag][@digest] ...

Once downloaded, let’s make the script executable:

$ chmod u+x download-frozen-image-v2.sh

Before running the script, we must ensure it has all the required tools. The script utilizes the jq package to parse data.

If the script throws an error mentioning jq not found, we install it using apt-get:

$ sudo apt-get install jq

Now that we have everything set up, let’s download the hello-world Docker image in the /tmp/old-hello-world directory:

$ ./download-frozen-image-v2.sh /tmp/old-hello-world hello-world:latest@sha256:8be990ef2aeb16dbcb9271ddfe2610fa6658d13f6dfb8bc72074cc1ca36966a7
warning: 'library/hello-world:latest@sha256:8be990ef2aeb16dbcb9271ddfe2610fa6658d13f6dfb8bc72074cc1ca36966a7' uses schemaVersion '1'
  this script cannot (currently) recreate the 'image config' to put in a 'manifest.json' (thus any schemaVersion 2+ images will be imported in the old way, and their 'docker history' will suffer)

Downloading 'library/hello-world:latest@sha256:8be990ef2aeb16dbcb9271ddfe2610fa6658d13f6dfb8bc72074cc1ca36966a7' (2 layers)...
#=O#-  #    #                                                                                                                        
############################################################################################################################### 100.0%
#=O#-  #    #                                                                                                                        
############################################################################################################################### 100.0%

Download of images into '/tmp/old-hello-world' complete.
Use something like the following to load the result into a Docker daemon:
  tar -cC '/tmp/old-hello-world' . | docker load

The script might display a warning about schemaVersion. This indicates a potential limitation in how the script handles newer image formats. However, the download should still proceed successfully.

3.2. Loading the Image Into Docker

To use the downloaded image with Docker, we can pipe the contents of the target directory (/tmp/old-hello-world) to the docker load command:

$ tar -cC '/tmp/old-hello-world' . | docker load

Ideally, in practice, we copy the data from the client without Docker installed to the target machine with Docker installed:

$ tar -C '/tmp/old-hello-world' -cf 'hello-world.tar' .
$ scp hello-world.tar user@hasdocker:~

Then, we load and use the image on the target host:

$ docker load hello-world.tar
$ docker run --rm --ti hello-world bash
root@1ad5e42123c9:/#

This command runs the downloaded hello-world image on the target machine. The –rm flag removes the container after it exits, and -ti provides a pseudo-terminal for interactive use.

4. Skopeo

skopeo is a command-line tool for working with container images and registries. In particular, it can inspect, copy, sign, and verify container images. Furthermore, it works across various storage mechanisms, including Docker registries, local directories, and OCI layouts.

We can install the Skopeo CLI tool using apt:

$ sudo apt-get update
$ sudo apt install skopeo

This command downloads and installs Skopeo, making it available on the command line.

4.1. Copying the Docker Image

Now, for this use case, Skopeo has the copy command that handles making a copy of the container image:

skopeo copy [options] source-image destination-image

For example, let’s make a copy of the ubuntu image from Docker Hub onto the machine:

$ skopeo copy docker://ubuntu docker-archive:/tmp/ubuntu.tar:ubuntu
Getting image source signatures
Copying blob 3c645031de29 done  
Copying config 7af9ba4f0a done  
Writing manifest to image destination
Storing signatures

In this case, we download the ubuntu image from Docker Hub and save it as a compressed archive named ubuntu.tar in the /tmp/ directory. The archive contains all the layers that make up the image.

4.2. Loading the Docker Image

Once downloaded, scp can be used to transfer the ubuntu.tar archive to a machine running Docker:

$ scp /tmp/ubuntu.tar user@hasdocker:~

On the machine with Docker installed, we can load the image archive and make it available for use with the load command:

$ docker load --input /tmp/ubuntu.tar

Then, we again run it in an interactive shell:

$ docker run -it ubuntu

The above command creates an interactive container and a pseudo-terminal for us to interact with.

5. docker-pull Script

docker-pull is a Python script for interacting with Docker Hub. This script is OS-independent, enabling its use on any operating system. Furthermore, most Linux distributions like Ubuntu come with Python preinstalled.

The script relies on the Docker registry HTTPS API v2. However, one limitation is that only V2 manifests are supported.

5.1. Installation and Usage

The docker-pull.py script resides in the docker-drag GitHub repository. We can clone the repository using git:

$ git clone https://github.com/NotGlop/docker-drag.git

Then, change directories into the cloned repository and verify the presence of the script:

$ cd docker-drag
$ ls
README.md  docker_pull.py

The script leverages Python 3. Here’s the script’s usage syntax for recent versions of Ubuntu systems:

$ python3 docker_pull.py image_name

For example, let’s demonstrate downloading the centos image using docker-pull.py:

$ python3 docker_pull.py centos
Creating image structure in: tmp_centos_latest
a1d0c7532777: Pull complete [83518086]
Docker image pulled: library_centos.tar

This command creates a temporary directory (tmp_centos_latest) to store the downloaded image files. On completion, we see the message indicating success: Docker image pulled: library_centos.tar.

5.2. Loading and Running the Image

The script downloads the image as a tar archive named library_centos.tar.

To use this image with Docker, we can load it using the docker load command:

$ docker load -i library_centos.tar

Once loaded, we can run the centos image in an interactive shell:

$ docker run -it centos

Hence, an interactive container with a pseudo-terminal is again created from the image.

6. Conclusion

In this article, we learned about ways to download Docker images without the Docker client installed on the machine. We mentioned and went into detail about three alternatives: download-frozen-image-v2.sh, skopeo, and docker-pull.py.

While Docker remains the recommended approach for managing Docker images, situations may arise when downloading images without the Docker client becomes necessary. For ease of use, Moby’s download-frozen-image-v2.sh is a good choice. docker-pull.py offers similar functionality but requires more research because of its less active maintenance by the project owners.

Lastly, Skopeo is actively maintained and integrates seamlessly with container workflows, making it a near-ideal alternative.