kubectl cp: Copy Files To and From Pods
kubectl cp copies files and directories between your machine and a container, using tar inside the pod.
For pulling a coverage report out of a CI pod or pushing a fixture in, kubectl cp is the quick path. It relies on tar being present in the container.
What it does
kubectl cp streams files between local paths and a pod:path target. Under the hood it runs tar in the container, so the container image must contain a tar binary. Pod paths use namespace/pod:/path and you select a container with -c.
Common usage
# local -> pod
kubectl cp ./fixtures.json mypod:/tmp/fixtures.json
# pod -> local
kubectl cp mypod:/var/log/app.log ./app.log
# specify namespace and container
kubectl cp data/pod-x:/out/report.xml ./report.xml -c runnerOptions
| Flag / Syntax | What it does |
|---|---|
| <src> <pod>:<path> | Copy a local source into the pod |
| <pod>:<path> <dest> | Copy from the pod to local |
| <ns>/<pod>:<path> | Include the namespace in the target |
| -c, --container | Container in a multi-container pod |
| --retries=<n> | Retry on transient stream errors (newer kubectl) |
In CI
kubectl cp needs tar in the container; distroless or scratch images will not work. Prefer absolute paths and avoid leading / weirdness: a warning about removing a leading slash is normal. For large or critical artifacts, an init/sidecar that writes to a shared volume is more robust than cp.
Common errors in CI
"error: ... tar: not found" or "executable file not found in $PATH" means the container has no tar; bake one in or use a different image. "error: unable to upgrade connection: container not found" means a wrong container name or the pod restarted mid-copy. "lost connection to pod" on big files means the stream timed out; retry or move smaller chunks.
Using this in CI
A runner has no kubeconfig, no cached context, and no interactive auth. Every kubectl invocation in CI needs the context supplied explicitly, and most confusing CI failures here are the command running against the wrong cluster or no cluster at all.
# never rely on the ambient context on a runner
kubectl --context "$KUBE_CONTEXT" -n "$NAMESPACE" get pods
# confirm what you are actually connected to before mutating anything
kubectl config current-context
kubectl cluster-info
# fail fast instead of hanging on an unreachable API server
kubectl --request-timeout=30s get nodes