skip to content

questions

4

How does `docker cp` move files between the host and a container, and what can't it do?

level: juniorimportance: must knowfreq 74%

answer

  1. No process runs inside the container
  2. Think about what the Engine API speaks
  3. A tar stream over /containers/{id}/archive
  4. `-` means stdin or stdout
  5. Works on a container that never started

basics

~20 s

docker cp streams a tar archive through the Docker Engine API, so the daemon does the extraction and the image needs no shell or tar binary. It works on stopped containers, expands no wildcards, and does not preserve ownership unless you pass -a.

solid answer

~50 s

`docker cp` is not a shell copy. The CLI opens the Engine API endpoints `GET /containers/{id}/archive` and `PUT /containers/{id}/archive` and pushes or pulls a **tar stream**; the daemon packs and unpacks it on the container's filesystem. Two consequences matter in interviews. First, nothing runs inside the container, so it works on a **stopped or created** container and on a distroless image with no shell, no `cp` and no `tar`. Second, because no shell is involved, the path is taken literally — `docker cp c:/logs/*.log .` fails, there is no glob expansion. `-` as the source or destination means the tar stream comes from stdin or goes to stdout, so `docker cp c:/var/log - > logs.tar` gives you a tarball directly. Use `-a` to carry UID/GID across, and remember copying into a running container writes files under the app's feet with no notification.

code

bash · 6 lines
bash
docker create --name gw ghcr.io/example/ingest-gateway:1.9
docker cp gw:/etc/ingest/config.yaml ./config.yaml

docker cp ./config.yaml gw:/etc/ingest/config.yaml

docker cp -a gw:/var/lib/ingest/. ./ingest-data

go deeper

for a junior

Know both directions of the syntax and that the container does not need to be running. Be able to say that a tar stream over the Docker API is what moves the bytes, not a shell command inside the container.

for a middle

Explain the API endpoints, the directory and /. path rules, what -a and -L change, and why globbing does not work. Be ready to show the stdin/stdout - form.

for a senior

Show the incident habit: copy the artefact out of the stopped container before removing it, and treat any copy into a running container as drift that will vanish on the next restart.

for a principal

Own the policy question: if teams routinely docker cp files into running containers, the image or the mount contract is wrong. Decide where configuration and data legitimately enter a container and make that path the easy one.

### What the command actually is `docker cp` looks like `cp`, but it is a thin wrapper over two Docker Engine API endpoints: - `GET /containers/{id}/archive?path=…` — the daemon walks the path inside the container's filesystem and returns a **tar stream**; - `PUT /containers/{id}/archive?path=…` — the daemon reads a **tar stream** from the request body and extracts it into the container's filesystem. The CLI's job is to build or unpack that tar on the host side. Everything on the container side is done by the daemon, walking the container's merged root filesystem from the host. **No process is started inside the container.** ### The four consequences that get asked about **1. It works on a stopped container.** The container only has to exist. `docker create` it and never start it, or let it exit with a stack trace, and you can still pull files out. This is why `docker cp` is the standard answer to "the container crashed, get me the dump/report it wrote": you do not need the process alive, only the container not yet `docker rm`-ed. The corollary is that `docker run --rm` destroys the container the moment it exits, taking the evidence with it. **2. It needs nothing in the image.** A distroless or `FROM scratch` image with a single static binary has no shell, no `cat`, no `tar`. `docker exec … tar` is impossible there; `docker cp` still works, because the tar is produced by the daemon, not by the container. **3. There is no shell, so there is no globbing.** `docker cp gw:/var/log/*.log ./` does not expand — the daemon looks for a path literally named `*.log` and you get a "no such file or directory" style error. Copy the directory instead (`docker cp gw:/var/log ./log`), or run a shell explicitly with `docker exec sh -c 'tar cf - /var/log/*.log' > logs.tar`. The same applies to `~` and `$HOME`. **4. `-` means stdin/stdout, and the payload is a tar.** `docker cp CONTAINER:SRC -` writes a tar archive to stdout; `docker cp - CONTAINER:DEST` reads one from stdin. That is how you pipe container→container without touching disk, and how you stream a directory straight into `tar -x` on the host. ### Path semantics people trip on The directory rules mirror `cp -a`: if the source is a directory and the destination exists as a directory, the source directory is copied **into** it, producing `dest/src/...`. Ending the source with `/.` copies the *contents* instead — `docker cp gw:/app/. ./app` fills `./app` rather than creating `./app/app`. If the destination does not exist, its parent must; a non-existent parent is an error, not an mkdir -p. Ownership is the other classic surprise. `-a` / `--archive` preserves UID/GID from the source; without it, the copied files can land with ownership that does not match the container's non-root application user, and the app then fails to read or rewrite them. If you copy a config file into a container running as UID 10001, check the ownership afterwards rather than assuming. Flags worth knowing: `-L` / `--follow-link` dereferences a symlink in the source path instead of copying the link itself; `-q` suppresses the progress output. Docker documents that some paths cannot be copied out — entries under `/proc`, `/sys`, `/dev` and tmpfs are not ordinary files in the container's layered filesystem. ### When it is the wrong tool `docker cp` is a *rescue and inspection* tool, not a delivery mechanism. Copying application files into a running container mutates the container's writable layer and leaves you with a container whose contents no longer match its image — the next restart from that image silently discards the change, and nobody can tell what was done. For files that must be present every time, put them in the image at build time or mount them; keep `docker cp` for pulling a heap dump, a core file, a profiler output or a log out of a container that is already broken. A last operational note: copying into a **running** container is not atomic from the application's point of view. The daemon extracts the tar entry by entry while the process is reading; a config file can be observed half-written. If the app watches the file for changes, write to a temporary name and move it with `docker exec`, or restart the container.

  • The image is distroless — no shell, no tar, no cp. Does `docker cp` still work?
    Yes. The daemon packs and unpacks the tar on the host side against the container's filesystem, so nothing has to exist inside the image. That is exactly why `docker cp` is the extraction tool for minimal images, where `docker exec tar` is impossible.
  • Why does `docker cp gw:/var/log/*.log .` fail?
    Because no shell is involved, so nothing expands the glob. The daemon looks for a path literally named `*.log`. Copy the whole directory, or expand the pattern inside the container with `docker exec sh -c 'tar cf - /var/log/*.log'` and redirect the tar on the host.
  • How do you get a directory out of a container as a single tar file without writing it to disk twice?
    `docker cp CONTAINER:/path - > path.tar`, or pipe it: `docker cp CONTAINER:/path - | tar -tvf -`. The `-` destination makes the CLI emit the raw tar stream it already receives from the API, so nothing is unpacked and repacked.

saying these in an interview costs you the question

  • Claims the container must be running to copy files out
  • Thinks `docker cp` shells out to `cp` inside the container
  • Expects wildcards and `$HOME` to expand in the path
  • Assumes the image needs a tar or cp binary
  • Uses `docker cp` to deploy config into running production containers
  • Assumes file ownership is preserved by default

context

open as a page

What is the difference between `docker save`/`load` and `docker export`/`import`?

level: middleimportance: must knowfreq 63%

basics

~20 s

docker 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.

open as a page

What does `docker commit` capture from a container, and why is the resulting image a poor deliverable?

level: middleimportance: should knowfreq 48%

basics

~20 s

docker commit freezes a container's writable layer as a new layer on top of its image and copies the container's runtime config into the new image. Nothing records how that state was produced, so the image cannot be rebuilt, reviewed or trusted.

open as a page

How does `docker diff` help you find what a running container has written, and where does it mislead you?

level: seniorimportance: nice to knowfreq 27%

basics

~20 s

docker diff CONTAINER lists every path that differs from the container's image, prefixed A for added, C for changed and D for deleted. It shows no sizes, no file contents, and nothing under a volume or bind mount — so a disk problem in mounted storage is invisible to it.

open as a page