Copy Files To and From Podman Containers with `podman cp`

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:

text
podman cp [options] [container:]SOURCE [container:]DESTINATION

Supported directions:

text
host → container
container → host
container → container

podman 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:

bash
podman run -d --name cp-demo registry.access.redhat.com/ubi9/ubi-minimal sleep 3600

Copy a file from the host to a container

Prepare a file on the host:

bash
echo "example content from host" > example.txt

Copy it into the container:

bash
podman cp ./example.txt cp-demo:/tmp/example.txt

Paths 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:

bash
podman exec cp-demo cat /tmp/example.txt

Sample output:

output
example content from host

The 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:

bash
podman cp cp-demo:/etc/os-release ./os-release

Read it on the host (relative paths resolve from your current working directory):

bash
head -3 ./os-release

Sample output:

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:

bash
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:

bash
podman cp cp-demo:/data/dir1 ./dest-dir/

Sample resulting path:

output
./dest-dir/dir1/a.txt

Copy only the contents by appending /. to the source path:

bash
podman cp cp-demo:/data/dir1/. ./dest-contents/

Sample resulting path:

output
./dest-contents/a.txt

The 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:

bash
podman stop cp-demo

Push a file while nothing is running inside the namespace:

bash
echo "stopped config" > config.conf

Copy it into stopped storage:

bash
podman cp ./config.conf cp-demo:/tmp/config.conf

Pull a file back out the same way:

bash
podman cp cp-demo:/tmp/config.conf ./config-from-stopped.conf

Confirm the round trip on the host:

bash
cat ./config-from-stopped.conf

Sample output:

output
stopped config

podman 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:

bash
podman start cp-demo

Copy 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:

bash
podman run -d --name cp-a registry.access.redhat.com/ubi9/ubi-minimal sleep 3600

Create a second container for the destination:

bash
podman run -d --name cp-b registry.access.redhat.com/ubi9/ubi-minimal sleep 3600

Seed a file in the source container:

bash
podman exec cp-a sh -c 'echo cross-container > /tmp/file.txt'

Copy directly into the second container:

bash
podman cp cp-a:/tmp/file.txt cp-b:/tmp/

Verify on the destination:

bash
podman exec cp-b cat /tmp/file.txt

Sample output:

output
cross-container

Understand 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:

bash
podman cp cp-demo:/tmp/example.txt ./target.txt

A missing parent directory for a file destination fails — Podman reports the full path as not found:

bash
podman cp ./example.txt cp-demo:/tmp/noparent/sub/example.txt

Sample output:

output
Error: "/tmp/noparent/sub/example.txt" could not be found on container cp-demo: no such file or directory

Create /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:

bash
echo single-file > ./single-file

Attempting to copy a directory onto it fails:

bash
podman cp cp-demo:/data/dir1 ./single-file

Sample output:

output
Error: destination must be a directory when copying a directory

Preserve 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:

bash
echo host-owned > hostfile.txt && chown 1000:1000 hostfile.txt

Copy with default archive behavior:

bash
podman cp hostfile.txt cp-demo:/tmp/hostfile.txt

Inside the container the file is owned by root:

bash
podman exec cp-demo ls -ln /tmp/hostfile.txt

Sample output:

output
-rw-r--r--. 1 0 0 11 Aug 22 16:03 /tmp/hostfile.txt

Use --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:

bash
podman cp --archive=false hostfile.txt cp-demo:/tmp/hostfile-noarch.txt

Ownership should match the source file:

bash
podman exec cp-demo ls -ln /tmp/hostfile-noarch.txt

Sample output:

output
-rw-r--r--. 1 1000 1000 11 Aug 22 16:03 /tmp/hostfile-noarch.txt

Rootless 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:

bash
podman cp cp-demo:/tmp/*.log ./

Sample output:

output
Error: "/tmp/*.log" could not be found on container cp-demo: no such file or directory

Podman 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 tar there
  • Stream a tar archive through podman cp - (next section)
  • Use podman mount for 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:

bash
echo stream-content > stream.txt

Ensure the destination directory exists inside the container:

bash
podman exec cp-demo mkdir -p /tmp/stream-dir

Pipe a tar archive on stdin:

bash
tar cf - -C . stream.txt | podman cp - cp-demo:/tmp/stream-dir/

Read back the extracted file:

bash
podman exec cp-demo cat /tmp/stream-dir/stream.txt

Sample output:

output
stream-content

Stream from a container directory to stdout and list members with tar:

bash
podman cp cp-demo:/data/dir1 - | tar tf -

Sample output:

output
dir1/
dir1/a.txt

This 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:

bash
podman mount cp-demo

Sample output:

output
/var/lib/containers/storage/overlay/26494a182718d7d52b8548a551044f2376128386d7a714474658a900bbdb757e/merged

Operate on files under that path (substitute your mount output):

bash
ls "$(podman mount cp-demo)/tmp/example.txt"

Unmount when finished:

bash
podman umount cp-demo

With 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:

bash
podman cp "./file:name" cp-demo:/tmp/file-colon

References


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.

Deepak Prasad

R&D Engineer

Founder of GoLinuxCloud with more than 15 years of expertise in Linux, Python, Go, Laravel, DevOps, Kubernetes, Git, Shell scripting, OpenShift, AWS, Networking, and Security. With extensive experience, he excels across development, DevOps, networking, and security, delivering robust and efficient solutions for diverse projects.

  • Go (programming language)
  • Python (programming language)
  • DevOps
  • Computer Security
  • Cloud Computing
  • Kubernetes
  • Linux
  • Ansible (software)