High-Speed File Transfer to and from Kubernetes PVCs
-
While
kubectl cpandkubectl execcan both be used to copy files, performance degrades significantly when the target size is large (e.g., ~100Gi). In such cases, execution becomes drastically slow. -
Additionally, both approaches have limitations: they either require extra tools (such as
tar) to be installed in the container, or they cannot be used at all with minimal base images likedistrolessorscratch. -
Most importantly, these methods do not support concurrent read/write operations - a critical limitation when performance and throughput matter.
- Upload local files or directories to a pod's mounted volume
- Download pod volume files back to local machine
- Safe overwrite protection
- Auto-rename existing remote directories (
pgdata-new-original-YYYY-MM-DD-HHMMSS) - Concurrent file transfer with worker pool
- Preserves directory structure
- Optional automatic
chownof uploaded files inside the pod (via Kubernetes exec API) - Fully based on SFTP + Kubernetes Exec API β no side effects on other pod processes
Youβre running PostgreSQL as a StatefulSet, and you need to restore a database from a basebackup and a WAL archive. If the volume is hostPath-based, this is relatively straightforward - you simply copy the required files onto the target node. But when using CSI-backed volumes (e.g., via a cloud provider), where the PVC is mounted as a block device, the situation becomes more complex. In such cases, conventional tools fall short.
Also - you may want to scale your StatefulSet to zero and back up the PVC contents safely and efficiently - for local testing, migration, or recovery.
Basic Scenarios:
- Download backup from PVC for verification / restore
- Sync files between PVCs and local environment
- Testing PVC mount behavior
- CI/CD pipelines to prepare volume data
kubectl-syncpod upload \
--namespace pgrwl-test \
--pvc postgres-data \
--mount-path=/var/lib/postgresql/data \
--src=backups \
--dst=pgdata-new \
--allow-overwrite \
--owner="999:999"Behavior:
- If
/var/lib/postgresql/data/pgdata-newexists -> it is renamed (safe overwrite) - Contents of
backups/are uploaded into/var/lib/postgresql/data/pgdata-new/ - File ownership is set to
999:999inside the helper pod
kubectl-syncpod download \
--namespace pgrwl-test \
--pvc postgres-data \
--mount-path=/var/lib/postgresql/data \
--src=pgdata-new \
--dst=backups-copyBehavior:
- Contents of
/var/lib/postgresql/data/pgdata-new/are downloaded - Files are written to
./backups-copy/ - Directory structure is preserved
Coming soon, PR is on review
- Install the Krew plugin manager if you havenβt already.
- Run the following command:
kubectl krew install syncpodbrew tap hashmap-kz/homebrew-tap
brew install kubectl-syncpod- Download the latest binary for your platform from the Releases page.
- Place the binary in your system's
PATH(e.g.,/usr/local/bin).
(
set -euo pipefail
OS="$(uname | tr '[:upper:]' '[:lower:]')"
ARCH="$(uname -m | sed -e 's/x86_64/amd64/' -e 's/\(arm\)\(64\)\?.*/\1\2/' -e 's/aarch64$/arm64/')"
TAG="$(curl -s https://api.github.com/repos/hashmap-kz/kubectl-syncpod/releases/latest | jq -r .tag_name)"
curl -L "https://github.com/hashmap-kz/kubectl-syncpod/releases/download/${TAG}/kubectl-syncpod_${TAG}_${OS}_${ARCH}.tar.gz" |
tar -xzf - -C /usr/local/bin && \
chmod +x /usr/local/bin/kubectl-syncpod
)sudo apt update -y && sudo apt install -y curl
curl -LO https://github.com/hashmap-kz/kubectl-syncpod/releases/latest/download/kubectl-syncpod_linux_amd64.deb
sudo dpkg -i kubectl-syncpod_linux_amd64.deb
apk update && apk add --no-cache bash curl
curl -LO https://github.com/hashmap-kz/kubectl-syncpod/releases/latest/download/kubectl-syncpod_linux_amd64.apk
apk add kubectl-syncpod_linux_amd64.apk --allow-untrusted
kubectl-syncpod spins up a temporary helper pod that:
- Mounts your target PVC
- Runs an
sshdserver with an in-memory public key - Listens on a randomized NodePort
- Accepts connections only via a secure, ephemeral SSH private key (never written to disk)
The CLI then:
- Uses an in-memory SFTP client to recursively transfer files
- Skips files that are already present and match by SHA-256
- Cleans up the helper pod and service automatically
| Feature | kubectl cp |
kubectl exec |
kubectl-syncpod (SFTP mode) |
|---|---|---|---|
| Uses sidecar or helper pod | β | β | β |
| Works with PVCs | β Helper pod mounts PVC | ||
Requires tools in container (tar, sh) |
β | β | β (uses sshd in helper pod) |
Supports readOnlyRootFilesystem pods |
β | β | β |
Works on distroless/scratch images |
β | β | β |
| Affects main application container | β | β | β |
| Requires container to run as root | Often yes | Often yes | β or configurable via helper pod spec |
| Safe for production workloads | β (safe for read) | ||
| Auto-cleans after sync | β | β | β |
| Supports concurrent transfers | β | β | β (parallel SFTP workers) |
| Performance on large file trees | π’ Slow | π’ Slow | π Fast (streaming + concurrency) |
This project is licensed under the Apache License 2.0 - see the LICENSE file for details.