Copying Files with docker cp
docker cp copies files or directories between your host machine and a Docker container. It matters when you need to pull logs, inspect generated files, recover data from a stopped container, or place a small test file into a running container without rebuilding an image. It is a convenient debugging tool, but it should not replace repeatable image builds, bind mounts, volumes, or proper backup workflows.
Overview: How docker cp Works
A Docker image is a read-only template made from stacked filesystem layers. A container is an instance of that image with runtime configuration, mounts, and a thin writable layer on top. When a process inside the container creates or changes a file that is not on a mounted volume, that change lives in the container’s writable layer. When the file is inside a bind mount or named volume, the data lives in that mounted storage instead.
docker cp asks the Docker daemon to copy bytes between the host and one container filesystem view. The Docker CLI is the client; it sends the copy request to the daemon, and the daemon reads from or writes to the container’s filesystem. On Docker Desktop for macOS and Windows, Linux containers run inside Docker’s managed Linux VM, but docker cp still feels like a host command because Docker bridges the request through the daemon.
The command can copy from a container to the host or from the host to a container. Container paths use the form CONTAINER:SRC_PATH or CONTAINER:DEST_PATH. The container side is interpreted inside the container’s filesystem, not on the host. A leading slash is optional for container paths, so web:/etc/nginx/nginx.conf and web:etc/nginx/nginx.conf both refer to the same absolute path inside the container. In lessons and scripts, the leading slash is clearer.
Unlike docker exec, docker cp does not require the container to be running. You can copy files out of a stopped container, which is useful after a crash. Docker still needs the container object to exist, because the container object owns the writable layer and mount configuration. If you remove the container with docker rm, files that existed only in that container layer are gone. Removing an image is different: it removes the reusable read-only template, not a specific container’s writable files.
By default, docker cp behaves much like Unix cp -a. It copies directories recursively and preserves file permissions where possible. Ownership has one important twist: when copying from a container to the host, copied files are usually created with the host user running the command. When copying from the host to a container, files are usually created as root inside the container. With -a, Docker tries to preserve source ownership instead. User and group IDs may not map to names on the other side, so think in UIDs and GIDs when ownership matters.
Syntax
docker cp [OPTIONS] CONTAINER:SRC_PATH DEST_PATH
docker cp [OPTIONS] SRC_PATH CONTAINER:DEST_PATH
| Part | Meaning |
|---|---|
CONTAINER |
A container name or ID, such as cp-web. This is not an image tag. |
SRC_PATH |
The file or directory to copy. On the container side, paths are inside the container filesystem. |
DEST_PATH |
The destination file or directory. If the destination is an existing directory, Docker copies into it. |
-a, --archive |
Preserves source ownership where possible instead of assigning ownership based on the destination side. |
-L, --follow-link |
Follows symbolic links in the source path and copies the link target instead of the link itself. |
- |
As source or destination, streams a tar archive through standard input or standard output. |
The most common forms are simple file copies. The tar streaming form is powerful for automation, but beginners should first become comfortable with normal host-to-container and container-to-host paths.
Examples
Copy a File from a Container to the Host
docker create --name cp-web nginx:1.27-alpine
docker cp cp-web:/etc/nginx/nginx.conf ./nginx.conf
docker rm cp-web
Output:
a3f6b7c8d9e0f1234567890abcdef1234567890abcdef1234567890abcdef1234
cp-web
docker create creates a container without starting the web server. That is enough for docker cp, because Docker can read the container filesystem even when the main process is not running. The copy command writes the Nginx configuration file to ./nginx.conf on the host. The copy itself normally prints no output when it succeeds; the shown lines come from docker create and docker rm.
Copy a Local File into a Running Container
printf 'hello from the host\n' > message.txt
docker run -d --name cp-alpine alpine:3.20 sleep 1d
docker cp ./message.txt cp-alpine:/tmp/message.txt
docker exec cp-alpine cat /tmp/message.txt
docker rm -f cp-alpine
rm -f message.txt
Output:
f4c2d6e8a0b1c3d4e5f678901234567890abcdef1234567890abcdef12345678
hello from the host
cp-alpine
This example creates a small host file, starts a long-running Alpine container, copies the file into /tmp, and verifies it with docker exec. The copied file lands in the container’s writable layer because /tmp is not a mounted volume in this example. If the container is removed, that copied file disappears with the container.
Copy a Directory Out for Inspection
docker run -d --name cp-site nginx:1.27-alpine
docker cp cp-site:/usr/share/nginx/html ./site-copy
docker rm -f cp-site
Output:
9d8c7b6a5f4e3210abcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcd
cp-site
When the source is a directory, Docker copies the directory recursively. After this command, ./site-copy contains the default Nginx static files from the image. This is useful for inspection, but copying files out of a container is not the same as publishing them into source control or building a new image. Treat it as a snapshot for learning or debugging.
Stream a Tar Archive from a Container
docker create --name cp-archive nginx:1.27-alpine
docker cp cp-archive:/etc/nginx - > nginx-etc.tar
docker rm cp-archive
Output:
7b6a5f4e3d2c1098abcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefab
cp-archive
Using - as the destination tells Docker to write a tar archive to standard output. The shell redirect writes that archive into nginx-etc.tar. This pattern is useful when you want a single archive instead of a copied directory tree. Be careful with redirects: > is handled by your host shell, not by Docker.
How it Works Step by Step
- You run
docker cpwith one host path and one container path. Docker determines direction from the side that containsCONTAINER:. - The Docker client sends the request to the Docker daemon. The client does not directly enter the container namespaces.
- The daemon resolves the container name or ID and locates the container filesystem, including the image layers, writable layer, and any mounted volumes.
- For container-to-host copies, Docker reads the source path from that filesystem view and sends the file data back to the client.
- For host-to-container copies, Docker receives data from the client and writes it into the destination path inside the container view.
- If the copied path is under a named volume or bind mount, the operation affects that mounted storage. If it is not mounted, the operation affects the container’s disposable writable layer.
- When the copy finishes, no image layer is changed. Future containers created from the same image will not include files you copied into this one container.
This last point is the heart of using docker cp well. It is a tactical operation against one container. It does not rebuild the image, update the Dockerfile, or change Compose configuration. For repeatable application files, use COPY in a Dockerfile. For development source code, use a bind mount. For durable runtime data, use a named volume and back it up deliberately.
Common Mistakes
Using an Image Name Instead of a Container
docker cp nginx:1.27-alpine:/etc/nginx/nginx.conf ./nginx.conf
This is wrong because nginx:1.27-alpine is an image reference, not a container name. The colon in an image tag also makes the command ambiguous to a human reader. Create or identify a container first:
docker create --name fixed-cp-web nginx:1.27-alpine
docker cp fixed-cp-web:/etc/nginx/nginx.conf ./nginx.conf
docker rm fixed-cp-web
Expecting Copied Files to Survive Container Replacement
docker run -d --name patched-web nginx:1.27-alpine
docker cp ./nginx.conf patched-web:/etc/nginx/nginx.conf
docker rm -f patched-web
The copied configuration changed one container. After docker rm -f, the file is gone unless it was also stored somewhere durable on the host. If the configuration is part of the application, put it in a Dockerfile or mount it explicitly:
docker run -d --name mounted-web -v "$PWD/nginx.conf:/etc/nginx/nginx.conf:ro" nginx:1.27-alpine
Forgetting Destination Directory Rules
If the destination path exists and is a directory, Docker copies the source into that directory. If the destination parent does not exist, the copy can fail. Create the destination directory in the image, with docker exec mkdir -p during debugging, or choose a known existing directory such as /tmp for temporary files.
Ignoring Ownership and Permissions
A file copied into a container may be owned by root, which can break applications that run as a non-root user. A file copied out of a container may lose the username you expected because the host may not have the same user database. Use -a when preserving numeric ownership matters, and use docker exec chown only as a temporary debugging step. For production, set ownership in the Dockerfile with COPY --chown where appropriate.
Best Practices
- Use
docker cpfor inspection, recovery, and short-lived debugging, not as your normal deployment method. - Name containers with
--namein examples and scripts so copy commands are readable. - Use specific image tags such as
nginx:1.27-alpineandalpine:3.20. Avoidlatestfor reproducible labs and builds because it moves over time. - Copy files out of stopped containers before removing them if you need crash artifacts from the writable layer.
- Prefer volumes for durable application data and bind mounts for local development files.
- Use
COPYin a Dockerfile for files that should exist in every new container. - Be careful with ownership. Container users and host users may have different names for the same numeric UID.
- Do not copy secrets into images or containers as an informal fix. Use Docker secrets, mounted secret files, or your orchestrator’s secret store.
- Remember that
docker cpis not related toEXPOSEor port publishing. Ports are published withdocker run -por Composeports:.
Practice Exercises
- Create an
nginx:1.27-alpinecontainer without starting it. Copy/etc/nginx/nginx.confto the host, then remove the container. Expected end state: a local file namednginx.conf. - Start an
alpine:3.20container that sleeps for one day. Copy a host file into/tmp, then usedocker execto print it. Hint: create the file withprintffirst. - Run a container with a named volume mounted at
/data. Copy a file into/data, remove the container, then start a new container with the same volume and verify the file is still there.
Summary
docker cpcopies files or directories between the host and one container.- The command works with running and stopped containers, but not with images by themselves.
- Container paths use
CONTAINER:/pathand are resolved inside the container filesystem view. - Files copied into the container writable layer disappear when that container is removed.
- Files under bind mounts or named volumes affect the mounted storage instead of only the container layer.
- Use Dockerfiles, bind mounts, volumes, and backups for repeatable workflows; use
docker cpfor targeted inspection and recovery.
