| 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.
Frequently Asked Questions
Does podman cp work on stopped containers?
Yes. podman cp reads and writes container storage directly, so the main process does not need to be running. That is different from podman exec, which requires a running container. Start the container again only when you need a process inside it, not merely to copy files.
What is the difference between copying a directory and copying with /. at the end?
podman cp container:/path/dir ./dest copies the directory itself, so you get ./dest/dir. podman cp container:/path/dir/. ./dest copies only the contents, so files land directly under ./dest without an extra directory level. The trailing /. is the usual pattern when you want contents only.
Why does podman cp fail with glob patterns like /tmp/*.log?
podman cp does not expand shell globs on the container side. The asterisk is passed literally, so Podman looks for a file literally named *.log. Copy the parent directory, use podman exec with a shell, or stream a tar archive instead.
When should I use podman cp instead of a volume?
Use podman cp for one-time transfers such as dropping a config file in, pulling a generated report out, or copying while a container is stopped. Use a volume or bind mount when the application needs persistent or continuously shared data across restarts.
What does podman cp --archive do?
On Podman 5.8.2, --archive defaults to true and adjusts ownership to the destination container primary UID and GID when copying into a container. Pass --archive=false to preserve the source ownership from the host or source container. Rootless UID mapping can still make ownership look unexpected on either side.

