What is the difference between `docker save`/`load` and `docker export`/`import`?
answer
- Two pairs, two different subjects
- Ask what survives the round trip
- Layers, tags and history versus one flat tar
- The imported image has no CMD or ENTRYPOINT
- Neither one captures volume contents
basics
~20 sdocker save archives an image with all its layers, tags and history, and docker load restores it unchanged. docker export dumps a container's filesystem as one flat tar; docker import turns that into a single-layer image with no history and no CMD or ENTRYPOINT.
solid answer
~50 sThey operate on different things. **`docker save` takes images**: the tar it writes contains every layer, the image config, the manifest and the repository tags, so `docker load` reproduces exactly the image you had, same digest and same `docker history`. **`docker export` takes a container**: it walks that container's filesystem and writes a single flat tar of files — no layers, no image config, no history. `docker import` then creates a one-layer image from that tar, and because the config is gone the new image has **no ENTRYPOINT, CMD, ENV, WORKDIR or EXPOSE** — run it and you get "no command specified" unless you supply one, or bake one in with `docker import --change`. Neither includes the contents of mounted volumes. Use save/load to move an image between hosts without a registry; export/import only when you deliberately want to flatten a filesystem and are willing to re-declare the config.
code
bash · 4 linesdocker save ghcr.io/example/ingest-gateway:1.9 | gzip > gw-1.9.tar.gz
gunzip -c gw-1.9.tar.gz | docker load
docker image inspect ghcr.io/example/ingest-gateway:1.9 --format '{{.Config.Entrypoint}}'go deeper
Remember which command takes an image and which takes a container: save/load for images, export/import for a container filesystem. Know that save/load is how you move an image to a host with no registry access.
Explain precisely what is lost in an export round trip — layers, history, and the whole image config including CMD and ENTRYPOINT — and be able to show docker import --change re-declaring it.
Show judgement about when flattening is acceptable at all, and know the operational details: uncompressed output, no volume data, and a saved tar carrying the platform the daemon actually holds.
Own the artefact strategy: a registry is the normal distribution channel, and tar files are the exception for air-gapped or customer-delivered builds. Decide how those tars are stored, verified and reconciled with the images the registry holds.
### Two different nouns The single most common mistake is treating these as four interchangeable commands. They are two pairs, and each pair has a different subject: | Command | Subject | Produces | |---|---|---| | `docker save` | one or more **images** | a tar containing every layer, the image config, the manifest, and the tags | | `docker load` | that tar | the same images back, tags and history intact | | `docker export` | one **container** | a tar of that container's filesystem, flattened | | `docker import` | that tar (or a URL, or stdin) | a **new image with exactly one layer** | ### What `save`/`load` preserve `docker save -o gw.tar ghcr.io/example/ingest-gateway:1.9` writes an OCI-style archive: a manifest, one blob per layer, and the image's JSON config. `docker load -i gw.tar` on another host puts that image back into the local image store with the same layer digests, the same image ID, the same tags and the same `docker history`. Layers that the target host already has are recognised as already present and are not duplicated in the store. That fidelity is the point. Save/load is the sanctioned way to move an image to an **air-gapped** or registry-less host: build once, `save`, carry the tar, `load`. Anything you could do with the image before you can do afterwards — including re-tagging it and pushing it to a registry later, because the content is unchanged. Two caveats. First, the tar contains *all* layers, so it is typically **larger** than an export of the same application, and it is uncompressed unless you pipe it through `gzip` or `zstd` yourself. Second, `docker save` writes what the daemon actually holds locally for that tag — with the classic image store that is one platform's image, so a tar saved on an amd64 host is an amd64 image regardless of what the registry holds under that tag. ### What `export`/`import` throw away `docker export gw > gw-fs.tar` asks the daemon to serialise the container's filesystem — the image layers merged with whatever the container has written since it started — into a single flat tar. There are no layer boundaries in the output and no image JSON at all. It is a filesystem, not an image. `docker import gw-fs.tar example/gw-flat:1` then wraps that tar as a brand-new image with one layer and an essentially empty config. Everything the Dockerfile declared is gone: **ENTRYPOINT, CMD, ENV, WORKDIR, USER, EXPOSE, VOLUME, HEALTHCHECK**. `docker run example/gw-flat:1` fails with an error about no command being specified; you must pass the command on the run line, or re-declare it at import time with `-c` / `--change`: ``` docker import -c 'ENTRYPOINT ["/usr/local/bin/python","-m","uvicorn"]' \ -c 'ENV PYTHONUNBUFFERED=1' gw-fs.tar example/gw-flat:1 ``` The history is also gone: `docker history` on the imported image shows one entry, created by the import. Nobody can see what the base image was, what packages were installed, or which build produced it. Neither pair captures **volume data**. `docker export` serialises the container's own filesystem; a path that is a bind mount or a named volume is a mount point, and its contents belong to the host or the volume, not to the container. Back volumes up separately — that is a different exercise from moving an image. ### Choosing between them in practice Use **save/load** whenever the goal is "the same image, over there": air-gapped deployment, seeding a build agent's cache, shipping an image to a customer who has no access to your registry, or archiving a release artefact byte-for-byte. Use **export/import** only for the narrow case where flattening is the goal — for example, taking a filesystem snapshot into a completely different toolchain, or building a base image from a filesystem you assembled elsewhere. Flattening for size is usually a bad trade: you lose all layer sharing, so every subsequent pull of a related image transfers the whole thing again, and you lose the provenance that makes the image auditable. If a team reaches for export/import to "squash" an image, the real fix is a multi-stage build. A final diagnostic tip: after any `import`, immediately run `docker image inspect` and look at `Config.Cmd` and `Config.Entrypoint`. Seeing them `null` is the fastest confirmation that the config really did not survive, and it is exactly the evidence an interviewer is listening for.
- After `docker import`, `docker run` on the new image fails saying no command is specified. Why, and what are your two options?The export tar carried only files, so the imported image's config has no Cmd or Entrypoint. Either pass the command on the run line, or re-declare it when importing with `docker import -c 'ENTRYPOINT [...]' -c 'CMD [...]'`. The durable fix is to stop flattening and rebuild from a Dockerfile.
- A colleague suggests export/import as a way to shrink a large image. What do you say?It removes layer boundaries, so the total bytes barely move while every pull loses layer reuse with sibling images, and it destroys the history and config that make the image auditable. Shrink with a multi-stage build, a slimmer base and fewer install artefacts instead.
- Does either pair back up the data in a named volume mounted into the container?No. Volume contents live outside the container's own filesystem, so `docker export` skips them and `docker save` never looked at containers at all. Back a volume up separately — for example by running a throwaway container that mounts the volume and tars it out.
saying these in an interview costs you the question
- Says save and export are the same command with different names
- Thinks `docker save` operates on a container
- Expects an imported image to keep its ENTRYPOINT and ENV
- Believes export/import is a legitimate way to shrink images
- Assumes either command backs up mounted volume data
- Thinks `docker load` needs a registry to be reachable