CertGrid CertGrid
Hands-on Lab·Docker

Moving Docker Images Without a Registry

Registries are the normal path, but not always an available one. Save an image to a file, delete it, load it back, and prove the image ID is identical - plus why export and import are a different thing that loses your metadata.

Dockerfiles and Builds Guide 22 of 46 Intermediate

Tested on the versions above. Image IDs differ for every build. The point is that the ID is the SAME before and after the round trip.

One Docker host is all this guide needs. Nothing here depends on a second machine, and the hardware above is modest on purpose - a 2 core, 4 GB VM runs everything in this path.
Server NameIP AddressOSRolesCPURAMHDD
DOCKER01192.168.0.21Ubuntu 26.04 LTSDocker Host2 Core4 GB50 GB

Before you start

  1. When you need this

    An air-gapped environment, a customer site with no outbound access, a machine behind a proxy that blocks the registry, or simply moving a large image between two machines on the same desk faster than a round trip through the internet. docker save writes an image and all its layers to a tar stream.

    bash
    docker save cg-bm:secret | gzip > img.tgz# no output on success - the archive is on disk# save writes to stdout, so always redirect or use -o

    Expected resultAn archive file, silence on success.

    Success conditionThe file exists. Forgetting the redirect dumps binary into your terminal - use -o img.tar if you prefer an explicit flag.

  2. Record the identity before you destroy it

    The whole point of the exercise is proving the round trip is lossless, so capture the image ID first. This is the content hash of the image configuration - if it matches afterwards, nothing was altered.

    bash Example session
    docker image inspect cg-bm:secret --format "before={{.Id}}"before=sha256:a0d196f076398eb091a579d4200fbb077d7d7e87b5f6198307c3bccb457aa0f9

    Expected resultA sha256 image ID.

    Success conditionYou have something to compare against. Do not skip this - a backup you have not verified is not a backup.

  3. Delete it, then load it back

    Remove the image entirely so the load has to do real work, then feed the archive back in. docker load restores the image with its tags intact, which is the difference from import.

    bash Example session
    # removes the local image - the archive is now the only copydocker rmi cg-bm:secret && docker images -q cg-bm:secret | wc -l0gunzip -c img.tgz | docker loadLoaded image: cg-bm:secret

    Expected resultThe image gone, then restored by name.

    Success conditionLoaded image: reports the original tag. The tag came from the archive - you did not have to re-tag anything.

  4. Prove nothing changed

    Same image ID, so the layers and configuration are byte-identical to what you saved. This is the check that turns the procedure into a guarantee.

    bash Example session
    docker image inspect cg-bm:secret --format "after={{.Id}}"after=sha256:a0d196f076398eb091a579d4200fbb077d7d7e87b5f6198307c3bccb457aa0f9

    Expected resultThe same sha256 you recorded before.

    Success conditionIdentical IDs. A different ID would mean the image you loaded is not the image you saved.

  5. save and load are not export and import

    Four commands, two pairs, and mixing them up is a common mistake. save/load work on IMAGES and preserve layers, history, tags and configuration. export/import work on a CONTAINER'S filesystem and produce a single flattened layer with no history and no CMD or ENTRYPOINT - the resulting image will not even start without you supplying a command.

    bash
    docker save IMAGE   -> image, all layers, history, tags   [use this]docker load         -> restores exactly what was saveddocker export CONTAINER -> flat filesystem, NO history, NO configdocker import       -> makes an image with no CMD or ENTRYPOINTdocker image inspect IMAGE --format "{{.Config.Cmd}}"# after an import this is empty, which is how people discover the difference

    Expected resultThe distinction, stated plainly.

    Success conditionYou would reach for save/load to move an image, and export/import only to flatten a container's filesystem deliberately.

  6. Practical notes for moving real images

    Archives are large because they contain every layer uncompressed - gzip helps considerably. You can save several images into one archive, which is how a whole application moves in a single file. On the far side, verify before deploying.

    bash
    docker save app:1.0 db:17 cache:7 | gzip > stack.tgz# one file containing three images, shared layers stored oncegunzip -c stack.tgz | docker load# then check each ID against the source machine before running anything

    Expected resultMultiple images in a single archive.

    Success conditionYou can move an entire stack offline. For anything security-sensitive, compare IDs at both ends rather than trusting the transfer.

Troubleshooting

Official sources