| Tested on | Red Hat Enterprise Linux 10.2 (Coughlan) |
|---|---|
| Package | podman-5.8.2-5.el10_2.x86_64 |
| Applies to | Any Linux host with Podman installed |
| Privilege | Rootful examples on the lab host; podman mount and ownership differ rootless |
| Scope | One-time file copy with podman cp — host↔container, container↔container, directory semantics, stopped containers, --archive, tar streams, podman mount, and common errors. Does not cover volume design, SELinux mount labels, image export, or user-namespace theory. |
| Related guides | Run containers with podman run Install Podman on RHEL |
podman cp copies files and directories between your host and container storage — or between two containers — without SSH and without rebuilding the image. This guide uses one demo container named cp-demo for every example.
podman cp syntax
The general form is:
podman cp [options] [container:]SOURCE [container:]DESTINATIONSupported directions:
host → container
container → host
container → containerpodman cp works on running and stopped containers because it operates on container storage, not on a live process the way podman exec does.
Create the demo container:
podman run -d --name cp-demo registry.access.redhat.com/ubi9/ubi-minimal sleep 3600Copy a file from the host to a container
Prepare a file on the host:
echo "example content from host" > example.txtCopy it into the container:
podman cp ./example.txt cp-demo:/tmp/example.txtPaths inside the container are relative to the container root — /tmp/file and tmp/file resolve the same way. The parent directory must exist when the destination is a file path.
Confirm the file arrived:
podman exec cp-demo cat /tmp/example.txtSample output:
example content from hostThe main sleep process in cp-demo keeps running; podman cp only changed files on disk.
Copy a file from a container to the host
Pull a file out of the container filesystem:
podman cp cp-demo:/etc/os-release ./os-releaseRead it on the host (relative paths resolve from your current working directory):
head -3 ./os-releaseSample output:
NAME="Red Hat Enterprise Linux"
VERSION="9.8 (Plow)"
ID="rhel"Copy directories with podman cp
Directory copies trip people up because Podman can copy the directory node or only its contents. First create sample data inside the container:
podman exec cp-demo sh -c 'mkdir -p /data/dir1 && echo f1 > /data/dir1/a.txt'Copy the directory itself — destination dest-dir/ receives a dir1 subdirectory:
podman cp cp-demo:/data/dir1 ./dest-dir/Sample resulting path:
./dest-dir/dir1/a.txtCopy only the contents by appending /. to the source path:
podman cp cp-demo:/data/dir1/. ./dest-contents/Sample resulting path:
./dest-contents/a.txtThe first form nests dir1 under dest-dir. The second drops a.txt directly under dest-contents with no extra directory level.
Copy files to or from a stopped container
Stop the container — copying still works:
podman stop cp-demoPush a file while nothing is running inside the namespace:
echo "stopped config" > config.confCopy it into stopped storage:
podman cp ./config.conf cp-demo:/tmp/config.confPull a file back out the same way:
podman cp cp-demo:/tmp/config.conf ./config-from-stopped.confConfirm the round trip on the host:
cat ./config-from-stopped.confSample output:
stopped configpodman cp touches container storage directly, so the process does not need to be running. Start the container again when you need podman exec or a listening service:
podman start cp-demoCopy files between two Podman containers
Podman can copy from one container to another without round-tripping through the host. Create two containers and a source file:
podman run -d --name cp-a registry.access.redhat.com/ubi9/ubi-minimal sleep 3600Create a second container for the destination:
podman run -d --name cp-b registry.access.redhat.com/ubi9/ubi-minimal sleep 3600Seed a file in the source container:
podman exec cp-a sh -c 'echo cross-container > /tmp/file.txt'Copy directly into the second container:
podman cp cp-a:/tmp/file.txt cp-b:/tmp/Verify on the destination:
podman exec cp-b cat /tmp/file.txtSample output:
cross-containerUnderstand file and directory destination rules
These patterns cover the cases you hit most often on Podman 5.8.2:
| Source | Destination | Result |
|---|---|---|
| File | Absent path (parent exists) | Creates the file |
| File | Existing file | Overwrites contents |
| File | Existing directory | Places file inside directory |
| Directory | Absent path | Creates destination directory with contents |
| Directory | Existing file | Error — destination must be a directory |
Directory + /. |
Directory | Copies contents without extra nesting |
Copying a file onto an existing host file overwrites it:
podman cp cp-demo:/tmp/example.txt ./target.txtA missing parent directory for a file destination fails — Podman reports the full path as not found:
podman cp ./example.txt cp-demo:/tmp/noparent/sub/example.txtSample output:
Error: "/tmp/noparent/sub/example.txt" could not be found on container cp-demo: no such file or directoryCreate /tmp/noparent/sub with podman exec first, or copy to an existing parent path.
Copying a directory onto a regular file also fails:
Point the destination at a regular file:
echo single-file > ./single-fileAttempting to copy a directory onto it fails:
podman cp cp-demo:/data/dir1 ./single-fileSample output:
Error: destination must be a directory when copying a directoryPreserve or change ownership with --archive
On Podman 5.8.2, --archive defaults to true. When copying into a container, Podman may chown the result to the destination container's primary UID and GID (often root, 0:0).
Copy a host file owned by UID 1000 with default archive behavior:
Create a host file and assign UID 1000:
echo host-owned > hostfile.txt && chown 1000:1000 hostfile.txtCopy with default archive behavior:
podman cp hostfile.txt cp-demo:/tmp/hostfile.txtInside the container the file is owned by root:
podman exec cp-demo ls -ln /tmp/hostfile.txtSample output:
-rw-r--r--. 1 0 0 11 Aug 22 16:03 /tmp/hostfile.txtUse --archive=false to preserve the source UID/GID instead of remapping ownership to the destination container's primary UID/GID.
Preserve the host UID with --archive=false:
podman cp --archive=false hostfile.txt cp-demo:/tmp/hostfile-noarch.txtOwnership should match the source file:
podman exec cp-demo ls -ln /tmp/hostfile-noarch.txtSample output:
-rw-r--r--. 1 1000 1000 11 Aug 22 16:03 /tmp/hostfile-noarch.txtRootless copies can look wrong on either side because host and container UIDs map differently. See Podman user namespaces and Podman volume permissions when ownership blocks reads or writes — not repeated here.
podman cp does not support shell globbing
Shell globs are not expanded on the container side:
podman cp cp-demo:/tmp/*.log ./Sample output:
Error: "/tmp/*.log" could not be found on container cp-demo: no such file or directoryPodman searches for a literal *.log path. Alternatives:
- Copy the parent directory:
podman cp cp-demo:/tmp ./tmp-backup/ - Run a shell inside the container via Run commands with podman exec and use
tarthere - Stream a tar archive through
podman cp -(next section) - Use
podman mountfor bulk work on the storage layer
Stream tar archives through podman cp
Pass - as the source or destination to read or write a tar stream on STDIN/STDOUT.
Stream from the host into a container directory. Create the payload and target directory first:
echo stream-content > stream.txtEnsure the destination directory exists inside the container:
podman exec cp-demo mkdir -p /tmp/stream-dirPipe a tar archive on stdin:
tar cf - -C . stream.txt | podman cp - cp-demo:/tmp/stream-dir/Read back the extracted file:
podman exec cp-demo cat /tmp/stream-dir/stream.txtSample output:
stream-contentStream from a container directory to stdout and list members with tar:
podman cp cp-demo:/data/dir1 - | tar tf -Sample output:
dir1/
dir1/a.txtThis pattern helps when you need many files without fighting glob semantics. These examples require tar on the host to create or inspect the stream; the container itself does not need a tar binary.
Use podman mount for direct filesystem access
Repeated podman cp calls add overhead. For large or many files, mount the container root filesystem and use normal host tools:
podman mount cp-demoSample output:
/var/lib/containers/storage/overlay/26494a182718d7d52b8548a551044f2376128386d7a714474658a900bbdb757e/mergedOperate on files under that path (substitute your mount output):
ls "$(podman mount cp-demo)/tmp/example.txt"Unmount when finished:
podman umount cp-demoWith rootless Podman, podman mount generally needs to run inside podman unshare unless the VFS storage driver is in use — see Podman unshare for the full workflow. Rootful hosts, as in this lab, can read the merged path directly.
podman cp vs volume or bind mount
| Need | Better choice |
|---|---|
| One-time file copy | podman cp |
| Retrieve a generated report or log | podman cp |
| Copy while container is stopped | podman cp |
| Bulk filesystem work | podman mount |
| Persistent application data across restarts | Podman volumes |
| Continuously expose a host directory | Bind mount vs volume |
podman cp is for transfers, not for application storage design. If the same path must stay shared across restarts, use a volume or bind mount at podman run time instead of copying on every deploy.
Common podman cp errors
| Symptom | Likely cause | Fix |
|---|---|---|
| Path could not be found on container | Missing parent directory or wrong path | podman exec cp-demo mkdir -p /parent/sub then retry |
| Destination must be a directory when copying a directory | Target is a regular file | Remove the file or choose a directory destination |
*.ext could not be found |
Glob not expanded by podman cp |
Copy the directory or use tar streaming |
| Ownership looks wrong after copy (rootless) | UID/GID mapping between host and container | Check with ls -ln; see user-namespace and volume-permissions guides |
| Colon in local path parsed wrong | container:path syntax ambiguity |
Use ./file:name or an absolute path for host files with colons |
A host file named file:name copies cleanly when you prefix the path:
podman cp "./file:name" cp-demo:/tmp/file-colonReferences
Summary
podman cp copies files and directories between the host and container storage, or between two containers, without starting a new container image. It works while the container is running or stopped because it reads and writes the container layer directly — unlike podman exec, which needs a running process.
Directory semantics are the main footgun: copying container:/path/dir nests the directory under the destination, while container:/path/dir/. copies only the contents. Default --archive behavior on Podman 5.8.2 remaps ownership to the destination container's primary UID when copying in; use --archive=false to preserve the source UID/GID instead.
For globs and large trees, copy a parent directory, pipe tar through podman cp -, or podman mount the container root for direct filesystem access. For data that must survive restarts or stay continuously shared, use volumes or bind mounts at create time — podman cp is the right tool for one-off transfers, not persistent application storage.

