Copy Files to and from Containers
Master transferring files between your host machine and Docker containers using `docker cp`. This lesson covers the core concept, step-by-step walkthrough, and common edge cases, including handling running vs. stopped containers and directory targets.
Focus: copy files to and from containers
You’ve mastered building images, running containers, and even mounting volumes — but what happens when you need to sneak a config file into a running container without rebuilding the whole image? Or grab a log file from a container that’s about to crash. The docker cp command is your direct file-transfer bridge between the host machine and any container, running or stopped. This lesson shows you how to copy files to and from containers with confidence, avoiding the common pitfalls that trip up even seasoned Docker users.
The problem this lesson solves
Docker containers are isolated by design — their filesystem is separate from the host. This isolation is great for security and reproducibility, but it creates a practical headache: how do you get a file into a container without rebuilding its image? And how do you pull a file out of a container (like a log or export) without exec’ing into a shell and piping over the network?
Mounting a volume at container start is the cleanest long-term solution, but it’s not always an option. Maybe the container is already running, or you only need a one-time transfer. docker cp fills this gap: it treats the container’s filesystem like a remote drive, letting you copy files and directories in either direction using a single, simple command.
Core concept / mental model
Think of docker cp as a cross-filesystem cp command — but instead of copying between two host paths, one side is a container path. The container is identified by its name or ID, followed by a colon (:) and the absolute path inside the container.
docker cp <source> <target>
- Host to container:
docker cp /host/file.txt container_name:/path/in/container/ - Container to host:
docker cp container_name:/path/in/container/file.txt /host/destination/
Important: The container does not need to be running.
docker cpworks with stopped containers too, because it reads the container’s filesystem metadata from the Docker overlay layers.
How it works step by step
Let’s walk through the exact sequence Docker follows when you run docker cp:
- Resolve the container: Docker looks up the container name or ID in its metadata (running or stopped).
- Access the container’s root filesystem: For running containers, Docker accesses the union filesystem (overlay2). For stopped containers, it reads the committed layers.
- Perform the copy: Docker uses its internal file copier — optimized for container paths — to transfer the file or directory. It does not use the container’s own
cpcommand or shell. - Preserve metadata by default:
docker cptries to preserve ownership, permissions, and (for files) timestamps. This behavior can be overridden with the--archive(-a) flag (which is on by default).
Pro tip: Because
docker cpworks at the Docker daemon level, it can copy files even if the container has no shell or basic utilities likebashortarinstalled.
Hands-on walkthrough
Let’s get practical. You’ll create a simple container, copy files in both directions, and verify the result.
1. Create a sample container
# Start an Alpine container in the background
docker run -d --name cp-demo alpine:latest sleep 3600
2. Copy a file from host to container
# Create a text file on the host
echo "Hello from host" > host-greeting.txt
# Copy it into the container's /tmp directory
docker cp host-greeting.txt cp-demo:/tmp/
# Verify the file is inside the container
docker exec cp-demo cat /tmp/host-greeting.txt
# Output: Hello from host
3. Copy a file from container to host
# Create a file inside the container
docker exec cp-demo sh -c "echo 'Greetings from container' > /tmp/container-file.txt"
# Copy it to the host
docker cp cp-demo:/tmp/container-file.txt ./
# Check the local copy
cat container-file.txt
# Output: Greetings from container
4. Copy a directory recursively
# Create a directory with nested files inside the container
docker exec cp-demo mkdir -p /app/data
docker exec cp-demo sh -c "echo 'nested' > /app/data/info.txt"
# Copy the whole directory to the host
docker cp cp-demo:/app/data ./container-data
# List the copied directory
ls -R container-data/
# Output: container-data/:
# info.txt
5. Copy to a stopped container
# Stop the container
docker stop cp-demo
# Copy a config file into the stopped container
echo "config=production" > app.conf
docker cp app.conf cp-demo:/app/config/
# Start and verify
docker start cp-demo
docker exec cp-demo cat /app/config/app.conf
# Output: config=production
Compare options / when to choose what
| Method | Use case | Pros | Cons |
|---|---|---|---|
docker cp |
One-time file transfer | Simple, works on stopped containers, no shell needed | No auto-sync, manual per transfer |
Volume mount (-v) |
Persistent shared data | Auto-sync, survives container restart | Must be set up at container start |
docker exec + pipe |
Ad-hoc files when cp fails | Works even without cp inside container |
Requires shell, more complex |
scp/rsync over SSH |
Remote hosts | Encryption, resumable transfers | Requires SSH inside container |
When to use
docker cp: Quick debugging (copy out logs), injecting configs into running containers, extracting artifacts from a stopped build container. When not to: For production data that needs persistence — use volumes instead.
Troubleshooting & edge cases
"No such container" error
docker cp missing-container:/tmp/file .
# Error: Error response from daemon: No such container: missing-container
Fix: Check the container is running or stopped with docker ps -a. The container name must match exactly.
Path not found inside container
docker cp demo:/nonexistent/path .
# Error: Error response from daemon: Could not find the file /nonexistent/path in container
Fix: Use docker exec with ls or test -e to verify the path exists before copying.
Copying symbolilc links
By default, docker cp follows symlinks and copies the target file. To preserve the symlink as a symlink, you need to use --follow-link=false (available in Docker 24+). Older versions always follow symlinks.
docker cp --follow-link=false container:/path/to/symlink ./
Overwrite vs. rename confusion
If the target is a directory, the source is placed inside it:
# Copies file.txt INTO /tmp/ (becomes /tmp/file.txt)
docker cp file.txt container:/tmp/
If the target ends with /, Docker treats it as a directory. If it’s a filename and exists as a directory, it fails:
docker cp file.txt container:/tmp/existing_dir # okay, creates /tmp/existing_dir/file.txt
docker cp file.txt container:/tmp/existing_file # ERROR if existing_file is a file
What you learned & what's next
You now know how to:
- Copy files from host to container using docker cp
- Extract files from container to host
- Transfer entire directories recursively
- Work with both running and stopped containers
- Correctly handle common edge cases like missing paths, symlinks, and overwrite behavior
Next up: You'll learn how to manage environment variables — injecting sensitive configs like API keys and database URLs without hardcoding them into your Dockerfile.
Practice recap
Try this mini-exercise: 1) Run an Nginx container (docker run -d --name nginx-practice -p 8080:80 nginx:alpine). 2) Copy a custom index.html from your host into the container at /usr/share/nginx/html/. 3) Verify the custom page loads by visiting http://localhost:8080. 4) Copy the default Nginx config file from the container to your host (/etc/nginx/nginx.conf) and inspect it. This will solidify your understanding of bidirectional file transfer with docker cp.
Common mistakes
- Forgetting to include the colon (
:) in the container path:docker cp file.txt containername/tmp/— without the colon, Docker thinks the entire string is a file name. - Trying to copy into a container that has been removed (not just stopped):
docker cponly works on containers visible indocker ps -a. - Confusing overwrite vs. rename behavior — if the target path ends with a slash, the source goes inside that directory; without a slash, it replaces the file/directory if it exists.
- Assuming
docker cppreserves extended attributes or ACLs — it preserves basic Unix permissions and ownership but does not handle all filesystem metadata (e.g., xattrs from some storage drivers).
Variations
- Use
docker cpwith an archive flag (-a) to preserve permissions and symlinks exactly as they are in the container (this is the default). - For large transfers, consider using
docker execwithtarpiped over stdin/stdout for better performance and compression:docker exec container tar -cf - /path | tar -xvf -. - When copying between two containers, use a two-step process: copy from container A to host, then from host to container B — there’s no direct container-to-container
docker cp.
Real-world use cases
- Extracting application logs from a crashed container for debugging without stopping the container (if still running).
- Injecting a one-time SSL certificate or license file into a running web server container without rebuilding the image.
- Grabbing a database dump file generated inside a container (e.g.,
pg_dump) to your local machine for backup or analysis.
Key takeaways
docker cplets you transfer files between host and container filesystems, even when the container is stopped.- The syntax is
docker cp <source> <target>with a colon separating container name from path:container:path. - Copying directories requires no special flag — just include a trailing
/on the target path to place contents inside. docker cpdoes not require a shell or any utilities inside the container, making it ideal for minimal images.- Edge cases like missing paths, symlinks, and overwrite behavior are common — always verify the container path exists before copying.
- For persistent data sharing, prefer volume mounts over ad-hoc
docker cpin production.
Keep learning
Related tutorials, quizzes, and articles for this topic.
Discussion
Questions, corrections, and tips help everyone reading this page.
0 comments
Add a comment
No comments yet — start the thread.