Answers: <p>thanks for motivation.
  I made a powershell version of it.
  Check it out...
  With it you can move in dockerhub containers to a restricted docker networks with a windows desktop and an ssh-scp tool to docker machine without root or administrator rights</p>
  
  <p><a href="https://gitlab.com/Jancsoj78/dockerless_docker_downloader" rel="nofollow noreferrer">https://gitlab.com/Jancsoj78/dockerless_docker_downloader</a>
  a new hacker tool :)</p>
  
  <pre><code>$image = "ubuntu"
  $tag = "latest"
  $imageuri = "https://auth.docker.io/token?service=registry.docker.io&amp;scope=repository:library/"+$image+":pull"
  $taguri = "https://registry-1.docker.io/v2/library/"+$image+"/manifests/"+$tag
  $bloburi = "https://registry-1.docker.io/v2/library/"+$image+"/blobs/sha256:"
  
  #token request
  $token = Invoke-WebRequest -Uri $imageuri | ConvertFrom-Json | Select -expand token
  
  #pull image manifest
  $blobs = $($(Invoke-Webrequest -Headers @{Authorization="Bearer $token"} -Method GET -Uri $taguri | ConvertFrom-Json | Select -expand fsLayers ) -replace "sha256:" -replace "@{blobSum=" -replace "}")
  
  #download blobs
  for ($i=0; $i -lt $blobs.length; $i++) {
      $blobelement =$blobs[$i]
  
      Invoke-Webrequest -Headers @{Authorization="Bearer $token"} -Method GET -Uri $bloburi$blobelement -OutFile blobtmp
  
      $source = "blobtmp"
      $newfile = "$blobelement.gz"
  
  #overwrite
  Copy-Item $source $newfile -Force -Recurse
  #source blobs
  ls *.gz
  }
  #postprocess
  echo "copy these .gz to your docker machine"
  echo "docker import .gz backward one by one"
  echo "lastone with ubuntu:latest"
  echo "after docker export and reimport to make a simple layer image"
  </code></pre>
  
Answers: CC BY-SA 4.0
Answers: 2019-03-20T16:37:48.093Z
Answers: 2019-03-22T19:46:14.327Z
Answers: 2019-03-22T19:46:14.327Z
Answers: Jancsó József
Answers: <p>Here is an adapted python script, thus having an OS independant solution: <a href="https://github.com/NotGlop/docker-drag" rel="nofollow noreferrer">docker-drag</a></p>
  
  <p>Use it like that, and it will create a TAR archive that you will be able to import using docker load :</p>
  
  <pre><code>python docker_pull.py hello-world
  python docker_pull.py alpine:3.9
  python docker_pull.py kalilinux/kali-linux-docker
  </code></pre>
  
Answers: CC BY-SA 4.0
Answers: 2019-04-09T13:16:06.527Z
Answers: If you close your github account there's nothing left, if you're ok with sharing it, keep the link but paste the script here also please. You can [edit] your answer, paste your code and then select it and type ctrl+K or the `{}` (code) button in the editor top bar to format it.
Answers: Tensibai
Answers: CC BY-SA 4.0
Answers: 2019-04-09T13:19:30.570Z
Answers: I would love to paste the code here but it is 100 lines long and I don't think it will be readable. Nevertheless, you can fork the code to save your own copy of the script.
Answers: Dutfaz
Answers: CC BY-SA 4.0
Answers: 2019-04-09T13:39:44.230Z
Answers: That's not for me, that to have an answer which is self sustaining, if the link break, do you really think this may help someone reading this answer in a few months ? (btw the maximum size for an answer is 30k characters)
Answers: Tensibai
Answers: CC BY-SA 4.0
Answers: 2021-04-18T18:24:34.437Z
Answers: You could put this in pip. I find it very helpful.
Answers: Anoyz
Answers: CC BY-SA 4.0
Answers: 2019-04-09T13:13:10.790Z
Answers: 2019-04-09T13:17:13.340Z
Answers: 2019-04-09T13:17:13.340Z
Answers: Dutfaz
Answers: <p>There is a tool called <a href="https://github.com/containers/skopeo" rel="noreferrer">Skopeo</a> which can retrieve Docker images from a repository and save them in several formats.</p>
  
  <p>For example:</p>
  
  <ol>
  <li><p>Download the image and save the layers as a tarball:
  <code>skopeo copy docker://ubuntu docker-archive:/tmp/ubuntu.tar:ubuntu</code></p></li>
  <li><p>Transfer <code>/tmp/ubuntu.tar</code> to another machine if desired.</p></li>
  <li><p>Load the image on a Docker instance which does not have internet connection:
  <code>docker load --input /tmp/ubuntu.tar</code></p></li>
  </ol>
  
  <p>It is available in CentOS 7 repo with the package name <code>skopeo</code>. There are no Debian or Ubuntu packages at this time (but it is easy to compile).</p>
  
Answers: CC BY-SA 4.0
Answers: 2020-09-18T23:05:32.007Z
Answers: FWIW there are deb packages now: https://github.com/containers/skopeo/blob/master/install.md
Answers: Jeremy Davis
Answers: CC BY-SA 4.0
Answers: 2018-11-20T17:20:03.050Z
Answers: 2018-11-20T17:20:03.050Z
Answers: snap
Answers: <p>To me it is not completely clear what you are trying to achieve and why the attempts are not a solution for the problem. If I would need to solve this issue I would like @Tensibai and other Q&amp;As indicated, do a docker pull first on a system with internet connectivity, save the docker image, copy it to the machine without internet connectivity, load the image and run it.</p>
  
  <p><strong>Demonstration</strong></p>
  
  <p>There are no images on system A:</p>
  
  <pre><code>userA@systemA ~ $ docker images
  REPOSITORY        TAG               IMAGE ID          CREATED             SIZE
  userA@systemA ~ $
  </code></pre>
  
  <p>Pull an image from dockerhub:</p>
  
  <pre><code>userA@systemA ~ $
  docker pull nginx
  Using default tag: latest
  latest: Pulling from library/nginx
  bc95e04b23c0: Pull complete 
  f3186e650f4e: Pull complete 
  9ac7d6621708: Pull complete 
  Digest: sha256:b81f317384d7388708a498555c28a7cce778a8f291d90021208b3eba3fe74887
  Status: Downloaded newer image for nginx:latest
  userA@systemA ~ $ docker images
  REPOSITORY        TAG               IMAGE ID            CREATED             SIZE
  nginx             latest            9e7424e5dbae        10 days ago         108MB
  </code></pre>
  
  <p>Save docker image:</p>
  
  <pre><code>userA@systemA ~ $ docker save nginx -o nginx.tar
  </code></pre>
  
  <p>Copy docker image to systemB and load it.</p>
  
  <pre><code>userB@systemB ~ $ docker load -i nginx.tar
  cec7521cdf36: Loading layer  58.44MB/58.44MB
  350d50e58b6c: Loading layer  53.76MB/53.76MB
  63c39cd4a775: Loading layer  3.584kB/3.584kB
  Loaded image: nginx:latest
  userB@systemB ~ $ docker images
  REPOSITORY        TAG               IMAGE ID            CREATED             SIZE
  nginx             latest            9e7424e5dbae        10 days ago         108MB
  </code></pre>
  
Answers: CC BY-SA 3.0
Answers: 2017-12-03T15:03:17.830Z
Answers: The machine with internet connectivity does not and cannot have Docker installed. The question is asking for a way to download an image *without* using the Docker client. See [my solution](https://devops.stackexchange.com/a/2772/3070).
Answers: igal
Answers: CC BY-SA 3.0
Answers: 2017-12-03T13:26:18.993Z
Answers: 2017-12-03T13:35:59.387Z
Answers: 2017-12-03T13:35:59.387Z
Answers: 030
Answers: <p>I didn't really understand <a href="https://devops.stackexchange.com/a/6666/28888">Jancsó's</a> way of postprocessing, so I've spent some time to modify his script and here's what I came with:</p>
  <p><a href="https://github.com/meetyourturik/dockerless-docker-downloader" rel="nofollow noreferrer">https://github.com/meetyourturik/dockerless-docker-downloader</a></p>
  <p>upd: apparently, 'a link only answer...' is something bad, so here's a whole script:</p>
  <pre><code># Workaround for SelfSigned Cert an force TLS 1.2
  add-type @&quot;
      using System.Net;
      using System.Security.Cryptography.X509Certificates;
      public class TrustAllCertsPolicy : ICertificatePolicy {
          public bool CheckValidationResult(
          ServicePoint srvPoint, X509Certificate certificate,
          WebRequest request, int certificateProblem) {
              return true;
          }
      }
  &quot;@
  [System.Net.ServicePointManager]::CertificatePolicy = New-Object TrustAllCertsPolicy
  [System.Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
  
  # use 'library/' prefix for 'official' images like postgres 
  $image = &quot;atlassian/jira-software&quot; 
  $tag = &quot;8.13.2&quot; 
  
  $imageuri = &quot;https://auth.docker.io/token?service=registry.docker.io&amp;scope=repository:${image}:pull&quot; 
  $taguri = &quot;https://registry-1.docker.io/v2/${image}/manifests/${tag}&quot;
  $bloburi = &quot;https://registry-1.docker.io/v2/${image}/blobs/&quot; 
  
  # generating folder to save image files 
  $path = &quot;$image$tag&quot; -replace '[\\/&quot;:*?&lt;&gt;|]'
  if (!(test-path $path)) { 
      New-Item -ItemType Directory -Force -Path $path 
  } 
  
  # token request 
  $token = Invoke-WebRequest -Uri $imageuri | ConvertFrom-Json | Select -expand token 
  
  # getting image manifest 
  $headers = @{} 
  $headers.add(&quot;Authorization&quot;, &quot;Bearer $token&quot;) 
  # this header is needed to get manifest in correct format: https://docs.docker.com/registry/spec/manifest-v2-2/ 
  $headers.add(&quot;Accept&quot;, &quot;application/vnd.docker.distribution.manifest.v2+json&quot;) 
  $manifest = Invoke-Webrequest -Headers $headers -Method GET -Uri $taguri | ConvertFrom-Json 
  
  # downloading config json 
  $configSha = $manifest | Select -expand config | Select -expand digest 
  $config = &quot;.\$path\config.json&quot; 
  Invoke-Webrequest -Headers @{Authorization=&quot;Bearer $token&quot;} -Method GET -Uri $bloburi$configSha -OutFile $config 
  
  # generating manifest.json 
  $manifestJson = @{} 
  $manifestJson.add(&quot;Config&quot;, &quot;config.json&quot;) 
  $manifestJson.add(&quot;RepoTags&quot;,@(&quot;${image}:${tag}&quot;)) 
  
  # downloading layers 
  $layers = $manifest | Select -expand layers | Select -expand digest 
  $blobtmp = &quot;.\$path\blobtmp&quot; 
  
  #downloading blobs 
  $layersJson = @() 
  foreach ($blobelement in $layers) { 
      # making so name doesnt start with 'sha256:' 
      $fileName = &quot;$blobelement.gz&quot; -replace 'sha256:' 
      $newfile = &quot;.\$path\$fileName&quot; 
      $layersJson += @($fileName) 
  
      # token expired after 5 minutes, so requesting new one for every blob just in case 
      $token = Invoke-WebRequest -Uri $imageuri | ConvertFrom-Json | Select -expand token 
      
      Invoke-Webrequest -Headers @{Authorization=&quot;Bearer $token&quot;} -Method GET -Uri $bloburi$blobelement -OutFile $blobtmp 
      
      Copy-Item $blobtmp $newfile -Force -Recurse 
  } 
  
  # removing temporary blob 
  Remove-Item $blobtmp 
  
  # saving manifest.json 
  $manifestJson.add(&quot;Layers&quot;, $layersJson) 
  ConvertTo-Json -Depth 5 -InputObject @($manifestJson) | Out-File -Encoding ascii &quot;.\$path\manifest.json&quot; 
  
  # postprocessing
  echo &quot;copy generated folder to your docker machine&quot; 
  echo &quot;tar -cvf imagename.tar *&quot; 
  echo &quot;docker load &lt; imagename.tar&quot;
  </code></pre>
  <p>after sctipt downloads blobs and generates config and manifest jsons download it to docker machine and execute two following commands:</p>
  <pre><code>tar -cvf imagename.tar *
  docker load &lt; imagename.tar
  </code></pre>
  <p>first creates an archive, 2nd uploads image archive to docker</p>
  
Answers: CC BY-SA 4.0
Answers: 2021-07-22T19:23:31.987Z
Answers: 2021-07-24T09:44:43.050Z
Answers: 2021-07-24T09:44:43.050Z
Answers: Ilia Turov
Answers: <p>Another tool that could be used to download docker images is <a href="https://github.com/030/p2iwd" rel="nofollow noreferrer">P2IWD</a>.</p>
  
Answers: CC BY-SA 4.0
Answers: 2022-03-06T13:24:03.963Z
Answers: There doesn’t appear to be any code in that repository.
Answers: igal
Answers: CC BY-SA 4.0
Answers: 2022-03-06T22:11:42.567Z
Answers: @igal version 0.1.0 has been released. It is capable to download docker images from a Nexus3 server and to upload them to another one.
Answers: 030
Answers: CC BY-SA 4.0
Answers: 2022-03-06T13:11:34.640Z
Answers: 2022-03-06T13:11:34.640Z
Answers: 030
Answers: <p>You can manually download a docker image with curl by:</p>
  <ol>
  <li>getting a free auth token</li>
  <li>download the manifest</li>
  <li>download the layers (blob hashes specified in the manifest)</li>
  </ol>
  <h1>Example</h1>
  <p>For example, I'll show how you can download <a href="https://hub.docker.com/_/hitch" rel="nofollow noreferrer">the <code>hitch</code> package</a> from hub.docker.com.</p>
  <h2>Get an Auth Token</h2>
  <p>Execute the following command to get an <a href="https://distribution.github.io/distribution/spec/auth/oauth/" rel="nofollow noreferrer">authentication token</a> from Docker Hub within the scope of the '<code>hitch</code>' package's namespace.</p>
  <pre><code># get a JSON with an anonymous token
  curl -so &quot;token.json&quot; &quot;https://auth.docker.io/token?service=registry.docker.io&amp;scope=&lt;resourcetype&gt;:&lt;component&gt;/&lt;component&gt;:&lt;action&gt;&quot;;
  
  # extract token from JSON
  token=$(cat token.json | jq -jr &quot;.token&quot;)
  </code></pre>
  <blockquote>
  <p>ⓘ INFO: If you're like me and wondering where the heck is the OCI spec that defines this <code>/token</code> endpoint for authentication, know that <a href="https://github.com/opencontainers/distribution-spec/issues/240" rel="nofollow noreferrer">it doesn't exist</a> </p>
  <p>To learn more about the syntax of this URL and '<code>scope</code>' GET variable, see docker's <a href="https://distribution.github.io/distribution/spec/auth/scope/" rel="nofollow noreferrer">Token Scope Documentation</a>.</p>
  </blockquote>
  <p>The above commands will get a free/temporary token that you can use in subsequent API calls. If all went well, there will be no output from these commands. Here's an example execution</p>
  <pre><code>user@disp7456:~$ curl -so &quot;token.json&quot; &quot;https://auth.docker.io/token?service=registry.docker.io&amp;scope=repository:library/hitch:pull&quot;;
  user@disp7456:~$ 
  
  user@disp7456:~$ token=$(cat token.json | jq -jr &quot;.token&quot;)
  user@disp7456:~$ 
  </code></pre>
  <h2>Download the Manifest</h2>
  <p>We can download the manifest for the '<code>1.8.0-1</code>' tag of the '<code>hitch</code>' package with the '<code>GET /v2/&lt;name&gt;/manifests/&lt;reference&gt;</code>' API endpoint.</p>
  <pre><code>curl -o manifest.json -s -H &quot;Authorization: Bearer ${token}&quot; https://registry-1.docker.io/v2/library/&lt;package_name&gt;/manifests/&lt;tag&gt;
  </code></pre>
  <p>And here's an example execution that downloads the manifest for the '<code>1.8.0-1</code>' tag of the '<code>hitch</code>' package</p>
  <pre><code>
  user@disp7456:~$ curl -o manifest.json -s -H &quot;Authorization: Bearer ${token}&quot; https://registry-1.docker.io/v2/library/hitch/manifests/1.8.0-1
  user@disp7456:~$ 
  
  user@disp7456:~$ ls
  manifest.json
  user@disp7456:~$ 
  </code></pre>
  <h2>Parse the Manifest</h2>
  <p>The previous step downloaded a file named '<code>manifest.json</code>'. This '<code>manifest.json</code>' file lists all of the &quot;layers&quot; that make up the image of the '<code>hitch</code>' package.</p>
  <p>Each &quot;layer&quot; consists of a tarball and some metadata about the layer in json format. The information that we need to download the layer's tar file is located in the '<code>manifest.json</code>' file. And the metadata about each layer is also in the '<code>manifest.json</code>' file.</p>
  <p>The format of the '<code>manifest.json</code>' file is '<code>vnd.docker.distribution.manifest.v1+json</code>', which is defined in <a href="https://github.com/distribution/distribution/blob/bf9f80eaffb0eabc768762bc4ff03ded277ea594/docs/spec/manifest-v2-1.md" rel="nofollow noreferrer">Image Manifest Version 2, Schema 1</a>.</p>
  <p>Most importantly, the '<code>manifest.json</code>' file contains two <a href="https://en.wikipedia.org/wiki/Parallel_array" rel="nofollow noreferrer">parallel arrays</a> of the same length:</p>
  <ol>
  <li>fsLayers[]</li>
  <li>history[]</li>
  </ol>
  <p>Consider this truncated snippet of the manifest for the '<code>hitch</code>' package's '<code>1.8.0-1</code>' tag:</p>
  <pre><code>{
     &quot;schemaVersion&quot;: 1,
     &quot;name&quot;: &quot;library/hitch&quot;,
     &quot;tag&quot;: &quot;1.8.0-1&quot;,
     &quot;architecture&quot;: &quot;amd64&quot;,
     &quot;fsLayers&quot;: [
        {
           &quot;blobSum&quot;: &quot;sha256:a3ed95caeb02ffe68cdd9fd84406680ae93d633cb16422d00e8a7c22955b46d4&quot;
        },
        {
           &quot;blobSum&quot;: &quot;sha256:a3ed95caeb02ffe68cdd9fd84406680ae93d633cb16422d00e8a7c22955b46d4&quot;
        },
        {
           &quot;blobSum&quot;: &quot;sha256:a3ed95caeb02ffe68cdd9fd84406680ae93d633cb16422d00e8a7c22955b46d4&quot;
        },
        {
           &quot;blobSum&quot;: &quot;sha256:3148f4af0a813bcff0a3ed2562aabfb1b596b52ef36eb5eb4d82ce836350b73a&quot;
        },
        {
           &quot;blobSum&quot;: &quot;sha256:a3ed95caeb02ffe68cdd9fd84406680ae93d633cb16422d00e8a7c22955b46d4&quot;
        },
        {
           &quot;blobSum&quot;: &quot;sha256:a0e9543db8c1238572466cf00b55436bc7b7e849f7cb305128f391a94b75c2fc&quot;
        },
        {
           &quot;blobSum&quot;: &quot;sha256:a3ed95caeb02ffe68cdd9fd84406680ae93d633cb16422d00e8a7c22955b46d4&quot;
        },
        {
           &quot;blobSum&quot;: &quot;sha256:a3ed95caeb02ffe68cdd9fd84406680ae93d633cb16422d00e8a7c22955b46d4&quot;
        },
        {
           &quot;blobSum&quot;: &quot;sha256:a3ed95caeb02ffe68cdd9fd84406680ae93d633cb16422d00e8a7c22955b46d4&quot;
        },
        {
           &quot;blobSum&quot;: &quot;sha256:a3ed95caeb02ffe68cdd9fd84406680ae93d633cb16422d00e8a7c22955b46d4&quot;
        },
        {
           &quot;blobSum&quot;: &quot;sha256:a3ed95caeb02ffe68cdd9fd84406680ae93d633cb16422d00e8a7c22955b46d4&quot;
        },
        {
           &quot;blobSum&quot;: &quot;sha256:a3ed95caeb02ffe68cdd9fd84406680ae93d633cb16422d00e8a7c22955b46d4&quot;
        },
        {
           &quot;blobSum&quot;: &quot;sha256:728328ac3bde9b85225b1f0d60f5c149f5635a191f5d8eaeeb00e095d36ef9fd&quot;
        }
     ],
     &quot;history&quot;: [
        {
           &quot;v1Compatibility&quot;: &quot;{\&quot;architecture\&quot;:\&quot;amd64\&quot;,\&quot;config\&quot;:{\&quot;Hostname\&quot;:\&quot;\&quot;,\&quot;Domainname\&quot;:\&quot;\&quot;,\&quot;User\&quot;:\&quot;\&quot;,\&quot;AttachStdin\&quot;:false,\&quot;AttachStdout\&quot;:false,\&quot;AttachStderr\&quot;:false,\&quot;ExposedPorts\&quot;:{\&quot;443/tcp\&quot;:{}},\&quot;Tty\&quot;:false,\&quot;OpenStdin\&quot;:false,\&quot;StdinOnce\&quot;:false,\&quot;Env\&quot;:[\&quot;PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin\&quot;],\&quot;Cmd\&quot;:[],\&quot;Image\&quot;:\&quot;sha256:996009c7d7eb032c9ea750e5decc1f8aedbf4530b892cf4ebc7716e1458f36d9\&quot;,\&quot;Volumes\&quot;:null,\&quot;WorkingDir\&quot;:\&quot;/etc/hitch\&quot;,\&quot;Entrypoint\&quot;:[\&quot;docker-hitch-entrypoint\&quot;],\&quot;OnBuild\&quot;:null,\&quot;Labels\&quot;:null},\&quot;container\&quot;:\&quot;0ff54ee96c4bbfe77da3b2124720ef95c6154d3bc1d3e40a168920dd818367c4\&quot;,\&quot;container_config\&quot;:{\&quot;Hostname\&quot;:\&quot;0ff54ee96c4b\&quot;,\&quot;Domainname\&quot;:\&quot;\&quot;,\&quot;User\&quot;:\&quot;\&quot;,\&quot;AttachStdin\&quot;:false,\&quot;AttachStdout\&quot;:false,\&quot;AttachStderr\&quot;:false,\&quot;ExposedPorts\&quot;:{\&quot;443/tcp\&quot;:{}},\&quot;Tty\&quot;:false,\&quot;OpenStdin\&quot;:false,\&quot;StdinOnce\&quot;:false,\&quot;Env\&quot;:[\&quot;PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin\&quot;],\&quot;Cmd\&quot;:[\&quot;/bin/sh\&quot;,\&quot;-c\&quot;,\&quot;#(nop) \&quot;,\&quot;CMD []\&quot;],\&quot;Image\&quot;:\&quot;sha256:996009c7d7eb032c9ea750e5decc1f8aedbf4530b892cf4ebc7716e1458f36d9\&quot;,\&quot;Volumes\&quot;:null,\&quot;WorkingDir\&quot;:\&quot;/etc/hitch\&quot;,\&quot;Entrypoint\&quot;:[\&quot;docker-hitch-entrypoint\&quot;],\&quot;OnBuild\&quot;:null,\&quot;Labels\&quot;:{}},\&quot;created\&quot;:\&quot;2024-05-14T05:23:11.666992342Z\&quot;,\&quot;docker_version\&quot;:\&quot;20.10.23\&quot;,\&quot;id\&quot;:\&quot;6703605aae83084affcafb4abcc7c556f0e436c4992ae224f1f58e88242328cb\&quot;,\&quot;os\&quot;:\&quot;linux\&quot;,\&quot;parent\&quot;:\&quot;c48ca3d95161bbcdfcaa2e016a675965d55f4f06147ef4445c69347c5965f188\&quot;,\&quot;throwaway\&quot;:true}&quot;
        },
        {
           &quot;v1Compatibility&quot;: &quot;{\&quot;id\&quot;:\&quot;c48ca3d95161bbcdfcaa2e016a675965d55f4f06147ef4445c69347c5965f188\&quot;,\&quot;parent\&quot;:\&quot;1d2af5a156bbc461d98824c2f6bfe295327d4419105c0b7f88f14cb28d0bb240\&quot;,\&quot;created\&quot;:\&quot;2024-05-14T05:23:11.581588417Z\&quot;,\&quot;container_config\&quot;:{\&quot;Cmd\&quot;:[\&quot;/bin/sh -c #(nop)  EXPOSE 443\&quot;]},\&quot;throwaway\&quot;:true}&quot;
        },
        {
           &quot;v1Compatibility&quot;: &quot;{\&quot;id\&quot;:\&quot;1d2af5a156bbc461d98824c2f6bfe295327d4419105c0b7f88f14cb28d0bb240\&quot;,\&quot;parent\&quot;:\&quot;8f914c821cbe154cc6677bceb043669e4295ad5cfb6409efa9c2ec1beba75fad\&quot;,\&quot;created\&quot;:\&quot;2024-05-14T05:23:11.489285564Z\&quot;,\&quot;container_config\&quot;:{\&quot;Cmd\&quot;:[\&quot;/bin/sh -c #(nop)  ENTRYPOINT [\\\&quot;docker-hitch-entrypoint\\\&quot;]\&quot;]},\&quot;throwaway\&quot;:true}&quot;
        },
        {
           &quot;v1Compatibility&quot;: &quot;{\&quot;id\&quot;:\&quot;8f914c821cbe154cc6677bceb043669e4295ad5cfb6409efa9c2ec1beba75fad\&quot;,\&quot;parent\&quot;:\&quot;a8d8314458142ee2a4ebccb19f48b6f9c696100103c3d49cbbe7ecd2575120e5\&quot;,\&quot;created\&quot;:\&quot;2024-05-14T05:23:11.403178706Z\&quot;,\&quot;container_config\&quot;:{\&quot;Cmd\&quot;:[\&quot;/bin/sh -c #(nop) COPY file:1abf3c94dce5dc9f6617dc8d36a6fe6f4f7236189d4819f16cefb54288e80e0d in /usr/local/bin/ \&quot;]}}&quot;
        },
        {
           &quot;v1Compatibility&quot;: &quot;{\&quot;id\&quot;:\&quot;a8d8314458142ee2a4ebccb19f48b6f9c696100103c3d49cbbe7ecd2575120e5\&quot;,\&quot;parent\&quot;:\&quot;5a78b0e89bbae2390b83e60174ae1efc583f766eff7dffaffa747ccb67472d0f\&quot;,\&quot;created\&quot;:\&quot;2024-05-14T05:23:11.304477182Z\&quot;,\&quot;container_config\&quot;:{\&quot;Cmd\&quot;:[\&quot;/bin/sh -c #(nop) WORKDIR /etc/hitch\&quot;]},\&quot;throwaway\&quot;:true}&quot;
        },
        {
           &quot;v1Compatibility&quot;: &quot;{\&quot;id\&quot;:\&quot;5a78b0e89bbae2390b83e60174ae1efc583f766eff7dffaffa747ccb67472d0f\&quot;,\&quot;parent\&quot;:\&quot;5a12a2c67ff9b5bfad288a4ede18d08c259c301efb85403d08a40ea2ad0eb1f8\&quot;,\&quot;created\&quot;:\&quot;2024-05-14T05:23:11.160227264Z\&quot;,\&quot;container_config\&quot;:{\&quot;Cmd\&quot;:[\&quot;|5 DISTVER=bullseye PKGCOMMIT=f12ab7958bc4885f3f00311cbca5103d9e6ba794 PKGVER=1 SHASUM=62b3554d668c9d17382415db10898bf661ee76343e4ee364f904457efda6cb1eeee7cb81d7a3897734024812b64b1c0e2dc305605706d81a0c1f6030508bf7e2 SRCVER=1.8.0 /bin/sh -c set -ex;     BASE_PKGS=\\\&quot;apt-utils curl dirmngr dpkg-dev debhelper devscripts equivs fakeroot git gnupg pkg-config\\\&quot;;     export DEBIAN_FRONTEND=noninteractive;     export DEBCONF_NONINTERACTIVE_SEEN=true;     tmpdir=\\\&quot;$(mktemp -d)\\\&quot;;     cd \\\&quot;$tmpdir\\\&quot;;     apt-get update;     apt-get install -y --no-install-recommends $BASE_PKGS;     git clone https://github.com/varnish/pkg-hitch.git;     cd pkg-hitch;     git checkout ${PKGCOMMIT};     rm -rf .git;     curl -Lf https://hitch-tls.org/source/hitch-${SRCVER}.tar.gz -o $tmpdir/orig.tgz;     echo \\\&quot;${SHASUM}  $tmpdir/orig.tgz\\\&quot; | sha512sum -c -;     tar xavf $tmpdir/orig.tgz --strip 1;     sed -i         -e \\\&quot;s/@SRCVER@/${SRCVER}/g\\\&quot;         -e \\\&quot;s/@PKGVER@/${PKGVER:-1}/g\\\&quot;         -e \\\&quot;s/@DISTVER@/$DISTVER/g\\\&quot; debian/changelog;     mk-build-deps --install --tool=\\\&quot;apt-get -o Debug::pkgProblemResolver=yes --yes\\\&quot; debian/control;     sed -i '' debian/hitch*;     dpkg-buildpackage -us -uc -j\\\&quot;$(nproc)\\\&quot;;     apt-get -y purge --auto-remove hitch-build-deps $BASE_PKGS;     apt-get -y --no-install-recommends install ../*.deb;     sed -i 's/daemon = on/daemon = off/' /etc/hitch/hitch.conf;     rm -rf /var/lib/apt/lists/* \\\&quot;$tmpdir\\\&quot;\&quot;]}}&quot;
        },
        {
           &quot;v1Compatibility&quot;: &quot;{\&quot;id\&quot;:\&quot;5a12a2c67ff9b5bfad288a4ede18d08c259c301efb85403d08a40ea2ad0eb1f8\&quot;,\&quot;parent\&quot;:\&quot;c03ad9230005f64133de4501e14a882ef25f03443da4da55ca002d5619f998be\&quot;,\&quot;created\&quot;:\&quot;2024-05-14T05:21:33.061082853Z\&quot;,\&quot;container_config\&quot;:{\&quot;Cmd\&quot;:[\&quot;/bin/sh -c #(nop)  ARG SHASUM=62b3554d668c9d17382415db10898bf661ee76343e4ee364f904457efda6cb1eeee7cb81d7a3897734024812b64b1c0e2dc305605706d81a0c1f6030508bf7e2\&quot;]},\&quot;throwaway\&quot;:true}&quot;
        },
        {
           &quot;v1Compatibility&quot;: &quot;{\&quot;id\&quot;:\&quot;c03ad9230005f64133de4501e14a882ef25f03443da4da55ca002d5619f998be\&quot;,\&quot;parent\&quot;:\&quot;24e7aee556d6a38bfa2e13430db8a998c023a2920017eabc0b3bf0dd7661bf7d\&quot;,\&quot;created\&quot;:\&quot;2024-05-14T05:21:32.967727298Z\&quot;,\&quot;container_config\&quot;:{\&quot;Cmd\&quot;:[\&quot;/bin/sh -c #(nop)  ARG PKGCOMMIT=f12ab7958bc4885f3f00311cbca5103d9e6ba794\&quot;]},\&quot;throwaway\&quot;:true}&quot;
        },
        {
           &quot;v1Compatibility&quot;: &quot;{\&quot;id\&quot;:\&quot;24e7aee556d6a38bfa2e13430db8a998c023a2920017eabc0b3bf0dd7661bf7d\&quot;,\&quot;parent\&quot;:\&quot;f0d07a99d7d1f0b849a4cbe8fc4552d374f4448c2e7f8bfd908aa43132c4ec34\&quot;,\&quot;created\&quot;:\&quot;2024-05-14T05:21:32.875807605Z\&quot;,\&quot;container_config\&quot;:{\&quot;Cmd\&quot;:[\&quot;/bin/sh -c #(nop)  ARG DISTVER=bullseye\&quot;]},\&quot;throwaway\&quot;:true}&quot;
        },
        {
           &quot;v1Compatibility&quot;: &quot;{\&quot;id\&quot;:\&quot;f0d07a99d7d1f0b849a4cbe8fc4552d374f4448c2e7f8bfd908aa43132c4ec34\&quot;,\&quot;parent\&quot;:\&quot;65c7b6d17437bf7a3216e2fea283071e9b5c0d71c6b97472baa8807a30b5d9d8\&quot;,\&quot;created\&quot;:\&quot;2024-05-14T05:21:32.781941821Z\&quot;,\&quot;container_config\&quot;:{\&quot;Cmd\&quot;:[\&quot;/bin/sh -c #(nop)  ARG PKGVER=1\&quot;]},\&quot;throwaway\&quot;:true}&quot;
        },
        {
           &quot;v1Compatibility&quot;: &quot;{\&quot;id\&quot;:\&quot;65c7b6d17437bf7a3216e2fea283071e9b5c0d71c6b97472baa8807a30b5d9d8\&quot;,\&quot;parent\&quot;:\&quot;863a608d086b1bcf7f9b30ccf57260e6cb5d3d793b4e1131aa8f6041b07a7270\&quot;,\&quot;created\&quot;:\&quot;2024-05-14T05:21:32.682503634Z\&quot;,\&quot;container_config\&quot;:{\&quot;Cmd\&quot;:[\&quot;/bin/sh -c #(nop)  ARG SRCVER=1.8.0\&quot;]},\&quot;throwaway\&quot;:true}&quot;
        },
        {
           &quot;v1Compatibility&quot;: &quot;{\&quot;id\&quot;:\&quot;863a608d086b1bcf7f9b30ccf57260e6cb5d3d793b4e1131aa8f6041b07a7270\&quot;,\&quot;parent\&quot;:\&quot;e00e363f3a25341591a5a5e724e20ae3e70f0396be8483a07c0b39d25d33fecd\&quot;,\&quot;created\&quot;:\&quot;2024-05-14T01:28:27.043980081Z\&quot;,\&quot;container_config\&quot;:{\&quot;Cmd\&quot;:[\&quot;/bin/sh -c #(nop)  CMD [\\\&quot;bash\\\&quot;]\&quot;]},\&quot;throwaway\&quot;:true}&quot;
        },
        {
           &quot;v1Compatibility&quot;: &quot;{\&quot;id\&quot;:\&quot;e00e363f3a25341591a5a5e724e20ae3e70f0396be8483a07c0b39d25d33fecd\&quot;,\&quot;created\&quot;:\&quot;2024-05-14T01:28:26.699066026Z\&quot;,\&quot;container_config\&quot;:{\&quot;Cmd\&quot;:[\&quot;/bin/sh -c #(nop) ADD file:9b38b383dd93169a663eed88edf3f2285b837257ead69dc40ab5ed1fb3f52c35 in / \&quot;]}}&quot;
        }
     ],
     ...
     ]
  }
  </code></pre>
  <p>The sha256sum used to download the blob of the <strong>first layer</strong> is found at the first element of the <code>fsLayers[]</code> array (<strong><code>fsLayers[0]</code></strong>). The metadata about this first layer is found at the first element of the <code>history[]</code> array (<strong><code>history[0]</code></strong>).</p>
  <p>The sha256sum used to download the blob of the <strong>second layer</strong> is found at second element of the <code>fsLayers[]</code> array (<strong><code>fsLayers[1]</code></strong>). The metadata about this second layer is found at the second element of the <code>history[]</code> array (<strong><code>history[1]</code></strong>).</p>
  <p>Et cetera...</p>
  <h2>Download the Layers</h2>
  <p>So how do we download each of these layers separately, yet organize them such that we can later import them as a single image into docker? The answer to that lies in the <a href="https://github.com/moby/moby/blob/79910625f0a7aa76590e4362817dda22f28343aa/image/spec/v1.md" rel="nofollow noreferrer">Docker Image Specification v1.0.0</a>.</p>
  <p>The above spec provides an example tree of the files:</p>
  <blockquote>
  <p>For example, here's what the full archive of <code>library/busybox</code> is (displayed in <code>tree</code> format):</p>
  <p><br />
  <code>.</code><br />
  <code>├── 5785b62b697b99a5af6cd5d0aabc804d5748abbb6d3d07da5d1d3795f2dcc83e</code><br />
  <code>│ ├── VERSION</code><br />
  <code>│ ├── json</code><br />
  <code>│ └── layer.tar</code><br />
  <code>├── a7b8b41220991bfc754d7ad445ad27b7f272ab8b4a2c175b9512b97471d02a8a</code><br />
  <code>│ ├── VERSION</code><br />
  <code>│ ├── json</code><br />
  <code>│ └── layer.tar</code><br />
  <code>├── a936027c5ca8bf8f517923169a233e391cbb38469a75de8383b5228dc2d26ceb</code><br />
  <code>│ ├── VERSION</code><br />
  <code>│ ├── json</code><br />
  <code>│ └── layer.tar</code><br />
  <code>├── f60c56784b832dd990022afc120b8136ab3da9528094752ae13fe63a2d28dc8c</code><br />
  <code>│ ├── VERSION</code><br />
  <code>│ ├── json</code><br />
  <code>│ └── layer.tar</code><br />
  <code>└── repositories</code>\</p>
  <p>There are one or more directories named with the ID for each layer in a full image. Each of these directories contains 3 files:</p>
  <ul>
  <li>`VERSION` - The schema version of the `json` file</li>
  <li>`json` - The JSON metadata for an image layer</li>
  <li>`layer.tar` - The Tar archive of the filesystem changeset for an image<br />
  layer.</li>
  </ul>
  <p>The content of the <code>VERSION</code> files is simply the semantic version of the JSON metadata schema:<br />
  <br />
  <code>1.0</code>\</p>
  <p>And the <code>repositories</code> file is another JSON file which describes names/tags:</p>
  <pre><code>{  
      &quot;busybox&quot;:{  
          &quot;latest&quot;:&quot;5785b62b697b99a5af6cd5d0aabc804d5748abbb6d3d07da5d1d3795f2dcc83e&quot;
      }
  }
  </code></pre>
  <p>Every key in this object is the name of a repository, and maps to a collection of tag suffixes. Each tag maps to the ID of the image represented by that tag.</p>
  </blockquote>
  <p>So, as shown in the quote from the spec above, what we need to do is to create a set of directories -- one for each layer -- named after the layer's ID. Each of these layer-specific directories must contain 3 files:</p>
  <ol>
  <li>The actual tarball of the layer (named '<code>layer.tar</code>'),</li>
  <li>The metadata of the layer (in a file literally named '<code>json</code>' -- with no file extension)</li>
  <li>A file named '<code>VERSION</code>' whose contents is literally just '<code>1.0</code>'</li>
  </ol>
  <p>All of the information that we need is in the '<code>manifest.json</code>' file that we already downloaded. Let's just loop through each layer that it defines, download the <code>layer.tar</code> tarball, create the '<code>json</code>' metadata file, and hard-code the '<code>VERSION</code>' file with the following BASH snippet</p>
  <pre><code>num_layers=$(cat manifest.json | jq -r &quot;.history | length&quot;)
  
  for ((i = 0 ; i &lt; $num_layers ; i++)); do
      layer_blobSum=$(cat manifest.json | jq -r &quot;.fsLayers[$i].blobSum&quot;)
      layer_metadata=$(cat manifest.json | jq -r &quot;.history[$i].v1Compatibility&quot;)
      layer_id=$(echo $layer_metadata | jq -r &quot;.id&quot;)
  
      echo $layer_id
      echo $layer_blobSum
  
      mkdir -p &quot;layers/$layer_id&quot;
      echo &quot;1.0&quot; &gt; &quot;layers/$layer_id/VERSION&quot;
      echo $layer_metadata &gt; &quot;layers/$layer_id/json&quot;
      curl -o &quot;layers/$layer_id/layer.tar&quot; -#LH &quot;Authorization: Bearer ${token}&quot; &quot;https://registry-1.docker.io/v2/library/&lt;package_name&gt;/blobs/${layer_blobSum}&quot;
  done
  </code></pre>
  <p>And here's an example execution that executes the above snippet to download all of the layers onto disk in a set of directories as defined by the <a href="https://github.com/moby/moby/blob/79910625f0a7aa76590e4362817dda22f28343aa/image/spec/v1.md" rel="nofollow noreferrer">Docker Image Specification v1.0.0</a>.</p>
  <pre><code>user@disp7456:~$ num_layers=$(cat manifest.json | jq -r &quot;.history | length&quot;)
  user@disp7456:~$ 
  
  user@disp7456:~$ for ((i = 0 ; i &lt; $num_layers ; i++)); do
      layer_blobSum=$(cat manifest.json | jq -r &quot;.fsLayers[$i].blobSum&quot;)
      layer_metadata=$(cat manifest.json | jq -r &quot;.history[$i].v1Compatibility&quot;)
      layer_id=$(echo $layer_metadata | jq -r &quot;.id&quot;)
  
      echo $layer_id
      echo $layer_blobSum
  
      mkdir -p &quot;layers/$layer_id&quot;
      echo &quot;1.0&quot; &gt; &quot;layers/$layer_id/VERSION&quot;
      echo $layer_metadata &gt; &quot;layers/$layer_id/json&quot;
      curl -o &quot;layers/$layer_id/layer.tar&quot; -#LH &quot;Authorization: Bearer ${token}&quot; &quot;https://registry-1.docker.io/v2/library/hitch/blobs/${layer_blobSum}&quot;
  done
  6703605aae83084affcafb4abcc7c556f0e436c4992ae224f1f58e88242328cb
  sha256:a3ed95caeb02ffe68cdd9fd84406680ae93d633cb16422d00e8a7c22955b46d4
  ################################################################ 100.0%
  c48ca3d95161bbcdfcaa2e016a675965d55f4f06147ef4445c69347c5965f188
  sha256:a3ed95caeb02ffe68cdd9fd84406680ae93d633cb16422d00e8a7c22955b46d4
  ################################################################ 100.0%
  1d2af5a156bbc461d98824c2f6bfe295327d4419105c0b7f88f14cb28d0bb240
  sha256:a3ed95caeb02ffe68cdd9fd84406680ae93d633cb16422d00e8a7c22955b46d4
  ################################################################ 100.0%
  8f914c821cbe154cc6677bceb043669e4295ad5cfb6409efa9c2ec1beba75fad
  sha256:3148f4af0a813bcff0a3ed2562aabfb1b596b52ef36eb5eb4d82ce836350b73a
  ################################################################ 100.0%
  a8d8314458142ee2a4ebccb19f48b6f9c696100103c3d49cbbe7ecd2575120e5
  sha256:a3ed95caeb02ffe68cdd9fd84406680ae93d633cb16422d00e8a7c22955b46d4
  ################################################################ 100.0%
  5a78b0e89bbae2390b83e60174ae1efc583f766eff7dffaffa747ccb67472d0f
  sha256:a0e9543db8c1238572466cf00b55436bc7b7e849f7cb305128f391a94b75c2fc
  ################################################################ 100.0%
  5a12a2c67ff9b5bfad288a4ede18d08c259c301efb85403d08a40ea2ad0eb1f8
  sha256:a3ed95caeb02ffe68cdd9fd84406680ae93d633cb16422d00e8a7c22955b46d4
  ################################################################ 100.0%
  c03ad9230005f64133de4501e14a882ef25f03443da4da55ca002d5619f998be
  sha256:a3ed95caeb02ffe68cdd9fd84406680ae93d633cb16422d00e8a7c22955b46d4
  ################################################################ 100.0%
  24e7aee556d6a38bfa2e13430db8a998c023a2920017eabc0b3bf0dd7661bf7d
  sha256:a3ed95caeb02ffe68cdd9fd84406680ae93d633cb16422d00e8a7c22955b46d4
  ################################################################ 100.0%
  f0d07a99d7d1f0b849a4cbe8fc4552d374f4448c2e7f8bfd908aa43132c4ec34
  sha256:a3ed95caeb02ffe68cdd9fd84406680ae93d633cb16422d00e8a7c22955b46d4
  ################################################################ 100.0%
  65c7b6d17437bf7a3216e2fea283071e9b5c0d71c6b97472baa8807a30b5d9d8
  sha256:a3ed95caeb02ffe68cdd9fd84406680ae93d633cb16422d00e8a7c22955b46d4
  ################################################################ 100.0%
  863a608d086b1bcf7f9b30ccf57260e6cb5d3d793b4e1131aa8f6041b07a7270
  sha256:a3ed95caeb02ffe68cdd9fd84406680ae93d633cb16422d00e8a7c22955b46d4
  ################################################################ 100.0%
  e00e363f3a25341591a5a5e724e20ae3e70f0396be8483a07c0b39d25d33fecd
  sha256:728328ac3bde9b85225b1f0d60f5c149f5635a191f5d8eaeeb00e095d36ef9fd
  ################################################################ 100.0%
  user@disp7456:~$ 
  
  user@disp7456:~$ tree layers/
  layers/
  ├── 1d2af5a156bbc461d98824c2f6bfe295327d4419105c0b7f88f14cb28d0bb240
  │   ├── json
  │   ├── layer.tar
  │   └── VERSION
  ├── 24e7aee556d6a38bfa2e13430db8a998c023a2920017eabc0b3bf0dd7661bf7d
  │   ├── json
  │   ├── layer.tar
  │   └── VERSION
  ├── 5a12a2c67ff9b5bfad288a4ede18d08c259c301efb85403d08a40ea2ad0eb1f8
  │   ├── json
  │   ├── layer.tar
  │   └── VERSION
  ├── 5a78b0e89bbae2390b83e60174ae1efc583f766eff7dffaffa747ccb67472d0f
  │   ├── json
  │   ├── layer.tar
  │   └── VERSION
  ├── 65c7b6d17437bf7a3216e2fea283071e9b5c0d71c6b97472baa8807a30b5d9d8
  │   ├── json
  │   ├── layer.tar
  │   └── VERSION
  ├── 6703605aae83084affcafb4abcc7c556f0e436c4992ae224f1f58e88242328cb
  │   ├── json
  │   ├── layer.tar
  │   └── VERSION
  ├── 863a608d086b1bcf7f9b30ccf57260e6cb5d3d793b4e1131aa8f6041b07a7270
  │   ├── json
  │   ├── layer.tar
  │   └── VERSION
  ├── 8f914c821cbe154cc6677bceb043669e4295ad5cfb6409efa9c2ec1beba75fad
  │   ├── json
  │   ├── layer.tar
  │   └── VERSION
  ├── a8d8314458142ee2a4ebccb19f48b6f9c696100103c3d49cbbe7ecd2575120e5
  │   ├── json
  │   ├── layer.tar
  │   └── VERSION
  ├── c03ad9230005f64133de4501e14a882ef25f03443da4da55ca002d5619f998be
  │   ├── json
  │   ├── layer.tar
  │   └── VERSION
  ├── c48ca3d95161bbcdfcaa2e016a675965d55f4f06147ef4445c69347c5965f188
  │   ├── json
  │   ├── layer.tar
  │   └── VERSION
  ├── e00e363f3a25341591a5a5e724e20ae3e70f0396be8483a07c0b39d25d33fecd
  │   ├── json
  │   ├── layer.tar
  │   └── VERSION
  └── f0d07a99d7d1f0b849a4cbe8fc4552d374f4448c2e7f8bfd908aa43132c4ec34
      ├── json
      ├── layer.tar
      └── VERSION
  
  14 directories, 39 files
  user@disp7456:~$ 
  </code></pre>
  <p>Finally, besides these layer-specific dirs, we need one additional file (named simply '<code>repository</code>', with no file extension) at the same height as these dirs. As defined by the <a href="https://github.com/moby/moby/blob/79910625f0a7aa76590e4362817dda22f28343aa/image/spec/v1.md" rel="nofollow noreferrer">Docker Image Specification v1.0.0</a>, this file should state the name &amp; tag of the image, and it points to the first layer of the image.</p>
  <p>Note the <code>0th</code> item in the <code>history[]</code> array is the first layer of the image, so we can create this file with the following command</p>
  <pre><code>start_image=$(cat manifest.json | jq -r &quot;.history[0].v1Compatibility&quot;)
  start_image_id=$(echo $start_image | jq -r &quot;.id&quot;)
  
  cat &gt; layers/repositories &lt;&lt;EOF
  {
      &quot;&lt;image_name&gt;&quot;: { &quot;stable&quot;: &quot;$start_image_id&quot; }
  }
  EOF
  </code></pre>
  <p>And here's an example execution to create our '<code>repository</code>' file for the '<code>hitch</code>' package.</p>
  <pre><code>user@disp7456:~$ start_image=$(cat manifest.json | jq -r &quot;.history[0].v1Compatibility&quot;)
  user@disp7456:~$ 
  
  user@disp7456:~$ start_image_id=$(echo $start_image | jq -r &quot;.id&quot;)
  user@disp7456:~$ 
  
  user@disp7456:~$ cat &gt; layers/repositories &lt;&lt;EOF
  {
      &quot;hitch&quot;: { &quot;stable&quot;: &quot;$start_image_id&quot; }
  }
  EOF
  user@disp7456:~$
  
  user@disp7456:~$ ls layers
  1d2af5a156bbc461d98824c2f6bfe295327d4419105c0b7f88f14cb28d0bb240
  24e7aee556d6a38bfa2e13430db8a998c023a2920017eabc0b3bf0dd7661bf7d
  5a12a2c67ff9b5bfad288a4ede18d08c259c301efb85403d08a40ea2ad0eb1f8
  5a78b0e89bbae2390b83e60174ae1efc583f766eff7dffaffa747ccb67472d0f
  65c7b6d17437bf7a3216e2fea283071e9b5c0d71c6b97472baa8807a30b5d9d8
  6703605aae83084affcafb4abcc7c556f0e436c4992ae224f1f58e88242328cb
  863a608d086b1bcf7f9b30ccf57260e6cb5d3d793b4e1131aa8f6041b07a7270
  8f914c821cbe154cc6677bceb043669e4295ad5cfb6409efa9c2ec1beba75fad
  a8d8314458142ee2a4ebccb19f48b6f9c696100103c3d49cbbe7ecd2575120e5
  c03ad9230005f64133de4501e14a882ef25f03443da4da55ca002d5619f998be
  c48ca3d95161bbcdfcaa2e016a675965d55f4f06147ef4445c69347c5965f188
  e00e363f3a25341591a5a5e724e20ae3e70f0396be8483a07c0b39d25d33fecd
  f0d07a99d7d1f0b849a4cbe8fc4552d374f4448c2e7f8bfd908aa43132c4ec34
  repositories
  user@disp7456:~$
  
  user@disp7456:~$ cat layers/repositories
  {
      &quot;hitch&quot;: { &quot;stable&quot;: &quot;6703605aae83084affcafb4abcc7c556f0e436c4992ae224f1f58e88242328cb&quot; }
  }
  user@disp7456:~$ 
  </code></pre>
  <p>Your '<code>layers/</code>' directory should now be prepared-to-spec for importing the entire image into docker.</p>
  <p>For reference, here's the contents of just one of the layers:</p>
  <pre><code>user@disp7456:~$ ls layers/8f914c821cbe154cc6677bceb043669e4295ad5cfb6409efa9c2ec1beba75fad/
  json  layer.tar  VERSION
  user@disp7456:~$ 
  
  user@disp7456:~$ cat layers/8f914c821cbe154cc6677bceb043669e4295ad5cfb6409efa9c2ec1beba75fad/VERSION 
  1.0
  user@disp7456:~$ 
  
  user@disp7456:~$ cat layers/8f914c821cbe154cc6677bceb043669e4295ad5cfb6409efa9c2ec1beba75fad/json 
  {&quot;id&quot;:&quot;8f914c821cbe154cc6677bceb043669e4295ad5cfb6409efa9c2ec1beba75fad&quot;,&quot;parent&quot;:&quot;a8d8314458142ee2a4ebccb19f48b6f9c696100103c3d49cbbe7ecd2575120e5&quot;,&quot;created&quot;:&quot;2024-05-14T05:23:11.403178706Z&quot;,&quot;container_config&quot;:{&quot;Cmd&quot;:[&quot;/bin/sh -c #(nop) COPY file:1abf3c94dce5dc9f6617dc8d36a6fe6f4f7236189d4819f16cefb54288e80e0d in /usr/local/bin/ &quot;]}}
  user@disp7456:~$ 
  
  user@disp7456:~$ sha256sum layers/8f914c821cbe154cc6677bceb043669e4295ad5cfb6409efa9c2ec1beba75fad/layer.tar 
  3148f4af0a813bcff0a3ed2562aabfb1b596b52ef36eb5eb4d82ce836350b73a  layers/8f914c821cbe154cc6677bceb043669e4295ad5cfb6409efa9c2ec1beba75fad/layer.tar
  user@disp7456:~$ 
  
  user@disp7456:~$ tar -tvf layers/8f914c821cbe154cc6677bceb043669e4295ad5cfb6409efa9c2ec1beba75fad/layer.tar 
  drwxr-xr-x 0/0               0 2024-05-12 19:00 usr/
  drwxr-xr-x 0/0               0 2024-05-12 19:00 usr/local/
  drwxr-xr-x 0/0               0 2024-05-14 00:23 usr/local/bin/
  -rwxrwxr-x 0/0             319 2024-05-14 00:21 usr/local/bin/docker-hitch-entrypoint
  user@disp7456:~$ 
  </code></pre>
  <h2>Load the Image</h2>
  <p>Finally, you can copy the whole <code>layers</code> directory into your restricted environment and then load the layers as one image into docker with <code>docker image load</code></p>
  <pre><code>tar -cC layers . | docker image load
  </code></pre>
  <p>Here's an example execution</p>
  <pre><code>user@disp7456:~$ docker image ls
  REPOSITORY   TAG       IMAGE ID   CREATED   SIZE
  user@disp7456:~$ 
  
  user@disp7456:~$ tar -cC layers . | docker load
  e00e363f3a25: Loading layer [==================================================&gt;]  31.43MB/31.43MB
  863a608d086b: Loading layer [==================================================&gt;]      32B/32B
  65c7b6d17437: Loading layer [==================================================&gt;]      32B/32B
  f0d07a99d7d1: Loading layer [==================================================&gt;]      32B/32B
  24e7aee556d6: Loading layer [==================================================&gt;]      32B/32B
  c03ad9230005: Loading layer [==================================================&gt;]      32B/32B
  5a12a2c67ff9: Loading layer [==================================================&gt;]      32B/32B
  5a78b0e89bba: Loading layer [==================================================&gt;]  1.573MB/1.573MB
  a8d831445814: Loading layer [==================================================&gt;]      32B/32B
  8f914c821cbe: Loading layer [==================================================&gt;]     415B/415B
  1d2af5a156bb: Loading layer [==================================================&gt;]      32B/32B
  c48ca3d95161: Loading layer [==================================================&gt;]      32B/32B
  6703605aae83: Loading layer [==================================================&gt;]      32B/32B
  user@disp7456:~$ 
  
  user@disp7456:~$ docker image ls
  REPOSITORY   TAG       IMAGE ID       CREATED       SIZE
  hitch        stable    f07eadb841be   3 weeks ago   85.1MB
  user@disp7456:~$ 
  </code></pre>
  <h1>Attribution</h1>
  <p>The example above was copied from <a href="https://tech.michaelaltfield.net/2024/09/03/container-download-curl-wget/" rel="nofollow noreferrer">Manually Downloading Container Images (Docker, Github Packages)</a></p>
  
Answers: CC BY-SA 4.0
Answers: 2024-09-03T15:30:44.203Z
Answers: 2024-09-03T15:30:44.203Z
Answers: Michael Altfield
Answers: <p>If your company has nexus or a similar repository, they may already have set up nexus to pull in what you need. You could try adding your repo's host/ip in front of the relative docker hub path.</p>
  
  <p>Examples:</p>
  
  <pre><code>docker pull nexus.example.com:18443/nginx
  docker pull nexus.example.com:18443/selenium/node-chrome
  docker pull nexus.example.com:18443/postgres:9.4
  </code></pre>
  
Answers: CC BY-SA 4.0
Answers: 2020-02-10T20:56:11.603Z
Answers: 2020-02-10T20:56:11.603Z
Answers: ScrappyDev
Answers: <p>It turned out that the <a href="https://mobyproject.org/" rel="noreferrer">Moby Project</a> has a shell script on the <a href="https://github.com/moby/moby/" rel="noreferrer">Moby Github</a> which can download images from <a href="https://hub.docker.com/" rel="noreferrer">Docker Hub</a> in a format that can be imported into Docker:</p>
  <ul>
  <li><a href="https://raw.githubusercontent.com/moby/moby/master/contrib/download-frozen-image-v2.sh" rel="noreferrer">download-frozen-image-v2.sh</a></li>
  </ul>
  <p>The usage syntax for the script is given by the following:</p>
  <pre class="lang-bash prettyprint-override"><code>download-frozen-image-v2.sh target_dir image[:tag][@digest] ...
  </code></pre>
  <p>The image can then be imported with <code>tar</code> and <code>docker load</code>:</p>
  <pre class="lang-bash prettyprint-override"><code>tar -cC 'target_dir' . | docker load
  </code></pre>
  <p>To verify that the script works as expected, I downloaded an Ubuntu image from <a href="https://hub.docker.com/" rel="noreferrer">Docker Hub</a> and loaded it into Docker:</p>
  <pre class="lang-bash prettyprint-override"><code>user@host:~$ bash download-frozen-image-v2.sh ubuntu ubuntu:latest
  user@host:~$ tar -cC 'ubuntu' . | docker load
  user@host:~$ docker run --rm -ti ubuntu bash
  root@1dd5e62113b9:/#
  </code></pre>
  <p>In practice I would have to first copy the data from the internet client (which does <em>not</em> have Docker installed) to the target/destination machine (which <em>does</em> have Docker installed):</p>
  <pre class="lang-bash prettyprint-override"><code>user@nodocker:~$ bash download-frozen-image-v2.sh ubuntu ubuntu:latest
  user@nodocker:~$ tar -C 'ubuntu' -cf 'ubuntu.tar' .
  user@nodocker:~$ scp ubuntu.tar user@hasdocker:~
  </code></pre>
  <p>and then load and use the image on the target host:</p>
  <pre class="lang-bash prettyprint-override"><code>user@hasdocker:~ docker load ubuntu.tar
  user@hasdocker:~ docker run --rm -ti ubuntu bash
  root@1dd5e62113b9:/#
  </code></pre>
  
Answers: CC BY-SA 3.0
Answers: 2017-12-03T15:11:37.627Z
Answers: `The machine with internet connectivity does not and cannot have Docker installed`. but you apply `docker load`
Answers: 030
Answers: CC BY-SA 3.0
Answers: 2017-12-03T15:13:05.310Z
Answers: @030 Just to test/demonstrate that the script works and that the download image data can be imported into Docker. In practice I would first have to copy the data to a machine with Docker installed.
Answers: igal
Answers: CC BY-SA 3.0
Answers: 2017-12-03T15:13:41.220Z
Answers: Perhaps you could add that part for clarification
Answers: 030
Answers: CC BY-SA 3.0
Answers: 2017-12-03T15:20:27.787Z
Answers: @030 I added an example session illustrating what the workflow would look like in practice.
Answers: igal
Answers: CC BY-SA 4.0
Answers: 2017-12-03T14:56:17.250Z
Answers: 2022-03-08T10:47:11.057Z
Answers: 2022-03-08T10:47:11.057Z
Answers: igal
Body: <p>I want to manually download a Docker Image from <a href="https://hub.docker.com/" rel="noreferrer">Docker Hub</a>. More specifically, I want to download a Docker Image from Docker Hub on a machine in a restricted environment which does not (and cannot) have the Docker client software installed. I would have thought that this would be possible using the <a href="https://docs.docker.com/v1.7/docker/reference/api/docker-io_api/" rel="noreferrer">official API</a>, but this does not appear to be the case - see the following discussion:</p>
  
  <ul>
  <li><a href="https://github.com/docker/distribution/issues/1016" rel="noreferrer">Fetch docker images without docker command. e.g. with wget</a></li>
  </ul>
  
  <p>Is it really the case that the API doesn't support downloading images? Is there a way to work around this?</p>
  
  <hr>
  
  <p><strong>UPDATE 1:</strong></p>
  
  <p>I came across the following ServerFault post:</p>
  
  <ul>
  <li><a href="https://serverfault.com/questions/701248/downloading-docker-image-for-transfer-to-non-internet-connected-machine">Downloading docker image for transfer to non-internet-connected machine</a></li>
  </ul>
  
  <p>The <a href="https://serverfault.com/a/718470/264818">accepted solution</a> uses the <code>docker save</code> command, which doesn't help in my situation. But <a href="https://serverfault.com/a/823571/264818">another solution posted there</a> cites the following StackOverflow post:</p>
  
  <ul>
  <li><a href="https://stackoverflow.com/questions/22381442/pulling-docker-images/25072118">Pulling docker images</a></li>
  </ul>
  
  <p><a href="https://stackoverflow.com/a/25072118/4451345">One of the solutions there</a> refers to a command-line tool called <a href="https://github.com/samalba/docker-registry-debug" rel="noreferrer">docker-registry-debug</a> which, among other things, can generate a <code>curl</code> command for downloading an image. Here is what I got:</p>
  
  <pre class="lang-none prettyprint-override"><code>user@host:~$ docker-registry-debug curlme docker ubuntu
  
  # Reading user/passwd from env var "USER_CREDS"
  # No password provided, disabling auth
  # Getting token from https://index.docker.io
  # Got registry endpoint from the server: https://registry-1.docker.io
  # Got token: signature=1234567890abcde1234567890abcde1234567890,repository="library/docker",access=read
  curl -i --location-trusted -I -X GET -H "Authorization: Token signature=1234567890abcde1234567890abcde1234567890,repository="library/docker",access=read" https://registry-1.docker.io/v1/images/ubuntu/layer
  
  user@host:~$ curl \
  -i --location-trusted -I -X GET \
  -H "Authorization: Token signature=1234567890abcde1234567890abcde1234567890,repository="library/docker",access=read" 
  
  https://registry-1.docker.io/v1/images/ubuntu/layer
  HTTP/1.1 404 NOT FOUND
  Server: gunicorn/18.0
  Date: Wed, 29 Nov 2017 01:00:00 GMT
  Expires: -1
  Content-Type: application/json
  Pragma: no-cache
  Cache-Control: no-cache
  Content-Length: 29
  X-Docker-Registry-Version: 0.8.15
  X-Docker-Registry-Config: common
  Strict-Transport-Security: max-age=31536000
  </code></pre>
  
  <p>So unfortunately it looks like the <code>curl</code> command generated does not work.</p>
  
  <hr>
  
  <p><strong>UPDATE 2:</strong></p>
  
  <p>It looks like I'm able to download layer blobs from Docker Hub. Here is how I'm currently going about it.</p>
  
  <p>Get an authorization token:</p>
  
  <pre><code>user@host:~$ export TOKEN=\
  "$(curl \
  --silent \
  --header 'GET' \
  "https://auth.docker.io/token?service=registry.docker.io&amp;scope=repository:library/ubuntu:pull" \
  | jq -r '.token' \
  )"
  </code></pre>
  
  <p>Pull an image manifest:</p>
  
  <pre><code>user@host:~$ curl \
  --silent \
  --request 'GET' \
  --header "Authorization: Bearer ${TOKEN}" \
  'https://registry-1.docker.io/v2/library/ubuntu/manifests/latest' \
  | jq '.'
  </code></pre>
  
  <p>Pull an image manifest and extract the blob sums:</p>
  
  <pre><code>user@host:~$ curl \
  --silent \
  --request 'GET' \
  --header "Authorization: Bearer ${TOKEN}" \
  'https://registry-1.docker.io/v2/library/ubuntu/manifests/latest' \
  | jq -r '.fsLayers[].blobSum'
  
  sha256:a3ed95caeb02ffe68cdd9fd84406680ae93d633cb16422d00e8a7c22955b46d4
  sha256:be588e74bd348ce48bb7161350f4b9d783c331f37a853a80b0b4abc0a33c569e
  sha256:e4ce6c3651b3a090bb43688f512f687ea6e3e533132bcbc4a83fb97e7046cea3
  sha256:421e436b5f80d876128b74139531693be9b4e59e4f1081c9a3c379c95094e375
  sha256:4c7380416e7816a5ab1f840482c9c3ca8de58c6f3ee7f95e55ad299abbfe599f
  sha256:660c48dd555dcbfdfe19c80a30f557ac57a15f595250e67bfad1e5663c1725bb
  </code></pre>
  
  <p>Download a single layer blob and write it to a file:</p>
  
  <pre><code>user@host:~$ BLOBSUM=\
  "sha256:a3ed95caeb02ffe68cdd9fd84406680ae93d633cb16422d00e8a7c22955b46d4"
  
  user@host:~$ curl \
  --silent \
  --location \
  --request GET \
  --header "Authorization: Bearer ${TOKEN}" \
  "https://registry-1.docker.io/v2/library/ubuntu/blobs/${BLOBSUM}" \
  &gt; "${BLOBSUM/*:/}.gz"
  </code></pre>
  
  <p>Write all of the blob sums to a file:</p>
  
  <pre><code>user@host:~$ curl \
  --silent \
  --request 'GET' \
  --header "Authorization: Bearer ${TOKEN}" \
  'https://registry-1.docker.io/v2/library/ubuntu/manifests/latest' \
  | jq -r '.fsLayers[].blobSum' &gt; ubuntu-blobsums.txt
  </code></pre>
  
  <p>Download all of the layer blobs from the manifest:</p>
  
  <pre><code>user@host:~$ while read BLOBSUM; do
  curl \
  --silent \
  --location \
  --request 'GET' \
  --header "Authorization: Bearer ${TOKEN}" \
  "https://registry-1.docker.io/v2/library/ubuntu/blobs/${BLOBSUM}" \
  &gt; "${BLOBSUM/*:/}.gz"; \
  done &lt; blobsums.txt
  </code></pre>
  
  <p>Now I have a bunch of layer blobs and I need to recombine them into an image - I think.</p>
  
  <hr>
  
  <p>Related Links:</p>
  
  <ul>
  <li><p><a href="https://forums.docker.com/t/docker-hub-api-retrieve-images/23046" rel="noreferrer">Docker Community Forums: <strong>Docker Hub API retrieve images</strong></a></p></li>
  <li><p><a href="https://forums.docker.com/t/manual-download-of-docker-hub-images/26073" rel="noreferrer">Docker Community Forums: <strong>Manual download of Docker Hub images</strong></a></p></li>
  <li><p><a href="https://github.com/docker/distribution/issues/1016" rel="noreferrer">Docker Issue #1016: <strong>Fetch docker images without docker command. e.g. with wget</strong></a></p></li>
  <li><p><a href="https://serverfault.com/questions/701248/downloading-docker-image-for-transfer-to-non-internet-connected-machine">ServerFault: <strong>Downloading docker image for transfer to non-internet-connected machine</strong></a></p></li>
  <li><p><a href="https://stackoverflow.com/questions/22381442/pulling-docker-images/">StackOverflow: <strong>Downloading docker image for transfer to non-internet-connected machine</strong></a></p></li>
  <li><p><a href="https://stackoverflow.com/questions/37905763/how-to-download-docker-images-without-using-pull-command">StackOverflow: <strong>How to download docker images without using pull command?</strong></a></p></li>
  <li><p><a href="https://stackoverflow.com/questions/40402513/is-there-a-way-to-download-docker-hub-images-whitout-docker-pull-for-a-machine">StackOverflow: <strong>Is there a way to download docker hub images without “docker pull” for a machine with out Internet access?</strong></a></p></li>
  <li><p><a href="https://stackoverflow.com/questions/34198392/docker-official-registry-docker-hub-url">StackOverflow: <strong>Docker official registry (Docker Hub) URL</strong></a></p></li>
  </ul>
  
Comments: CC BY-SA 3.0
Comments: 2017-11-30T11:06:03.160Z
Comments: "More specifically, I want to download a Docker Image from Docker Hub on a machine in a restricted environment which does not (and cannot) have the Docker client software installed." => What's the point to have the images on this machine then ? (easier workaround is using a pivot host, one where you acn docker pull from dockerhub and docker save/ docker push to an internal registry after)
Comments: Tensibai
Comments: CC BY-SA 3.0
Comments: 2017-11-30T13:46:04.853Z
Comments: @Tensibai To copy it to another machine that *does* have Docker but *doesn't* have internet access.
Comments: igal
Comments: CC BY-SA 3.0
Comments: 2017-11-30T19:04:00.370Z
Comments: Did you had a look at the docker pull code? It sounds the way to go to build something like this from basic http calls
Comments: Tensibai
Comments: CC BY-SA 3.0
Comments: 2017-11-30T20:57:39.843Z
Comments: @Tensibai I think I figured it out. I also think I got a solution from the Docker community. I'll come back and post the solution later today.
Comments: igal
Comments: CC BY-SA 3.0
Comments: 2017-12-03T15:04:26.180Z
Comments: @Tensibai I posted [a solution](https://devops.stackexchange.com/a/2772/3070) with a shell script that solves the problem.
Comments: igal
Comments: CC BY-SA 4.0
Comments: 2019-03-23T12:43:17.253Z
Comments: Perhaps it would be easier to give the docker host Internet access by using the other as a proxy
Comments: Marged
Comments: CC BY-SA 4.0
Comments: 2019-03-23T13:00:10.437Z
Comments: @Marged That wasn't an option. The server was configured in such a way that it could not easily be used as a proxy.
Comments: igal
Tags: <docker><dockerhub>
Title: Downloading Docker Images from Docker Hub without using Docker
UserDisplayName: igal
ThreadText: # Downloading Docker Images from Docker Hub without using Docker
  
  (Asked by: username_0 on 2017-11-29)
  
  I want to manually download a Docker Image from [Docker Hub](https://hub.docker.com/). More specifically, I want to download a Docker Image from Docker Hub on a machine in a restricted environment which does not (and cannot) have the Docker client software installed. I would have thought that this would be possible using the [official API](https://docs.docker.com/v1.7/docker/reference/api/docker-io_api/), but this does not appear to be the case - see the following discussion:
  
  * [Fetch docker images without docker command. e.g. with wget](https://github.com/docker/distribution/issues/1016)
  
  Is it really the case that the API doesn't support downloading images? Is there a way to work around this?
  
  ---
  
  **UPDATE 1:**
  
  I came across the following ServerFault post:
  
  * [Downloading docker image for transfer to non-internet-connected machine](https://serverfault.com/questions/701248/downloading-docker-image-for-transfer-to-non-internet-connected-machine)
  
  The [accepted solution](https://serverfault.com/a/718470/264818) uses the `docker save` command, which doesn't help in my situation. But [another solution posted there](https://serverfault.com/a/823571/264818) cites the following StackOverflow post:
  
  * [Pulling docker images](https://stackoverflow.com/questions/22381442/pulling-docker-images/25072118)
  
  [One of the solutions there](https://stackoverflow.com/a/25072118/4451345) refers to a command-line tool called [docker-registry-debug](https://github.com/samalba/docker-registry-debug) which, among other things, can generate a `curl` command for downloading an image. Here is what I got:
  
  ```none
  user@host:~$ docker-registry-debug curlme docker ubuntu
  
  # Reading user/passwd from env var "USER_CREDS"
  # No password provided, disabling auth
  # Getting token from https://index.docker.io
  # Got registry endpoint from the server: https://registry-1.docker.io
  # Got token: signature=1234567890abcde1234567890abcde1234567890,repository="library/docker",access=read
  curl -i --location-trusted -I -X GET -H "Authorization: Token signature=1234567890abcde1234567890abcde1234567890,repository="library/docker",access=read" https://registry-1.docker.io/v1/images/ubuntu/layer
  
  user@host:~$ curl \
  -i --location-trusted -I -X GET \
  -H "Authorization: Token signature=1234567890abcde1234567890abcde1234567890,repository="library/docker",access=read" 
  
  https://registry-1.docker.io/v1/images/ubuntu/layer
  HTTP/1.1 404 NOT FOUND
  Server: gunicorn/18.0
  Date: Wed, 29 Nov 2017 01:00:00 GMT
  Expires: -1
  Content-Type: application/json
  Pragma: no-cache
  Cache-Control: no-cache
  Content-Length: 29
  X-Docker-Registry-Version: 0.8.15
  X-Docker-Registry-Config: common
  Strict-Transport-Security: max-age=31536000
  
  ```
  
  So unfortunately it looks like the `curl` command generated does not work.
  
  ---
  
  **UPDATE 2:**
  
  It looks like I'm able to download layer blobs from Docker Hub. Here is how I'm currently going about it.
  
  Get an authorization token:
  
  ```
  user@host:~$ export TOKEN=\
  "$(curl \
  --silent \
  --header 'GET' \
  "https://auth.docker.io/token?service=registry.docker.io&scope=repository:library/ubuntu:pull" \
  | jq -r '.token' \
  )"
  
  ```
  
  Pull an image manifest:
  
  ```
  user@host:~$ curl \
  --silent \
  --request 'GET' \
  --header "Authorization: Bearer ${TOKEN}" \
  'https://registry-1.docker.io/v2/library/ubuntu/manifests/latest' \
  | jq '.'
  
  ```
  
  Pull an image manifest and extract the blob sums:
  
  ```
  user@host:~$ curl \
  --silent \
  --request 'GET' \
  --header "Authorization: Bearer ${TOKEN}" \
  'https://registry-1.docker.io/v2/library/ubuntu/manifests/latest' \
  | jq -r '.fsLayers[].blobSum'
  
  sha256:a3ed95caeb02ffe68cdd9fd84406680ae93d633cb16422d00e8a7c22955b46d4
  sha256:be588e74bd348ce48bb7161350f4b9d783c331f37a853a80b0b4abc0a33c569e
  sha256:e4ce6c3651b3a090bb43688f512f687ea6e3e533132bcbc4a83fb97e7046cea3
  sha256:421e436b5f80d876128b74139531693be9b4e59e4f1081c9a3c379c95094e375
  sha256:4c7380416e7816a5ab1f840482c9c3ca8de58c6f3ee7f95e55ad299abbfe599f
  sha256:660c48dd555dcbfdfe19c80a30f557ac57a15f595250e67bfad1e5663c1725bb
  
  ```
  
  Download a single layer blob and write it to a file:
  
  ```
  user@host:~$ BLOBSUM=\
  "sha256:a3ed95caeb02ffe68cdd9fd84406680ae93d633cb16422d00e8a7c22955b46d4"
  
  user@host:~$ curl \
  --silent \
  --location \
  --request GET \
  --header "Authorization: Bearer ${TOKEN}" \
  "https://registry-1.docker.io/v2/library/ubuntu/blobs/${BLOBSUM}" \
  > "${BLOBSUM/*:/}.gz"
  
  ```
  
  Write all of the blob sums to a file:
  
  ```
  user@host:~$ curl \
  --silent \
  --request 'GET' \
  --header "Authorization: Bearer ${TOKEN}" \
  'https://registry-1.docker.io/v2/library/ubuntu/manifests/latest' \
  | jq -r '.fsLayers[].blobSum' > ubuntu-blobsums.txt
  
  ```
  
  Download all of the layer blobs from the manifest:
  
  ```
  user@host:~$ while read BLOBSUM; do
  curl \
  --silent \
  --location \
  --request 'GET' \
  --header "Authorization: Bearer ${TOKEN}" \
  "https://registry-1.docker.io/v2/library/ubuntu/blobs/${BLOBSUM}" \
  > "${BLOBSUM/*:/}.gz"; \
  done < blobsums.txt
  
  ```
  
  Now I have a bunch of layer blobs and I need to recombine them into an image - I think.
  
  ---
  
  Related Links:
  
  * [Docker Community Forums: **Docker Hub API retrieve images**](https://forums.docker.com/t/docker-hub-api-retrieve-images/23046)
  * [Docker Community Forums: **Manual download of Docker Hub images**](https://forums.docker.com/t/manual-download-of-docker-hub-images/26073)
  * [Docker Issue #1016: **Fetch docker images without docker command. e.g. with wget**](https://github.com/docker/distribution/issues/1016)
  * [ServerFault: **Downloading docker image for transfer to non-internet-connected machine**](https://serverfault.com/questions/701248/downloading-docker-image-for-transfer-to-non-internet-connected-machine)
  * [StackOverflow: **Downloading docker image for transfer to non-internet-connected machine**](https://stackoverflow.com/questions/22381442/pulling-docker-images/)
  * [StackOverflow: **How to download docker images without using pull command?**](https://stackoverflow.com/questions/37905763/how-to-download-docker-images-without-using-pull-command)
  * [StackOverflow: **Is there a way to download docker hub images without “docker pull” for a machine with out Internet access?**](https://stackoverflow.com/questions/40402513/is-there-a-way-to-download-docker-hub-images-whitout-docker-pull-for-a-machine)
  * [StackOverflow: **Docker official registry (Docker Hub) URL**](https://stackoverflow.com/questions/34198392/docker-official-registry-docker-hub-url)
  
  **Tags:** docker, dockerhub
  
  > **Comments:**
  > **username_1**: "More specifically, I want to download a Docker Image from Docker Hub on a machine in a restricted environment which does not (and cannot) have the Docker client software installed." => What's the point to have the images on this machine then ? (easier workaround is using a pivot host, one where you acn docker pull from dockerhub and docker save/ docker push to an internal registry after)  
  > **username_0**: @username_1 To copy it to another machine that *does* have Docker but *doesn't* have internet access.  
  > **username_1**: Did you had a look at the docker pull code? It sounds the way to go to build something like this from basic http calls  
  > **username_0**: @username_1 I think I figured it out. I also think I got a solution from the Docker community. I'll come back and post the solution later today.  
  > **username_0**: @username_1 I posted [a solution](https://devops.stackexchange.com/a/2772/3070) with a shell script that solves the problem.  
  > **username_2**: Perhaps it would be easier to give the docker host Internet access by using the other as a proxy  
  > **username_0**: @username_2 That wasn't an option. The server was configured in such a way that it could not easily be used as a proxy.  
  
  ---
  
  ## 9 answers
  
  ### Answer by username_0 on 2017-12-03. Score: 56 | Accepted answer
  
  It turned out that the [Moby Project](https://mobyproject.org/) has a shell script on the [Moby Github](https://github.com/moby/moby/) which can download images from [Docker Hub](https://hub.docker.com/) in a format that can be imported into Docker:
  
  * [download-frozen-image-v2.sh](https://raw.githubusercontent.com/moby/moby/master/contrib/download-frozen-image-v2.sh)
  
  The usage syntax for the script is given by the following:
  
  ```bash
  download-frozen-image-v2.sh target_dir image[:tag][@digest] ...
  
  ```
  
  The image can then be imported with `tar` and `docker load`:
  
  ```bash
  tar -cC 'target_dir' . | docker load
  
  ```
  
  To verify that the script works as expected, I downloaded an Ubuntu image from [Docker Hub](https://hub.docker.com/) and loaded it into Docker:
  
  ```bash
  user@host:~$ bash download-frozen-image-v2.sh ubuntu ubuntu:latest
  user@host:~$ tar -cC 'ubuntu' . | docker load
  user@host:~$ docker run --rm -ti ubuntu bash
  root@1dd5e62113b9:/#
  
  ```
  
  In practice I would have to first copy the data from the internet client (which does *not* have Docker installed) to the target/destination machine (which *does* have Docker installed):
  
  ```bash
  user@nodocker:~$ bash download-frozen-image-v2.sh ubuntu ubuntu:latest
  user@nodocker:~$ tar -C 'ubuntu' -cf 'ubuntu.tar' .
  user@nodocker:~$ scp ubuntu.tar user@hasdocker:~
  
  ```
  
  and then load and use the image on the target host:
  
  ```bash
  user@hasdocker:~ docker load ubuntu.tar
  user@hasdocker:~ docker run --rm -ti ubuntu bash
  root@1dd5e62113b9:/#
  
  ```
  
  > **Comments:**
  > **username_8**: `The machine with internet connectivity does not and cannot have Docker installed`. but you apply `docker load`  
  > **username_0**: @username_8 Just to test/demonstrate that the script works and that the download image data can be imported into Docker. In practice I would first have to copy the data to a machine with Docker installed.  
  > **username_8**: Perhaps you could add that part for clarification  
  > **username_0**: @username_8 I added an example session illustrating what the workflow would look like in practice.  
  
  ---
  
  ### Answer by username_6 on 2018-11-20. Score: 20
  
  There is a tool called [Skopeo](https://github.com/containers/skopeo) which can retrieve Docker images from a repository and save them in several formats.
  
  For example:
  
  1. Download the image and save the layers as a tarball:
     `skopeo copy docker://ubuntu docker-archive:/tmp/ubuntu.tar:ubuntu`
  2. Transfer `/tmp/ubuntu.tar` to another machine if desired.
  3. Load the image on a Docker instance which does not have internet connection:
     `docker load --input /tmp/ubuntu.tar`
  
  It is available in CentOS 7 repo with the package name `skopeo`. There are no Debian or Ubuntu packages at this time (but it is easy to compile).
  
  > **Comments:**
  > **username_7**: FWIW there are deb packages now: https://github.com/containers/skopeo/blob/master/install.md  
  
  ---
  
  ### Answer by username_3 on 2019-03-20. Score: 5
  
  thanks for motivation.
  I made a powershell version of it.
  Check it out...
  With it you can move in dockerhub containers to a restricted docker networks with a windows desktop and an ssh-scp tool to docker machine without root or administrator rights
  
  <https://gitlab.com/Jancsoj78/dockerless_docker_downloader>
  a new hacker tool :)
  
  ```
  $image = "ubuntu"
  $tag = "latest"
  $imageuri = "https://auth.docker.io/token?service=registry.docker.io&scope=repository:library/"+$image+":pull"
  $taguri = "https://registry-1.docker.io/v2/library/"+$image+"/manifests/"+$tag
  $bloburi = "https://registry-1.docker.io/v2/library/"+$image+"/blobs/sha256:"
  
  #token request
  $token = Invoke-WebRequest -Uri $imageuri | ConvertFrom-Json | Select -expand token
  
  #pull image manifest
  $blobs = $($(Invoke-Webrequest -Headers @{Authorization="Bearer $token"} -Method GET -Uri $taguri | ConvertFrom-Json | Select -expand fsLayers ) -replace "sha256:" -replace "@{blobSum=" -replace "}")
  
  #download blobs
  for ($i=0; $i -lt $blobs.length; $i++) {
      $blobelement =$blobs[$i]
  
      Invoke-Webrequest -Headers @{Authorization="Bearer $token"} -Method GET -Uri $bloburi$blobelement -OutFile blobtmp
  
      $source = "blobtmp"
      $newfile = "$blobelement.gz"
  
  #overwrite
  Copy-Item $source $newfile -Force -Recurse
  #source blobs
  ls *.gz
  }
  #postprocess
  echo "copy these .gz to your docker machine"
  echo "docker import .gz backward one by one"
  echo "lastone with ubuntu:latest"
  echo "after docker export and reimport to make a simple layer image"
  
  ```
  
  
  ---
  
  ### Answer by username_4 on 2019-04-09. Score: 3
  
  Here is an adapted python script, thus having an OS independant solution: [docker-drag](https://github.com/NotGlop/docker-drag)
  
  Use it like that, and it will create a TAR archive that you will be able to import using docker load :
  
  ```
  python docker_pull.py hello-world
  python docker_pull.py alpine:3.9
  python docker_pull.py kalilinux/kali-linux-docker
  
  ```
  
  > **Comments:**
  > **username_1**: If you close your github account there's nothing left, if you're ok with sharing it, keep the link but paste the script here also please. You can [edit] your answer, paste your code and then select it and type ctrl+K or the `{}` (code) button in the editor top bar to format it.  
  > **username_4**: I would love to paste the code here but it is 100 lines long and I don't think it will be readable. Nevertheless, you can fork the code to save your own copy of the script.  
  > **username_1**: That's not for me, that to have an answer which is self sustaining, if the link break, do you really think this may help someone reading this answer in a few months ? (btw the maximum size for an answer is 30k characters)  
  > **username_5**: You could put this in pip. I find it very helpful.  
  
  ---
  
  ### Answer by username_8 on 2017-12-03. Score: 1
  
  To me it is not completely clear what you are trying to achieve and why the attempts are not a solution for the problem. If I would need to solve this issue I would like @username_1 and other Q&As indicated, do a docker pull first on a system with internet connectivity, save the docker image, copy it to the machine without internet connectivity, load the image and run it.
  
  **Demonstration**
  
  There are no images on system A:
  
  ```
  userA@systemA ~ $ docker images
  REPOSITORY        TAG               IMAGE ID          CREATED             SIZE
  userA@systemA ~ $
  
  ```
  
  Pull an image from dockerhub:
  
  ```
  userA@systemA ~ $
  docker pull nginx
  Using default tag: latest
  latest: Pulling from library/nginx
  bc95e04b23c0: Pull complete 
  f3186e650f4e: Pull complete 
  9ac7d6621708: Pull complete 
  Digest: sha256:b81f317384d7388708a498555c28a7cce778a8f291d90021208b3eba3fe74887
  Status: Downloaded newer image for nginx:latest
  userA@systemA ~ $ docker images
  REPOSITORY        TAG               IMAGE ID            CREATED             SIZE
  nginx             latest            9e7424e5dbae        10 days ago         108MB
  
  ```
  
  Save docker image:
  
  ```
  userA@systemA ~ $ docker save nginx -o nginx.tar
  
  ```
  
  Copy docker image to systemB and load it.
  
  ```
  userB@systemB ~ $ docker load -i nginx.tar
  cec7521cdf36: Loading layer  58.44MB/58.44MB
  350d50e58b6c: Loading layer  53.76MB/53.76MB
  63c39cd4a775: Loading layer  3.584kB/3.584kB
  Loaded image: nginx:latest
  userB@systemB ~ $ docker images
  REPOSITORY        TAG               IMAGE ID            CREATED             SIZE
  nginx             latest            9e7424e5dbae        10 days ago         108MB
  
  ```
  
  > **Comments:**
  > **username_0**: The machine with internet connectivity does not and cannot have Docker installed. The question is asking for a way to download an image *without* using the Docker client. See [my solution](https://devops.stackexchange.com/a/2772/3070).  
  
  ---
  
  ### Answer by username_9 on 2021-07-22. Score: 1
  
  I didn't really understand [username_3's](https://devops.stackexchange.com/a/6666/28888) way of postprocessing, so I've spent some time to modify his script and here's what I came with:
  
  <https://github.com/meetyourturik/dockerless-docker-downloader>
  
  upd: apparently, 'a link only answer...' is something bad, so here's a whole script:
  
  ```
  # Workaround for SelfSigned Cert an force TLS 1.2
  add-type @"
      using System.Net;
      using System.Security.Cryptography.X509Certificates;
      public class TrustAllCertsPolicy : ICertificatePolicy {
          public bool CheckValidationResult(
          ServicePoint srvPoint, X509Certificate certificate,
          WebRequest request, int certificateProblem) {
              return true;
          }
      }
  "@
  [System.Net.ServicePointManager]::CertificatePolicy = New-Object TrustAllCertsPolicy
  [System.Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
  
  # use 'library/' prefix for 'official' images like postgres 
  $image = "atlassian/jira-software" 
  $tag = "8.13.2" 
  
  $imageuri = "https://auth.docker.io/token?service=registry.docker.io&scope=repository:${image}:pull" 
  $taguri = "https://registry-1.docker.io/v2/${image}/manifests/${tag}"
  $bloburi = "https://registry-1.docker.io/v2/${image}/blobs/" 
  
  # generating folder to save image files 
  $path = "$image$tag" -replace '[\\/":*?<>|]'
  if (!(test-path $path)) { 
      New-Item -ItemType Directory -Force -Path $path 
  } 
  
  # token request 
  $token = Invoke-WebRequest -Uri $imageuri | ConvertFrom-Json | Select -expand token 
  
  # getting image manifest 
  $headers = @{} 
  $headers.add("Authorization", "Bearer $token") 
  # this header is needed to get manifest in correct format: https://docs.docker.com/registry/spec/manifest-v2-2/ 
  $headers.add("Accept", "application/vnd.docker.distribution.manifest.v2+json") 
  $manifest = Invoke-Webrequest -Headers $headers -Method GET -Uri $taguri | ConvertFrom-Json 
  
  # downloading config json 
  $configSha = $manifest | Select -expand config | Select -expand digest 
  $config = ".\$path\config.json" 
  Invoke-Webrequest -Headers @{Authorization="Bearer $token"} -Method GET -Uri $bloburi$configSha -OutFile $config 
  
  # generating manifest.json 
  $manifestJson = @{} 
  $manifestJson.add("Config", "config.json") 
  $manifestJson.add("RepoTags",@("${image}:${tag}")) 
  
  # downloading layers 
  $layers = $manifest | Select -expand layers | Select -expand digest 
  $blobtmp = ".\$path\blobtmp" 
  
  #downloading blobs 
  $layersJson = @() 
  foreach ($blobelement in $layers) { 
      # making so name doesnt start with 'sha256:' 
      $fileName = "$blobelement.gz" -replace 'sha256:' 
      $newfile = ".\$path\$fileName" 
      $layersJson += @($fileName) 
  
      # token expired after 5 minutes, so requesting new one for every blob just in case 
      $token = Invoke-WebRequest -Uri $imageuri | ConvertFrom-Json | Select -expand token 
      
      Invoke-Webrequest -Headers @{Authorization="Bearer $token"} -Method GET -Uri $bloburi$blobelement -OutFile $blobtmp 
      
      Copy-Item $blobtmp $newfile -Force -Recurse 
  } 
  
  # removing temporary blob 
  Remove-Item $blobtmp 
  
  # saving manifest.json 
  $manifestJson.add("Layers", $layersJson) 
  ConvertTo-Json -Depth 5 -InputObject @($manifestJson) | Out-File -Encoding ascii ".\$path\manifest.json" 
  
  # postprocessing
  echo "copy generated folder to your docker machine" 
  echo "tar -cvf imagename.tar *" 
  echo "docker load < imagename.tar"
  
  ```
  
  after sctipt downloads blobs and generates config and manifest jsons download it to docker machine and execute two following commands:
  
  ```
  tar -cvf imagename.tar *
  docker load < imagename.tar
  
  ```
  
  first creates an archive, 2nd uploads image archive to docker
  
  
  ---
  
  ### Answer by username_8 on 2022-03-06. Score: 0
  
  Another tool that could be used to download docker images is [P2IWD](https://github.com/030/p2iwd).
  
  > **Comments:**
  > **username_0**: There doesn’t appear to be any code in that repository.  
  > **username_8**: @username_0 version 0.1.0 has been released. It is capable to download docker images from a Nexus3 server and to upload them to another one.  
  
  ---
  
  ### Answer by username_10 on 2024-09-03. Score: 0
  
  You can manually download a docker image with curl by:
  
  1. getting a free auth token
  2. download the manifest
  3. download the layers (blob hashes specified in the manifest)
  
  # Example
  
  For example, I'll show how you can download [the `hitch` package](https://hub.docker.com/_/hitch) from hub.docker.com.
  
  ## Get an Auth Token
  
  Execute the following command to get an [authentication token](https://distribution.github.io/distribution/spec/auth/oauth/) from Docker Hub within the scope of the '`hitch`' package's namespace.
  
  ```
  # get a JSON with an anonymous token
  curl -so "token.json" "https://auth.docker.io/token?service=registry.docker.io&scope=<resourcetype>:<component>/<component>:<action>";
  
  # extract token from JSON
  token=$(cat token.json | jq -jr ".token")
  
  ```
  
  > ⓘ INFO: If you're like me and wondering where the heck is the OCI spec that defines this `/token` endpoint for authentication, know that [it doesn't exist](https://github.com/opencontainers/distribution-spec/issues/240)
  >
  > To learn more about the syntax of this URL and '`scope`' GET variable, see docker's [Token Scope Documentation](https://distribution.github.io/distribution/spec/auth/scope/).
  
  The above commands will get a free/temporary token that you can use in subsequent API calls. If all went well, there will be no output from these commands. Here's an example execution
  
  ```
  user@disp7456:~$ curl -so "token.json" "https://auth.docker.io/token?service=registry.docker.io&scope=repository:library/hitch:pull";
  user@disp7456:~$ 
  
  user@disp7456:~$ token=$(cat token.json | jq -jr ".token")
  user@disp7456:~$ 
  
  ```
  
  ## Download the Manifest
  
  We can download the manifest for the '`1.8.0-1`' tag of the '`hitch`' package with the '`GET /v2/<name>/manifests/<reference>`' API endpoint.
  
  ```
  curl -o manifest.json -s -H "Authorization: Bearer ${token}" https://registry-1.docker.io/v2/library/<package_name>/manifests/<tag>
  
  ```
  
  And here's an example execution that downloads the manifest for the '`1.8.0-1`' tag of the '`hitch`' package
  
  ```
  
  user@disp7456:~$ curl -o manifest.json -s -H "Authorization: Bearer ${token}" https://registry-1.docker.io/v2/library/hitch/manifests/1.8.0-1
  user@disp7456:~$ 
  
  user@disp7456:~$ ls
  manifest.json
  user@disp7456:~$ 
  
  ```
  
  ## Parse the Manifest
  
  The previous step downloaded a file named '`manifest.json`'. This '`manifest.json`' file lists all of the "layers" that make up the image of the '`hitch`' package.
  
  Each "layer" consists of a tarball and some metadata about the layer in json format. The information that we need to download the layer's tar file is located in the '`manifest.json`' file. And the metadata about each layer is also in the '`manifest.json`' file.
  
  The format of the '`manifest.json`' file is '`vnd.docker.distribution.manifest.v1+json`', which is defined in [Image Manifest Version 2, Schema 1](https://github.com/distribution/distribution/blob/bf9f80eaffb0eabc768762bc4ff03ded277ea594/docs/spec/manifest-v2-1.md).
  
  Most importantly, the '`manifest.json`' file contains two [parallel arrays](https://en.wikipedia.org/wiki/Parallel_array) of the same length:
  
  1. fsLayers[]
  2. history[]
  
  Consider this truncated snippet of the manifest for the '`hitch`' package's '`1.8.0-1`' tag:
  
  ```
  {
     "schemaVersion": 1,
     "name": "library/hitch",
     "tag": "1.8.0-1",
     "architecture": "amd64",
     "fsLayers": [
        {
           "blobSum": "sha256:a3ed95caeb02ffe68cdd9fd84406680ae93d633cb16422d00e8a7c22955b46d4"
        },
        {
           "blobSum": "sha256:a3ed95caeb02ffe68cdd9fd84406680ae93d633cb16422d00e8a7c22955b46d4"
        },
        {
           "blobSum": "sha256:a3ed95caeb02ffe68cdd9fd84406680ae93d633cb16422d00e8a7c22955b46d4"
        },
        {
           "blobSum": "sha256:3148f4af0a813bcff0a3ed2562aabfb1b596b52ef36eb5eb4d82ce836350b73a"
        },
        {
           "blobSum": "sha256:a3ed95caeb02ffe68cdd9fd84406680ae93d633cb16422d00e8a7c22955b46d4"
        },
        {
           "blobSum": "sha256:a0e9543db8c1238572466cf00b55436bc7b7e849f7cb305128f391a94b75c2fc"
        },
        {
           "blobSum": "sha256:a3ed95caeb02ffe68cdd9fd84406680ae93d633cb16422d00e8a7c22955b46d4"
        },
        {
           "blobSum": "sha256:a3ed95caeb02ffe68cdd9fd84406680ae93d633cb16422d00e8a7c22955b46d4"
        },
        {
           "blobSum": "sha256:a3ed95caeb02ffe68cdd9fd84406680ae93d633cb16422d00e8a7c22955b46d4"
        },
        {
           "blobSum": "sha256:a3ed95caeb02ffe68cdd9fd84406680ae93d633cb16422d00e8a7c22955b46d4"
        },
        {
           "blobSum": "sha256:a3ed95caeb02ffe68cdd9fd84406680ae93d633cb16422d00e8a7c22955b46d4"
        },
        {
           "blobSum": "sha256:a3ed95caeb02ffe68cdd9fd84406680ae93d633cb16422d00e8a7c22955b46d4"
        },
        {
           "blobSum": "sha256:728328ac3bde9b85225b1f0d60f5c149f5635a191f5d8eaeeb00e095d36ef9fd"
        }
     ],
     "history": [
        {
           "v1Compatibility": "{\"architecture\":\"amd64\",\"config\":{\"Hostname\":\"\",\"Domainname\":\"\",\"User\":\"\",\"AttachStdin\":false,\"AttachStdout\":false,\"AttachStderr\":false,\"ExposedPorts\":{\"443/tcp\":{}},\"Tty\":false,\"OpenStdin\":false,\"StdinOnce\":false,\"Env\":[\"PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin\"],\"Cmd\":[],\"Image\":\"sha256:996009c7d7eb032c9ea750e5decc1f8aedbf4530b892cf4ebc7716e1458f36d9\",\"Volumes\":null,\"WorkingDir\":\"/etc/hitch\",\"Entrypoint\":[\"docker-hitch-entrypoint\"],\"OnBuild\":null,\"Labels\":null},\"container\":\"0ff54ee96c4bbfe77da3b2124720ef95c6154d3bc1d3e40a168920dd818367c4\",\"container_config\":{\"Hostname\":\"0ff54ee96c4b\",\"Domainname\":\"\",\"User\":\"\",\"AttachStdin\":false,\"AttachStdout\":false,\"AttachStderr\":false,\"ExposedPorts\":{\"443/tcp\":{}},\"Tty\":false,\"OpenStdin\":false,\"StdinOnce\":false,\"Env\":[\"PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin\"],\"Cmd\":[\"/bin/sh\",\"-c\",\"#(nop) \",\"CMD []\"],\"Image\":\"sha256:996009c7d7eb032c9ea750e5decc1f8aedbf4530b892cf4ebc7716e1458f36d9\",\"Volumes\":null,\"WorkingDir\":\"/etc/hitch\",\"Entrypoint\":[\"docker-hitch-entrypoint\"],\"OnBuild\":null,\"Labels\":{}},\"created\":\"2024-05-14T05:23:11.666992342Z\",\"docker_version\":\"20.10.23\",\"id\":\"6703605aae83084affcafb4abcc7c556f0e436c4992ae224f1f58e88242328cb\",\"os\":\"linux\",\"parent\":\"c48ca3d95161bbcdfcaa2e016a675965d55f4f06147ef4445c69347c5965f188\",\"throwaway\":true}"
        },
        {
           "v1Compatibility": "{\"id\":\"c48ca3d95161bbcdfcaa2e016a675965d55f4f06147ef4445c69347c5965f188\",\"parent\":\"1d2af5a156bbc461d98824c2f6bfe295327d4419105c0b7f88f14cb28d0bb240\",\"created\":\"2024-05-14T05:23:11.581588417Z\",\"container_config\":{\"Cmd\":[\"/bin/sh -c #(nop)  EXPOSE 443\"]},\"throwaway\":true}"
        },
        {
           "v1Compatibility": "{\"id\":\"1d2af5a156bbc461d98824c2f6bfe295327d4419105c0b7f88f14cb28d0bb240\",\"parent\":\"8f914c821cbe154cc6677bceb043669e4295ad5cfb6409efa9c2ec1beba75fad\",\"created\":\"2024-05-14T05:23:11.489285564Z\",\"container_config\":{\"Cmd\":[\"/bin/sh -c #(nop)  ENTRYPOINT [\\\"docker-hitch-entrypoint\\\"]\"]},\"throwaway\":true}"
        },
        {
           "v1Compatibility": "{\"id\":\"8f914c821cbe154cc6677bceb043669e4295ad5cfb6409efa9c2ec1beba75fad\",\"parent\":\"a8d8314458142ee2a4ebccb19f48b6f9c696100103c3d49cbbe7ecd2575120e5\",\"created\":\"2024-05-14T05:23:11.403178706Z\",\"container_config\":{\"Cmd\":[\"/bin/sh -c #(nop) COPY file:1abf3c94dce5dc9f6617dc8d36a6fe6f4f7236189d4819f16cefb54288e80e0d in /usr/local/bin/ \"]}}"
        },
        {
           "v1Compatibility": "{\"id\":\"a8d8314458142ee2a4ebccb19f48b6f9c696100103c3d49cbbe7ecd2575120e5\",\"parent\":\"5a78b0e89bbae2390b83e60174ae1efc583f766eff7dffaffa747ccb67472d0f\",\"created\":\"2024-05-14T05:23:11.304477182Z\",\"container_config\":{\"Cmd\":[\"/bin/sh -c #(nop) WORKDIR /etc/hitch\"]},\"throwaway\":true}"
        },
        {
           "v1Compatibility": "{\"id\":\"5a78b0e89bbae2390b83e60174ae1efc583f766eff7dffaffa747ccb67472d0f\",\"parent\":\"5a12a2c67ff9b5bfad288a4ede18d08c259c301efb85403d08a40ea2ad0eb1f8\",\"created\":\"2024-05-14T05:23:11.160227264Z\",\"container_config\":{\"Cmd\":[\"|5 DISTVER=bullseye PKGCOMMIT=f12ab7958bc4885f3f00311cbca5103d9e6ba794 PKGVER=1 SHASUM=62b3554d668c9d17382415db10898bf661ee76343e4ee364f904457efda6cb1eeee7cb81d7a3897734024812b64b1c0e2dc305605706d81a0c1f6030508bf7e2 SRCVER=1.8.0 /bin/sh -c set -ex;     BASE_PKGS=\\\"apt-utils curl dirmngr dpkg-dev debhelper devscripts equivs fakeroot git gnupg pkg-config\\\";     export DEBIAN_FRONTEND=noninteractive;     export DEBCONF_NONINTERACTIVE_SEEN=true;     tmpdir=\\\"$(mktemp -d)\\\";     cd \\\"$tmpdir\\\";     apt-get update;     apt-get install -y --no-install-recommends $BASE_PKGS;     git clone https://github.com/varnish/pkg-hitch.git;     cd pkg-hitch;     git checkout ${PKGCOMMIT};     rm -rf .git;     curl -Lf https://hitch-tls.org/source/hitch-${SRCVER}.tar.gz -o $tmpdir/orig.tgz;     echo \\\"${SHASUM}  $tmpdir/orig.tgz\\\" | sha512sum -c -;     tar xavf $tmpdir/orig.tgz --strip 1;     sed -i         -e \\\"s/@SRCVER@/${SRCVER}/g\\\"         -e \\\"s/@PKGVER@/${PKGVER:-1}/g\\\"         -e \\\"s/@DISTVER@/$DISTVER/g\\\" debian/changelog;     mk-build-deps --install --tool=\\\"apt-get -o Debug::pkgProblemResolver=yes --yes\\\" debian/control;     sed -i '' debian/hitch*;     dpkg-buildpackage -us -uc -j\\\"$(nproc)\\\";     apt-get -y purge --auto-remove hitch-build-deps $BASE_PKGS;     apt-get -y --no-install-recommends install ../*.deb;     sed -i 's/daemon = on/daemon = off/' /etc/hitch/hitch.conf;     rm -rf /var/lib/apt/lists/* \\\"$tmpdir\\\"\"]}}"
        },
        {
           "v1Compatibility": "{\"id\":\"5a12a2c67ff9b5bfad288a4ede18d08c259c301efb85403d08a40ea2ad0eb1f8\",\"parent\":\"c03ad9230005f64133de4501e14a882ef25f03443da4da55ca002d5619f998be\",\"created\":\"2024-05-14T05:21:33.061082853Z\",\"container_config\":{\"Cmd\":[\"/bin/sh -c #(nop)  ARG SHASUM=62b3554d668c9d17382415db10898bf661ee76343e4ee364f904457efda6cb1eeee7cb81d7a3897734024812b64b1c0e2dc305605706d81a0c1f6030508bf7e2\"]},\"throwaway\":true}"
        },
        {
           "v1Compatibility": "{\"id\":\"c03ad9230005f64133de4501e14a882ef25f03443da4da55ca002d5619f998be\",\"parent\":\"24e7aee556d6a38bfa2e13430db8a998c023a2920017eabc0b3bf0dd7661bf7d\",\"created\":\"2024-05-14T05:21:32.967727298Z\",\"container_config\":{\"Cmd\":[\"/bin/sh -c #(nop)  ARG PKGCOMMIT=f12ab7958bc4885f3f00311cbca5103d9e6ba794\"]},\"throwaway\":true}"
        },
        {
           "v1Compatibility": "{\"id\":\"24e7aee556d6a38bfa2e13430db8a998c023a2920017eabc0b3bf0dd7661bf7d\",\"parent\":\"f0d07a99d7d1f0b849a4cbe8fc4552d374f4448c2e7f8bfd908aa43132c4ec34\",\"created\":\"2024-05-14T05:21:32.875807605Z\",\"container_config\":{\"Cmd\":[\"/bin/sh -c #(nop)  ARG DISTVER=bullseye\"]},\"throwaway\":true}"
        },
        {
           "v1Compatibility": "{\"id\":\"f0d07a99d7d1f0b849a4cbe8fc4552d374f4448c2e7f8bfd908aa43132c4ec34\",\"parent\":\"65c7b6d17437bf7a3216e2fea283071e9b5c0d71c6b97472baa8807a30b5d9d8\",\"created\":\"2024-05-14T05:21:32.781941821Z\",\"container_config\":{\"Cmd\":[\"/bin/sh -c #(nop)  ARG PKGVER=1\"]},\"throwaway\":true}"
        },
        {
           "v1Compatibility": "{\"id\":\"65c7b6d17437bf7a3216e2fea283071e9b5c0d71c6b97472baa8807a30b5d9d8\",\"parent\":\"863a608d086b1bcf7f9b30ccf57260e6cb5d3d793b4e1131aa8f6041b07a7270\",\"created\":\"2024-05-14T05:21:32.682503634Z\",\"container_config\":{\"Cmd\":[\"/bin/sh -c #(nop)  ARG SRCVER=1.8.0\"]},\"throwaway\":true}"
        },
        {
           "v1Compatibility": "{\"id\":\"863a608d086b1bcf7f9b30ccf57260e6cb5d3d793b4e1131aa8f6041b07a7270\",\"parent\":\"e00e363f3a25341591a5a5e724e20ae3e70f0396be8483a07c0b39d25d33fecd\",\"created\":\"2024-05-14T01:28:27.043980081Z\",\"container_config\":{\"Cmd\":[\"/bin/sh -c #(nop)  CMD [\\\"bash\\\"]\"]},\"throwaway\":true}"
        },
        {
           "v1Compatibility": "{\"id\":\"e00e363f3a25341591a5a5e724e20ae3e70f0396be8483a07c0b39d25d33fecd\",\"created\":\"2024-05-14T01:28:26.699066026Z\",\"container_config\":{\"Cmd\":[\"/bin/sh -c #(nop) ADD file:9b38b383dd93169a663eed88edf3f2285b837257ead69dc40ab5ed1fb3f52c35 in / \"]}}"
        }
     ],
     ...
     ]
  }
  
  ```
  
  The sha256sum used to download the blob of the **first layer** is found at the first element of the `fsLayers[]` array (**`fsLayers[0]`**). The metadata about this first layer is found at the first element of the `history[]` array (**`history[0]`**).
  
  The sha256sum used to download the blob of the **second layer** is found at second element of the `fsLayers[]` array (**`fsLayers[1]`**). The metadata about this second layer is found at the second element of the `history[]` array (**`history[1]`**).
  
  Et cetera...
  
  ## Download the Layers
  
  So how do we download each of these layers separately, yet organize them such that we can later import them as a single image into docker? The answer to that lies in the [Docker Image Specification v1.0.0](https://github.com/moby/moby/blob/79910625f0a7aa76590e4362817dda22f28343aa/image/spec/v1.md).
  
  The above spec provides an example tree of the files:
  
  > For example, here's what the full archive of `library/busybox` is (displayed in `tree` format):
  >
  > `.`  
  > `├── 5785b62b697b99a5af6cd5d0aabc804d5748abbb6d3d07da5d1d3795f2dcc83e`  
  > `│ ├── VERSION`  
  > `│ ├── json`  
  > `│ └── layer.tar`  
  > `├── a7b8b41220991bfc754d7ad445ad27b7f272ab8b4a2c175b9512b97471d02a8a`  
  > `│ ├── VERSION`  
  > `│ ├── json`  
  > `│ └── layer.tar`  
  > `├── a936027c5ca8bf8f517923169a233e391cbb38469a75de8383b5228dc2d26ceb`  
  > `│ ├── VERSION`  
  > `│ ├── json`  
  > `│ └── layer.tar`  
  > `├── f60c56784b832dd990022afc120b8136ab3da9528094752ae13fe63a2d28dc8c`  
  > `│ ├── VERSION`  
  > `│ ├── json`  
  > `│ └── layer.tar`  
  > `└── repositories`\
  >
  > There are one or more directories named with the ID for each layer in a full image. Each of these directories contains 3 files:
  >
  > * `VERSION` - The schema version of the `json` file
  > * `json` - The JSON metadata for an image layer
  > * `layer.tar` - The Tar archive of the filesystem changeset for an image  
  >   layer.
  >
  > The content of the `VERSION` files is simply the semantic version of the JSON metadata schema:  
  >   
  > `1.0`\
  >
  > And the `repositories` file is another JSON file which describes names/tags:
  >
  > ```
  > {  
  >     "busybox":{  
  >         "latest":"5785b62b697b99a5af6cd5d0aabc804d5748abbb6d3d07da5d1d3795f2dcc83e"
  >     }
  > }
  >
  > ```
  >
  > Every key in this object is the name of a repository, and maps to a collection of tag suffixes. Each tag maps to the ID of the image represented by that tag.
  
  So, as shown in the quote from the spec above, what we need to do is to create a set of directories -- one for each layer -- named after the layer's ID. Each of these layer-specific directories must contain 3 files:
  
  1. The actual tarball of the layer (named '`layer.tar`'),
  2. The metadata of the layer (in a file literally named '`json`' -- with no file extension)
  3. A file named '`VERSION`' whose contents is literally just '`1.0`'
  
  All of the information that we need is in the '`manifest.json`' file that we already downloaded. Let's just loop through each layer that it defines, download the `layer.tar` tarball, create the '`json`' metadata file, and hard-code the '`VERSION`' file with the following BASH snippet
  
  ```
  num_layers=$(cat manifest.json | jq -r ".history | length")
  
  for ((i = 0 ; i < $num_layers ; i++)); do
      layer_blobSum=$(cat manifest.json | jq -r ".fsLayers[$i].blobSum")
      layer_metadata=$(cat manifest.json | jq -r ".history[$i].v1Compatibility")
      layer_id=$(echo $layer_metadata | jq -r ".id")
  
      echo $layer_id
      echo $layer_blobSum
  
      mkdir -p "layers/$layer_id"
      echo "1.0" > "layers/$layer_id/VERSION"
      echo $layer_metadata > "layers/$layer_id/json"
      curl -o "layers/$layer_id/layer.tar" -#LH "Authorization: Bearer ${token}" "https://registry-1.docker.io/v2/library/<package_name>/blobs/${layer_blobSum}"
  done
  
  ```
  
  And here's an example execution that executes the above snippet to download all of the layers onto disk in a set of directories as defined by the [Docker Image Specification v1.0.0](https://github.com/moby/moby/blob/79910625f0a7aa76590e4362817dda22f28343aa/image/spec/v1.md).
  
  ```
  user@disp7456:~$ num_layers=$(cat manifest.json | jq -r ".history | length")
  user@disp7456:~$ 
  
  user@disp7456:~$ for ((i = 0 ; i < $num_layers ; i++)); do
      layer_blobSum=$(cat manifest.json | jq -r ".fsLayers[$i].blobSum")
      layer_metadata=$(cat manifest.json | jq -r ".history[$i].v1Compatibility")
      layer_id=$(echo $layer_metadata | jq -r ".id")
  
      echo $layer_id
      echo $layer_blobSum
  
      mkdir -p "layers/$layer_id"
      echo "1.0" > "layers/$layer_id/VERSION"
      echo $layer_metadata > "layers/$layer_id/json"
      curl -o "layers/$layer_id/layer.tar" -#LH "Authorization: Bearer ${token}" "https://registry-1.docker.io/v2/library/hitch/blobs/${layer_blobSum}"
  done
  6703605aae83084affcafb4abcc7c556f0e436c4992ae224f1f58e88242328cb
  sha256:a3ed95caeb02ffe68cdd9fd84406680ae93d633cb16422d00e8a7c22955b46d4
  ################################################################ 100.0%
  c48ca3d95161bbcdfcaa2e016a675965d55f4f06147ef4445c69347c5965f188
  sha256:a3ed95caeb02ffe68cdd9fd84406680ae93d633cb16422d00e8a7c22955b46d4
  ################################################################ 100.0%
  1d2af5a156bbc461d98824c2f6bfe295327d4419105c0b7f88f14cb28d0bb240
  sha256:a3ed95caeb02ffe68cdd9fd84406680ae93d633cb16422d00e8a7c22955b46d4
  ################################################################ 100.0%
  8f914c821cbe154cc6677bceb043669e4295ad5cfb6409efa9c2ec1beba75fad
  sha256:3148f4af0a813bcff0a3ed2562aabfb1b596b52ef36eb5eb4d82ce836350b73a
  ################################################################ 100.0%
  a8d8314458142ee2a4ebccb19f48b6f9c696100103c3d49cbbe7ecd2575120e5
  sha256:a3ed95caeb02ffe68cdd9fd84406680ae93d633cb16422d00e8a7c22955b46d4
  ################################################################ 100.0%
  5a78b0e89bbae2390b83e60174ae1efc583f766eff7dffaffa747ccb67472d0f
  sha256:a0e9543db8c1238572466cf00b55436bc7b7e849f7cb305128f391a94b75c2fc
  ################################################################ 100.0%
  5a12a2c67ff9b5bfad288a4ede18d08c259c301efb85403d08a40ea2ad0eb1f8
  sha256:a3ed95caeb02ffe68cdd9fd84406680ae93d633cb16422d00e8a7c22955b46d4
  ################################################################ 100.0%
  c03ad9230005f64133de4501e14a882ef25f03443da4da55ca002d5619f998be
  sha256:a3ed95caeb02ffe68cdd9fd84406680ae93d633cb16422d00e8a7c22955b46d4
  ################################################################ 100.0%
  24e7aee556d6a38bfa2e13430db8a998c023a2920017eabc0b3bf0dd7661bf7d
  sha256:a3ed95caeb02ffe68cdd9fd84406680ae93d633cb16422d00e8a7c22955b46d4
  ################################################################ 100.0%
  f0d07a99d7d1f0b849a4cbe8fc4552d374f4448c2e7f8bfd908aa43132c4ec34
  sha256:a3ed95caeb02ffe68cdd9fd84406680ae93d633cb16422d00e8a7c22955b46d4
  ################################################################ 100.0%
  65c7b6d17437bf7a3216e2fea283071e9b5c0d71c6b97472baa8807a30b5d9d8
  sha256:a3ed95caeb02ffe68cdd9fd84406680ae93d633cb16422d00e8a7c22955b46d4
  ################################################################ 100.0%
  863a608d086b1bcf7f9b30ccf57260e6cb5d3d793b4e1131aa8f6041b07a7270
  sha256:a3ed95caeb02ffe68cdd9fd84406680ae93d633cb16422d00e8a7c22955b46d4
  ################################################################ 100.0%
  e00e363f3a25341591a5a5e724e20ae3e70f0396be8483a07c0b39d25d33fecd
  sha256:728328ac3bde9b85225b1f0d60f5c149f5635a191f5d8eaeeb00e095d36ef9fd
  ################################################################ 100.0%
  user@disp7456:~$ 
  
  user@disp7456:~$ tree layers/
  layers/
  ├── 1d2af5a156bbc461d98824c2f6bfe295327d4419105c0b7f88f14cb28d0bb240
  │   ├── json
  │   ├── layer.tar
  │   └── VERSION
  ├── 24e7aee556d6a38bfa2e13430db8a998c023a2920017eabc0b3bf0dd7661bf7d
  │   ├── json
  │   ├── layer.tar
  │   └── VERSION
  ├── 5a12a2c67ff9b5bfad288a4ede18d08c259c301efb85403d08a40ea2ad0eb1f8
  │   ├── json
  │   ├── layer.tar
  │   └── VERSION
  ├── 5a78b0e89bbae2390b83e60174ae1efc583f766eff7dffaffa747ccb67472d0f
  │   ├── json
  │   ├── layer.tar
  │   └── VERSION
  ├── 65c7b6d17437bf7a3216e2fea283071e9b5c0d71c6b97472baa8807a30b5d9d8
  │   ├── json
  │   ├── layer.tar
  │   └── VERSION
  ├── 6703605aae83084affcafb4abcc7c556f0e436c4992ae224f1f58e88242328cb
  │   ├── json
  │   ├── layer.tar
  │   └── VERSION
  ├── 863a608d086b1bcf7f9b30ccf57260e6cb5d3d793b4e1131aa8f6041b07a7270
  │   ├── json
  │   ├── layer.tar
  │   └── VERSION
  ├── 8f914c821cbe154cc6677bceb043669e4295ad5cfb6409efa9c2ec1beba75fad
  │   ├── json
  │   ├── layer.tar
  │   └── VERSION
  ├── a8d8314458142ee2a4ebccb19f48b6f9c696100103c3d49cbbe7ecd2575120e5
  │   ├── json
  │   ├── layer.tar
  │   └── VERSION
  ├── c03ad9230005f64133de4501e14a882ef25f03443da4da55ca002d5619f998be
  │   ├── json
  │   ├── layer.tar
  │   └── VERSION
  ├── c48ca3d95161bbcdfcaa2e016a675965d55f4f06147ef4445c69347c5965f188
  │   ├── json
  │   ├── layer.tar
  │   └── VERSION
  ├── e00e363f3a25341591a5a5e724e20ae3e70f0396be8483a07c0b39d25d33fecd
  │   ├── json
  │   ├── layer.tar
  │   └── VERSION
  └── f0d07a99d7d1f0b849a4cbe8fc4552d374f4448c2e7f8bfd908aa43132c4ec34
      ├── json
      ├── layer.tar
      └── VERSION
  
  14 directories, 39 files
  user@disp7456:~$ 
  
  ```
  
  Finally, besides these layer-specific dirs, we need one additional file (named simply '`repository`', with no file extension) at the same height as these dirs. As defined by the [Docker Image Specification v1.0.0](https://github.com/moby/moby/blob/79910625f0a7aa76590e4362817dda22f28343aa/image/spec/v1.md), this file should state the name & tag of the image, and it points to the first layer of the image.
  
  Note the `0th` item in the `history[]` array is the first layer of the image, so we can create this file with the following command
  
  ```
  start_image=$(cat manifest.json | jq -r ".history[0].v1Compatibility")
  start_image_id=$(echo $start_image | jq -r ".id")
  
  cat > layers/repositories <<EOF
  {
      "<image_name>": { "stable": "$start_image_id" }
  }
  EOF
  
  ```
  
  And here's an example execution to create our '`repository`' file for the '`hitch`' package.
  
  ```
  user@disp7456:~$ start_image=$(cat manifest.json | jq -r ".history[0].v1Compatibility")
  user@disp7456:~$ 
  
  user@disp7456:~$ start_image_id=$(echo $start_image | jq -r ".id")
  user@disp7456:~$ 
  
  user@disp7456:~$ cat > layers/repositories <<EOF
  {
      "hitch": { "stable": "$start_image_id" }
  }
  EOF
  user@disp7456:~$
  
  user@disp7456:~$ ls layers
  1d2af5a156bbc461d98824c2f6bfe295327d4419105c0b7f88f14cb28d0bb240
  24e7aee556d6a38bfa2e13430db8a998c023a2920017eabc0b3bf0dd7661bf7d
  5a12a2c67ff9b5bfad288a4ede18d08c259c301efb85403d08a40ea2ad0eb1f8
  5a78b0e89bbae2390b83e60174ae1efc583f766eff7dffaffa747ccb67472d0f
  65c7b6d17437bf7a3216e2fea283071e9b5c0d71c6b97472baa8807a30b5d9d8
  6703605aae83084affcafb4abcc7c556f0e436c4992ae224f1f58e88242328cb
  863a608d086b1bcf7f9b30ccf57260e6cb5d3d793b4e1131aa8f6041b07a7270
  8f914c821cbe154cc6677bceb043669e4295ad5cfb6409efa9c2ec1beba75fad
  a8d8314458142ee2a4ebccb19f48b6f9c696100103c3d49cbbe7ecd2575120e5
  c03ad9230005f64133de4501e14a882ef25f03443da4da55ca002d5619f998be
  c48ca3d95161bbcdfcaa2e016a675965d55f4f06147ef4445c69347c5965f188
  e00e363f3a25341591a5a5e724e20ae3e70f0396be8483a07c0b39d25d33fecd
  f0d07a99d7d1f0b849a4cbe8fc4552d374f4448c2e7f8bfd908aa43132c4ec34
  repositories
  user@disp7456:~$
  
  user@disp7456:~$ cat layers/repositories
  {
      "hitch": { "stable": "6703605aae83084affcafb4abcc7c556f0e436c4992ae224f1f58e88242328cb" }
  }
  user@disp7456:~$ 
  
  ```
  
  Your '`layers/`' directory should now be prepared-to-spec for importing the entire image into docker.
  
  For reference, here's the contents of just one of the layers:
  
  ```
  user@disp7456:~$ ls layers/8f914c821cbe154cc6677bceb043669e4295ad5cfb6409efa9c2ec1beba75fad/
  json  layer.tar  VERSION
  user@disp7456:~$ 
  
  user@disp7456:~$ cat layers/8f914c821cbe154cc6677bceb043669e4295ad5cfb6409efa9c2ec1beba75fad/VERSION 
  1.0
  user@disp7456:~$ 
  
  user@disp7456:~$ cat layers/8f914c821cbe154cc6677bceb043669e4295ad5cfb6409efa9c2ec1beba75fad/json 
  {"id":"8f914c821cbe154cc6677bceb043669e4295ad5cfb6409efa9c2ec1beba75fad","parent":"a8d8314458142ee2a4ebccb19f48b6f9c696100103c3d49cbbe7ecd2575120e5","created":"2024-05-14T05:23:11.403178706Z","container_config":{"Cmd":["/bin/sh -c #(nop) COPY file:1abf3c94dce5dc9f6617dc8d36a6fe6f4f7236189d4819f16cefb54288e80e0d in /usr/local/bin/ "]}}
  user@disp7456:~$ 
  
  user@disp7456:~$ sha256sum layers/8f914c821cbe154cc6677bceb043669e4295ad5cfb6409efa9c2ec1beba75fad/layer.tar 
  3148f4af0a813bcff0a3ed2562aabfb1b596b52ef36eb5eb4d82ce836350b73a  layers/8f914c821cbe154cc6677bceb043669e4295ad5cfb6409efa9c2ec1beba75fad/layer.tar
  user@disp7456:~$ 
  
  user@disp7456:~$ tar -tvf layers/8f914c821cbe154cc6677bceb043669e4295ad5cfb6409efa9c2ec1beba75fad/layer.tar 
  drwxr-xr-x 0/0               0 2024-05-12 19:00 usr/
  drwxr-xr-x 0/0               0 2024-05-12 19:00 usr/local/
  drwxr-xr-x 0/0               0 2024-05-14 00:23 usr/local/bin/
  -rwxrwxr-x 0/0             319 2024-05-14 00:21 usr/local/bin/docker-hitch-entrypoint
  user@disp7456:~$ 
  
  ```
  
  ## Load the Image
  
  Finally, you can copy the whole `layers` directory into your restricted environment and then load the layers as one image into docker with `docker image load`
  
  ```
  tar -cC layers . | docker image load
  
  ```
  
  Here's an example execution
  
  ```
  user@disp7456:~$ docker image ls
  REPOSITORY   TAG       IMAGE ID   CREATED   SIZE
  user@disp7456:~$ 
  
  user@disp7456:~$ tar -cC layers . | docker load
  e00e363f3a25: Loading layer [==================================================>]  31.43MB/31.43MB
  863a608d086b: Loading layer [==================================================>]      32B/32B
  65c7b6d17437: Loading layer [==================================================>]      32B/32B
  f0d07a99d7d1: Loading layer [==================================================>]      32B/32B
  24e7aee556d6: Loading layer [==================================================>]      32B/32B
  c03ad9230005: Loading layer [==================================================>]      32B/32B
  5a12a2c67ff9: Loading layer [==================================================>]      32B/32B
  5a78b0e89bba: Loading layer [==================================================>]  1.573MB/1.573MB
  a8d831445814: Loading layer [==================================================>]      32B/32B
  8f914c821cbe: Loading layer [==================================================>]     415B/415B
  1d2af5a156bb: Loading layer [==================================================>]      32B/32B
  c48ca3d95161: Loading layer [==================================================>]      32B/32B
  6703605aae83: Loading layer [==================================================>]      32B/32B
  user@disp7456:~$ 
  
  user@disp7456:~$ docker image ls
  REPOSITORY   TAG       IMAGE ID       CREATED       SIZE
  hitch        stable    f07eadb841be   3 weeks ago   85.1MB
  user@disp7456:~$ 
  
  ```
  
  # Attribution
  
  The example above was copied from [Manually Downloading Container Images (Docker, Github Packages)](https://tech.username_10.net/2024/09/03/container-download-curl-wget/)
  
  
  ---
  
  ### Answer by username_11 on 2020-02-10. Score: -1
  
  If your company has nexus or a similar repository, they may already have set up nexus to pull in what you need. You could try adding your repo's host/ip in front of the relative docker hub path.
  
  Examples:
  
  ```
  docker pull nexus.example.com:18443/nginx
  docker pull nexus.example.com:18443/selenium/node-chrome
  docker pull nexus.example.com:18443/postgres:9.4
  
  ```
  
  
  ---
  
  