Articles

Kubernetes v1.37: Storage Version Migration Goes GA – What You Need to Know

Kubernetes v1.37 brings Storage Version Migration (SVM) to General Availability and enables it by default. This outline explains why stale storage versions matter, how the built‑in SVM API works, and how to monitor and bundle migrations with CRD upgrades.

Written by:
APin

Senior Technology Analyst • Verified Expert

More from this author →
Kubernetes v1.37: Storage Version Migration Goes GA – What You Need to Know

Kubernetes v1.37 brings Storage Version Migration (SVM) to General Availability and enables it by default. This outline explains why stale storage versions matter, how the built‑in SVM API works, and how to monitor and bundle migrations with CRD upgrades.

SVM Reaches GA in Kubernetes v1.37

In Kubernetes v1.37, Storage Version Migration (SVM) has reached General Availability (GA), transitioning from an experimental utility to a stable, native component of the control plane. Enabled by default across all clusters, SVM addresses the structural challenge of stale schema representations within the underlying etcd storage.

Kubernetes objects are persisted using a specific storage version. When a Custom Resource Definition (CRD) is updated—such as promoting a schema from v1alpha1 to v1—the system only applies the new version to subsequent write operations. Existing resources remain in their legacy format. Previously, administrators relied on manual kubectl scripting or external tooling to re-serialize these objects. The introduction of the storagemigration.k8s.io/v1 API standardizes this process, ensuring all resources conform to the current storage version defined in the CRD.

Operational Workflow

The SVM controller automates the reconciliation process, reading existing resources and triggering a write-back to the API server to force serialization into the preferred storage version. This mechanism is essential for:

  • Safely dropping deprecated API versions from .status.storedVersions.
  • Applying new encryption-at-rest configurations to existing stored data.
  • Rotating encryption keys across the entire data set.

Implementation Example

To trigger a migration, define a StorageVersionMigration object. This declarative approach allows engineers to bundle migration logic directly into infrastructure-as-code manifests:

apiVersion: storagemigration.k8s.io/v1
kind: StorageVersionMigration
metadata:
  name: example-resource-migration
spec:
  resource:
    group: example.com
    resource: crontabs

Progress can be monitored via the resource status. Once the Succeeded condition is True, administrators can verify that all instances are persisted in the desired schema. If the CRD definition is updated concurrently, the migration should be retried to account for any new objects created during the process before finalize-ing the removal of older stored versions.

Why Stale Storage Versions Are a Problem

In Kubernetes, stored API resources are serialized using a specific schema representation known as the storage version. Because the system requires an active mutation to update these serialized records, simply modifying a CustomResourceDefinition (CRD) to promote a new API version does not automatically re-serialize existing objects. This creates "stale" storage versions, which introduce operational risks when attempting to prune deprecated API versions.

Stale versions typically manifest in the following scenarios:

  • CRD Version Promotion: When dropping older versions (e.g., v1alpha1) in favor of a newer schema (e.g., v1), existing resources stored in the deprecated format prevent the safe removal of that version from the CRD's .status.storedVersions. Attempting to drop support before all objects are re-written can lead to data access failures.
  • Encryption-at-Rest and Key Rotation: When storage encryption is enabled or encryption keys are rotated, the control plane only secures new writes or updates. Existing objects remain unencrypted or continue to use obsolete key material until they are explicitly re-written through the API server.

Historically, managing these discrepancies required manual kubectl intervention or the deployment of out-of-tree controllers, both of which are prone to human error and difficult to audit at scale. With the introduction of the native StorageVersionMigration API (storagemigration.k8s.io/v1), engineers can now declaratively trigger the re-serialization of resources. This controller ensures all objects are persisted using the current cluster-defined storage version.

Recommendation: To maintain cluster hygiene, bundle StorageVersionMigration objects alongside CRD manifest upgrades. Always verify the migration status by checking the Succeeded condition in the object status before removing older versions from the CRD definition. If the .status.storedVersions remains populated after a migration, it indicates a concurrent update occurred, requiring a subsequent migration attempt to safely complete the deprecation process.

How Built‑In Storage Version Migration Works

Storage version migration (SVM) in Kubernetes is a declarative, control‑plane‑driven process that rewrites persisted objects so that they are stored using the API server’s default storage version. The feature graduated to General Availability in v1.37 and is enabled by default, eliminating the need for the out‑of‑tree kube‑storage‑version‑migrator component.

Declarative creation of a StorageVersionMigration object

To start a migration you apply a StorageVersionMigration manifest that references the target API group and resource. The object lives in the built‑in storagemigration.k8s.io/v1 API and is persisted like any other Kubernetes resource.

apiVersion: storagemigration.k8s.io/v1
kind: StorageVersionMigration
metadata:
  name: crontabs-migration
spec:
  resource:
    group: example.com
    resource: crontabs

Applying the manifest (e.g., kubectl apply -f crontabs-migration.yaml) creates a single source of truth for the migration request, which can be version‑controlled alongside other cluster manifests.

Role of the StorageVersionMigrator controller

The control‑plane runs a built‑in StorageVersionMigrator controller that continuously watches for StorageVersionMigration objects. Its responsibilities include:

  • Enumerating all persisted instances of the specified resource across etcd.
  • Fetching each object via the API server, which forces a write‑back using the current default storage version.
  • Updating the status.conditions field of the migration object to reflect progress (Running) and completion (Succeeded).
  • Handling failures by retrying until the object is successfully rewritten or the migration is deleted.

Automatic rewriting to the default storage version

When the controller reads an object, the API server serializes it using the version marked storage: true in the corresponding CustomResourceDefinition (or built‑in API). The write‑back updates the stored representation in etcd, thereby converting stale versions (e.g., v1alpha1 or v1beta1) to the preferred version (v1). After a successful migration, the CRD’s .status.storedVersions should contain only the new version, confirming that no legacy data remains.

Monitoring and verification

Operators can inspect migration status with:

kubectl get storageversionmigration.storagemigration.k8s.io/crontabs-migration -o yaml

A Succeeded condition set to True indicates that every instance has been rewritten. If the .status.storedVersions field does not match the expected list, the migration should be re‑run before deprecating the older API version.

Step‑by‑Step Example: Migrating a Custom Resource

Before initiating a migration, understand that a StorageVersionMigration object tells the built‑in StorageVersionMigrator controller which API group and resource to rewrite to the declared storage version. The controller reads the spec.resource fields, scans the backing datastore, and performs a read‑modify‑write cycle for each instance, ensuring that the object is persisted using the new version schema.

The following manifest demonstrates a migration for a custom resource definition (CRD) named crontabs.example.com. The CRD has been updated so that version v1 is marked as storage: true. The migration object targets the example.com API group and the crontabs resource kind.

apiVersion: storagemigration.k8s.io/v1
kind: StorageVersionMigration
metadata:
  name: crontabs-migration
spec:
  resource:
    group: example.com
    resource: crontabs

Apply the manifest with the standard kubectl command:

kubectl apply -f crontabs-migration.yaml

After submission, the controller updates the Status sub‑resource to reflect progress. You can monitor the migration using:

kubectl get storageversionmigration.storagemigration.k8s.io/crontabs-migration -o yaml

Typical status output includes two conditions:

  • Running – indicates the controller is still rewriting objects.
  • Succeeded – set to true when every stored instance has been persisted with the new storage version.

When Succeeded is true, the CRD’s .status.storedVersions should contain only v1. If older versions remain, the migration must be re‑run after the CRD definition is stable.

Because StorageVersionMigration is a native declarative API, you can bundle it with a CRD upgrade in a single manifest file, for example:

---
apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
  name: crontabs.example.com
spec:
  group: example.com
  versions:
  - name: v1
    storage: true
    served: true
    schema: …
---
apiVersion: storagemigration.k8s.io/v1
kind: StorageVersionMigration
metadata:
  name: crontabs-migration
spec:
  resource:
    group: example.com
    resource: crontabs

By applying this combined manifest, the cluster first registers the new storage version and then automatically rewrites existing objects, eliminating manual kubectl get/replace scripts and reducing the risk of stale data persisting in storage.

Monitoring, Verifying, and Bundling Migrations

Before a CustomResourceDefinition (CRD) can drop an older API version, every persisted object must be rewritten to the new storage version. Kubernetes 1.37 provides a built‑in StorageVersionMigration controller that performs this rewrite automatically. The migration lifecycle is observable through the StorageVersionMigration object’s status.conditions field.

Checking migration status with kubectl

  • Retrieve the full object in YAML to see the condition array:
    kubectl get storageversionmigration.storagemigration.k8s.io/crontabs-migration -o yaml
  • Locate the Running and Succeeded entries under status.conditions. Each condition contains:
    • type: the condition name (e.g., Running, Succeeded)
    • status: "True" or "False"
    • lastUpdateTime: timestamp of the latest change
    • reason: a short machine‑readable explanation

A migration in progress shows Running: "True" and Succeeded: "False". When the controller finishes rewriting all objects, the conditions flip to Running: "False" and Succeeded: "True" with reason: StorageVersionMigrationSucceeded. At that point the storage layer contains only the desired version.

Updating .status.storedVersions in the CRD

After a successful migration, the CRD’s .status.storedVersions field must be reconciled to list only the preferred version. If the field still includes the older version, it indicates that the CRD was modified during migration and a retry is required. The update can be performed manually or as part of the upgrade pipeline:

kubectl patch crd crontabs.example.com \
  --type merge -p '{"status":{"storedVersions":["v1"]}}'

Bundling migrations with CRD upgrades

Because StorageVersionMigration is a native declarative API, it can be co‑located with the CRD manifest. A single multi‑document YAML file can contain both resources, ensuring that the migration is created immediately after the CRD’s storage: true flag is set on the new version:

apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
  name: crontabs.example.com
spec:
  group: example.com
  versions:
  - name: v1
    storage: true
    served: true
    schema: {...}
---
apiVersion: storagemigration.k8s.io/v1
kind: StorageVersionMigration
metadata:
  name: crontabs-migration
spec:
  resource:
    group: example.com
    resource: crontabs

Applying the file with kubectl apply -f creates the upgraded CRD and triggers the migration in a single, auditable operation, simplifying automation and compliance tracking.

APPWORKS ENGINEERING

Looking for Custom Software or AI Solutions?

Appworks Technologies designs, builds, and scales production enterprise platforms, microservices, and AI agent workflows tailored to your business goals.

Editorial Policy & Research Methodology

Our findings are based on rigorous internal research, verified industry benchmarks, and direct technical implementation experience from our enterprise client projects. All statistics and technical claims are reviewed by senior engineers before publication to ensure accuracy, transparency, and helpfulness for our readers.

Have an Idea? we offer services in Lucknow, Bangalore, Delhi NCR and other locations