Copy Files To Docker Container

Copy Files To Docker Container

Reading time1 min
#Docker#DevOps#Containers#dockercp#FileTransfer#ContainerManagement

Copying Files into Docker Containers: Practical Techniques with docker cp

Copying files into running Docker containers is deceptively simple. Underneath, though, routine mistakes can stall your CI pipeline, break permissions, or complicate troubleshooting—especially when dealing with stateful workloads, ephemeral volumes, or custom entrypoints.


File Transfers in Docker: Where the Default Falls Short

Consider the recurring tasks: injecting secrets post-deploy, hot-patching configuration for a misbehaving service, or loading a ~1GB binary for performance diagnostics. Relying solely on docker cp isn’t always ideal—its behavior varies with file sizes, volume mounts, the user context inside containers, and OS quirks (notably on Docker Desktop with Windows filesystems).


Command Syntax and Quick Recap

docker cp allows file or directory transfer between host and container:

docker cp <SRC_PATH> <CONTAINER>:<DEST_PATH>
docker cp <CONTAINER>:<SRC_PATH> <DEST_PATH>

Example: push a config file into a running service:

docker cp ./config.prod.yaml api_1:/etc/api/config.yaml

This copies the file. It does not sync—no tracking of future changes, and no magic with inotify or filesystem events.


Key Technical Considerations and Pitfalls

Bandwidth, Bottlenecks, and Daemon Overhead

Large files (hundreds of megabytes up) are throttled by the Docker daemon and can stress host CPU and IO:

$ time docker cp large_model.bin analytics_worker:/tmp/

This can take minutes, especially with Docker Desktop’s VM translation layer on Mac/Windows. In high-churn workflows, bind mounts or staged image builds are significantly faster. Treat docker cp as a last resort for shipping binaries over ~100MB: it’s functional, but not performance-optimized.


Directory Copies: Dot-Slash Details

Docker’s handling of trailing slashes and dots occasionally bites even experienced users.

docker cp ./init_scripts/. appserver:/usr/local/bin/

Copies contents without nesting.
Omitting .:

docker cp ./init_scripts appserver:/usr/local/bin/

Creates /usr/local/bin/init_scripts and nests everything inside—potentially breaking entrypoint assumptions.
Pay attention—especially when scripting or templating commands.


File Permissions and Ownership Shifts

Files copied in as root (most Docker setups run with UID 0 by default) will retain ownership—unless your image’s USER directive, entrypoint, or bind-mounted volume uses an explicit user (e.g., UID 1001 for app processes). When things go wrong:

  • Non-root containers end up with files they can’t access:
    PermissionError: [Errno 13] Permission denied: '/app/config.yaml'
    
  • Hybrid environments (CI runners, volume-shared dev setups) exhibit subtle failures.

Remediation:

  1. Fix ownership post-copy:
    docker exec appserver chown -R node:node /usr/local/bin/
    
  2. Pre-adjust on host before copying, if possible:
    sudo chown -R 1001:1001 ./data_volume/
    

No flag in docker cp to force chown—manual adjustments needed.


Path Existence and Directory Creation Gotchas

If /target/dir is missing inside the container:

docker cp certs/app.pem nginx:/target/dir/app.pem

Docker creates /target/dir/app.pem as a directory, depositing app.pem inside as a file—a common cause of

stat /target/dir/app.pem: not a directory

—errors on service start.

Mitigate: create the target path before copying:

docker exec nginx mkdir -p /target/dir

Or explicitly copy to an existing, verified path.


Relative Paths and Context Drift

Relative source paths (./build, ../artifacts) are resolved from the invoking shell, not the Docker context.
CI/CD runners, makefiles, and scripted workflows often misalign here—verify with pwd and ls before running docker cp.


When to Avoid docker cp Altogether

  • Iterative development: Bind mount host directories during docker run:
    docker run -v $PWD/static:/app/static ...
    
    Real-time changes, no copying, but raises host/guest permission and path separator issues (esp. Windows).
  • Immutable image construction:
    Bake in via Dockerfile for predictable, reproducible builds:
    COPY static /app/static
    
    Key in production if you need artifact traceability.
  • High-volume data sync:
    Use rsync in an exec'd shell, or a shared external volume.

Quick Reference Table

Use CasePreferred TechniqueCaveats / Notes
Patch single file in running appdocker cpMind permissions and destination existence
Bulk data development (rapid churn)Bind-mount host volumeWatch for host/container fs differences
Static config for prodCOPY in DockerfileImmutable—no late changes; rebuild if modified
CI/CD deploymentCOPY in image; avoid cp post-launchSave time, avoid non-reproducible container state
Large file or binary (>100MB)Volume mount or multi-stage builddocker cp can be slow, disrupts deployments
Hotfix with file ownership needsdocker cp, then chown with execNo flags; always post-fix as needed

A Real Example: Late Injection in Debug Containers

Debugging a legacy Java process in openjdk:11.0.18-jdk-slim—attach a JVM agent:

docker cp ./debug_agent.so java_app:/tmp/
docker exec java_app chown appuser:appgroup /tmp/debug_agent.so
docker exec -u appuser java_app bash -c 'export LD_PRELOAD=/tmp/debug_agent.so; ./start.sh'

Note how both transfer and permissions must be managed.
(Later, for repeatability and automation, this should move to a Dockerfile COPY and ARG.)


Summary

docker cp is sharp but limited. Use it precisely and sparingly, especially in production workflows or automated deployments.
Diagnose errors by checking path existence, permissions, and daemon performance. Consider bind mounts and image builds wherever reliability and speed matter. Unexpected behaviors surface most on multi-platform Docker hosts and with non-root user images.


Drop complex or environment-specific transfer scenarios in the comments; nuances vary per base image, orchestrator, and storage driver. Sometimes, the best technique is the simplest—just not always the most obvious.