MCPcopy
hub / github.com/utkuozdemir/pv-migrate

github.com/utkuozdemir/pv-migrate @v3.5.0 sqlite

repository ↗ · DeepWiki ↗ · release v3.5.0 ↗
449 symbols 1,591 edges 70 files 133 documented · 30%
README

pv-migrate

build codecov Go Report Card Latest GitHub release GitHub license GitHub stars GitHub forks GitHub issues GitHub all releases Docker Pulls SSHD Docker Pulls Rsync Docker Pulls

pv-migrate is a CLI tool/kubectl plugin for moving Kubernetes PersistentVolumeClaim data.

Its primary workflow is direct PVC-to-PVC migration, including in-namespace, cross-namespace, and cross-cluster copies. It can also back up PVC data to bucket storage and restore it later using S3-compatible storage, Azure Blob, GCS, or a custom rclone remote.


[!WARNING] Heads up: this is a side project I maintain in my spare time. I might take a long time to look at issues or PRs, or not get to them at all. Sorry in advance, and thanks for understanding.


Demo

pv-migrate demo GIF

Why this exists

On Kubernetes, renaming a resource like a Deployment is usually just a manifest change. Create the same object with a new name or namespace, apply it, and move on.

PVCs are different. The Kubernetes object is only the metadata. The real data lives in the storage backend.

pv-migrate moves that data. It can copy directly between PVCs, or use bucket storage as a backup target or intermediate hop.

Workflows

PVC-to-PVC migration

Copy data directly from one PVC to another. This is the core pv-migrate workflow and uses rsync-based strategies.

$ pv-migrate --source old-pvc --dest new-pvc

See PVC-to-PVC migration for strategies and examples.

Bucket backup and restore

Back up a PVC to object storage and restore it later. Use this for backups, one-off exports, or moves where direct cluster-to-cluster connectivity is awkward.

$ pv-migrate backup \
  --source app-data \
  --backend s3 \
  --bucket pv-backups \
  --name app-data-2026-04-11

$ pv-migrate restore \
  --dest app-data-restore \
  --backend s3 \
  --bucket pv-backups \
  --name app-data-2026-04-11

See bucket backup and restore for backend options, object layout, raw rclone config mode, and permissions caveats.

Use cases

:arrow_right: You have a database that has a PersistentVolumeClaim db-data of size 50Gi.
Your DB grew over time, and you need more space for it.
You cannot resize the PVC because it doesn't support volume expansion.
Create a new, bigger PVC db-data-v2 and use pv-migrate to copy data from db-data to db-data-v2.

:arrow_right: You need to move PersistentVolumeClaim my-pvc from namespace ns-a to namespace ns-b.
Create the PVC with the same name and manifest in ns-b and use pv-migrate to copy its content.

:arrow_right: You are moving from one cloud provider to another, and you need to move the data from one Kubernetes cluster to the other.
Just use pv-migrate to copy the data securely over the internet.

:arrow_right: You need to change the StorageClass of a volume, for instance, from a ReadWriteOnce one (like local-path) to a ReadWriteMany like NFS. As the StorageClass is not editable, you can use pv-migrate to transfer the data from the old PVC to the new one with the desired StorageClass.

:arrow_right: You need to keep a PVC backup in object storage before a risky operation, or to export PVC data out of the cluster for later restore.
Use pv-migrate backup to copy the volume into S3-compatible storage, Azure Blob, or GCS, then pv-migrate restore when you need the data back.

:arrow_right: You want scheduled PVC backups using Kubernetes-native building blocks. Run pv-migrate backup from a CronJob and rely on bucket lifecycle rules or separate automation for retention.

:arrow_right: Direct cluster-to-cluster connectivity is awkward, blocked, or temporary.
Back up the source PVC to a bucket, then restore from that bucket into the destination cluster.

Highlights

  • Supports in-namespace, in-cluster, and cross-cluster migrations
  • Uses rsync over SSH with a freshly generated Ed25519 or RSA key pair each time to securely migrate the files
  • Supports backing up PVC data to and restoring it from S3-compatible, Azure Blob, or GCS bucket storage
  • Supports custom rclone remotes for backup/restore backends
  • Lets you override rendered manifests, including images, affinity, and other Helm values
  • Supports multiple migration strategies and falls back when needed:
  • Mount both PVCs in a single pod (mount)
  • ClusterIP service (clusterip)
  • LoadBalancer service (loadbalancer)
  • NodePort service (nodeport, opt-in)
  • Local port-forward transfer (local, opt-in)
  • Push mode (--rsync-push) for when the source side cannot expose a service, e.g., behind a firewall or NAT
  • Detach mode (--detach) for large transfers, so the job can keep running after the CLI exits
  • Customizable strategy order
  • Supports arm32v7 (Raspberry Pi, etc.), arm64, and amd64
  • Supports completion for popular shells: bash, zsh, fish, powershell

Installation

See docs/install.md for install options and shell completion setup.

Usage

See docs/usage.md for usage guides and command references:

Star history

Star History Chart

Contributing

See CONTRIBUTING for details.

Extension points exported contracts — how you extend this code

Strategy (Interface)
(no doc) [7 implementers]
internal/strategy/strategy.go
LogStreamFunc (FuncType)
(no doc)
internal/progresslog/logger.go
LogStreamFunc (FuncType)
(no doc)
internal/rsync/progress/logger.go
ParseLineFunc (FuncType)
(no doc)
internal/progresslog/logger.go

Core symbols most depended-on inside this repo

Run
called by 69
internal/strategy/strategy.go
New
called by 41
internal/pvc/info.go
GenerateConfig
called by 12
internal/rclone/config.go
RunBackup
called by 12
pvmigrate/backup.go
getMergedHelmValues
called by 11
internal/strategy/strategy.go
logClose
called by 11
internal/strategy/local.go
GetClusterClient
called by 10
internal/k8s/client.go
cannotDoReason
called by 9
internal/strategy/mount.go

Shape

Function 366
Struct 41
Method 33
FuncType 6
TypeAlias 2
Interface 1

Languages

Go100%

Modules by API surface

integration/integration_test.go39 symbols
integration/backup_test.go22 symbols
internal/bucketstorage/bucketstorage.go20 symbols
internal/strategy/mount_test.go18 symbols
internal/migrator/migrator.go15 symbols
internal/strategy/topology.go14 symbols
internal/strategy/local.go14 symbols
internal/progresslog/logger.go14 symbols
internal/migrator/migrator_test.go14 symbols
internal/app/backup.go14 symbols
internal/rclone/config_test.go13 symbols
internal/rclone/cmd_test.go12 symbols

Dependencies from manifests, versioned

dario.cat/mergov1.0.2 · 1×
github.com/AdaLogics/go-fuzz-headersv0.0.0-2024080614160 · 1×
github.com/Azure/go-ansitermv0.0.0-2025010203350 · 1×
github.com/MakeNowJust/heredocv1.0.0 · 1×
github.com/Masterminds/goutilsv1.1.1 · 1×
github.com/Masterminds/semver/v3v3.5.0 · 1×
github.com/ProtonMail/go-cryptov1.4.1 · 1×
github.com/asaskevich/govalidatorv0.0.0-2023030114320 · 1×
github.com/blang/semver/v4v4.0.0 · 1×

For agents

$ claude mcp add pv-migrate \
  -- python -m otcore.mcp_server <graph>

⬇ download graph artifact