Table of Contents

How to Clone a PVC in Kubernetes

Summary: This wiki page shows how to clone a Persistent Volume Claim (PVC) in Kubernetes.
Date: 14 September 2025

When doing an Argo CD sync I got an error on one of our PVCs. For one of our applications we upgraded the storage class but hadn't had the time yet to convert the actual PVC:

one or more objects failed to apply, reason: PersistentVolumeClaim "seq" is invalid: spec: Forbidden: spec is immutable after creation except resources.requests and volumeAttributesClassName for bound claims   core.PersistentVolumeClaimSpec{    ... // 2 identical fields    Resources: {Requests: {s"storage": {i: {...}, Format: "BinarySI"}}},    VolumeName: "pvc-2bc75dce-01e6-4f8b-ae06-3fc6c6657dac", -  StorageClassName: &"default", +  StorageClassName: &"managed-premium",    VolumeMode: &"Filesystem",    DataSource: nil,    ... // 2 identical fields   }

As you can see, the storage class 'default' is changed to 'managed-premium', but unfortunately, this cannot be done online in Kubernetes, as that setting is immutable. Follow the procedure below to use 'korb' to quickly clone the PVC to a new one with the correct storage class.

Current Situation

This is the current pvc manifest:

apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: seq
  namespace: appops
  uid: 33dd11a4-e97e-4ce2-85e5-94efbad9e087
  resourceVersion: '737538059'
  creationTimestamp: '2024-08-07T14:57:02Z'
  labels:
    app: seq
    chart: seq-2024.3.1
    heritage: Helm
    k8slens-edit-resource-version: v1
    release: seq
  annotations:
    argocd.argoproj.io/tracking-id: appops:/PersistentVolumeClaim:appops/seq
    pv.kubernetes.io/bind-completed: 'yes'
    pv.kubernetes.io/bound-by-controller: 'yes'
    volume.beta.kubernetes.io/storage-provisioner: disk.csi.azure.com
    volume.kubernetes.io/selected-node: aks-system-12344567-vmss000000
    volume.kubernetes.io/storage-provisioner: disk.csi.azure.com
  finalizers:
    - kubernetes.io/pvc-protection
  selfLink: /api/v1/namespaces/appops/persistentvolumeclaims/seq
status:
  phase: Bound
  accessModes:
    - ReadWriteOnce
  capacity:
    storage: 1Ti
spec:
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
      storage: 1Ti
  volumeName: pvc-2bc75dce-01e6-4f8b-ae06-3fc6c6657dac
  storageClassName: default
  volumeMode: Filesystem

Note that the disk is 1TB in size. From the monitoring we see that it is 82% full.

Approach

There are several ways to clone a PVC, but I decided to create a quick clone using korb. Korb is a command-line tool to clone Kubernetes Persistent Volume Claims (PVCs) and migrate data between different storage classes. It supports various strategies for copying data, including snapshot-based and copy-twice methods.

Follow the steps below:

Note the following:

Result

apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: seq
  namespace: appops
  uid: 1946a79c-37aa-4f01-8ce0-ed090d2b9b67
  resourceVersion: '743857155'
  creationTimestamp: '2025-09-10T22:24:19Z'
  labels:
    app: seq
    chart: seq-2024.3.1
    heritage: Helm
    k8slens-edit-resource-version: v1
    release: seq
  annotations:
    argocd.argoproj.io/tracking-id: appops:/PersistentVolumeClaim:appops/seq
    pv.kubernetes.io/bind-completed: 'yes'
    pv.kubernetes.io/bound-by-controller: 'yes'
    volume.beta.kubernetes.io/storage-provisioner: disk.csi.azure.com
    volume.kubernetes.io/selected-node: aks-system-12344567-vmss000000
    volume.kubernetes.io/storage-provisioner: disk.csi.azure.com
  finalizers:
    - kubernetes.io/pvc-protection
  selfLink: /api/v1/namespaces/appops/persistentvolumeclaims/seq
status:
  phase: Bound
  accessModes:
    - ReadWriteOnce
  capacity:
    storage: 1Ti
spec:
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
      storage: 1Ti
  volumeName: pvc-1946a79c-37aa-4f01-8ce0-ed090d2b9b67
  storageClassName: managed-premium
  volumeMode: Filesystem

Useful links