Containerized Data Importer: declarative VM disks on Kubernetes PVCs
Data Import Service for kubernetes, designed with kubevirt in mind.
At a glance
- What is it?
- CDI is a Kubernetes add-on that populates a PersistentVolumeClaim with a virtual machine disk image or other data, through a DataVolume custom resource that tracks the population as a pod. It is the storage layer KubeVirt depends on, it is storage-device agnostic, and it can be used without KubeVirt at all.
- Who is it for?
- Adopt Containerized Data Importer if you run virtual machines on Kubernetes and need their disks to appear from a declarative source rather than a manual image copy, or if you want a supported way to seed ordinary volumes with data.
- Can I use it commercially?
- Yes. Apache-2.0 is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository last received commits 10 days ago.
- What is it written in?
- Mainly Go, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on October 20, 2026, and from our analysis. They are not legal advice.
Editorial analysis
A PVC that fills itself after creation
Containerized Data Importer, abbreviated CDI, is a persistent storage management add-on for Kubernetes whose primary goal is to provide a declarative way to build virtual machine disks on PVCs for KubeVirt virtual machines.
The problem it solves is narrow and real. A virtual machine needs a disk, and a disk is a large binary blob that has to exist somewhere before the machine boots. In the pre-CDI world that meant someone pushing an image into object storage and hand-editing a volume claim, which is a manual step that does not survive being described as code. CDI makes the disk a declarative object, so the same thing that launches the virtual machine also describes where its disk comes from.
The mechanism is a pod. When you create a DataVolume, CDI runs an importer that populates the volume, and the DataVolume tracks the state of that population. The data can come from a URL, a container registry, another PVC through a clone, or an upload from a client, and there are additional sources for specific hypervisor formats.
Two properties are worth holding onto. First, CDI works with standard core Kubernetes resources and is storage device agnostic, so it does not care which storage class backs the claim. Second, while its primary focus is building disk images for KubeVirt, the documentation states it is also useful outside a KubeVirt context for initializing Kubernetes volumes with data. The same importer machinery is useful for seeding a volume with a config file or a dataset, not just a disk image.
The audience is platform engineers running virtual machines on Kubernetes, and secondarily anyone who wants a declarative, retryable way to fill a volume.
DataVolume over a plain PVC, and why it is preferred
CDI includes a CustomResourceDefinition that provides an object of type DataVolume. The DataVolume is an abstraction on top of the standard Kubernetes PVC and can be used to automate creation and population of a PVC with data.
You can use PVCs directly with CDI, and the documentation is explicit that DataVolumes are the preferred method, for three stated reasons: they offer full functionality, a stable API, and better integration with KubeVirt.
That is a real architectural commitment rather than a stylistic preference. A DataVolume is a first-class API object with a lifecycle, and it exists in the cluster as a thing you can read and watch. A PVC being populated by a sidecar pod is a convention that any tool can attempt; a DataVolume is a resource with its own status. For a platform team that wants to answer questions about what is happening to a volume during a rollout, that difference is the whole argument.
The content type is selected by the contentType field in the DataVolume, and kubevirt is the default. CDI only supports certain combinations of source and contentType, and the list is short enough to read aloud: http works with kubevirt and archive, registry works with kubevirt only, pvc is not applicable because the content is cloned rather than interpreted, upload works with kubevirt and archive, imageio works with kubevirt, and vddk works with kubevirt.
That constraint is worth internalising before you design a manifest. The combination list is the contract, and a manifest that pairs the wrong source with the wrong contentType is rejected rather than quietly reinterpreted.
The seven sources and what each one actually does
The source types are where the practical differences lie, and each has a mechanism behind it.
The http source populates the volume using a pod that downloads from the given URL and handles the content according to the contentType setting. Basic authentication can be configured with a secret, and custom TLS certificates can be specified in a ConfigMap.
The registry source populates the volume with a Container Disk downloaded from the given image URL. The only valid contentType for this source is kubevirt, and the image must be a Container Disk.
The pvc source clones another volume. CDI attempts an efficient clone using the storage backend if possible, and otherwise transfers the data to the target PVC over a TLS secured connection between two pods on the cluster network. Whether your clone is a cheap block-level copy or a full network transfer therefore depends on your storage backend, and that difference can be large.
The upload source prepares to receive data from a client machine through an upload proxy, which transits data from an authenticated client to a pod that populates the PVC. You must have a valid UploadToken to send data to the proxy.
The blank source populates a volume with an empty KubeVirt virtual machine disk, and is valid only with the kubevirt contentType. CDI creates a VM disk on the PVC which uses all of the available space.
Two hypervisor-specific sources exist for migrations. The imageio source imports virtual machine disks from a running oVirt installation, using the provided credentials to transfer the indicated disk image securely. The vddk source imports disks from VMware, transferring them using vCenter or ESX API credentials and a user-provided image containing the non-redistributable VDDK library, which is a licensing-driven detail that is easy to miss until the import fails.
For the two content types that carry meaning, the kubevirt type treats the imported data as a VM disk, decompressing and converting the file from qcow2 to raw format if needed and resizing the disk to use all available space. The archive type means the data is a tar archive, though compression is not yet supported for archives. The contents are extracted into the volume and can then be used with a regular pod, or with a virtual machine using KubeVirt's filesystem feature.
Deploying the operator and creating a first DataVolume
Deployment is three kubectl commands, and the version is resolved from the latest release first:
$ export VERSION=$(curl -s https://api.github.com/repos/kubevirt/containerized-data-importer/releases/latest | grep '"tag_name":' | sed -E 's/.*"([^"]+)".*/\1/')
$ kubectl create -f https://github.com/kubevirt/containerized-data-importer/releases/download/$VERSION/cdi-operator.yaml
$ kubectl create -f https://github.com/kubevirt/containerized-data-importer/releases/download/$VERSION/cdi-cr.yamlThe two manifests are the operator and the custom resource definition, which is why both are needed. The operator is what reconciles DataVolume objects into importer pods.
There is one deployment detail that the documentation flags and that you should not skip. The default namespace is used in the document, but in a production setup a protected namespace that is inaccessible to regular users should be used instead. That is a permissions boundary, not a style preference, since the operator creates pods that write into volumes.
Once the operator is running, using it is a single create against an example manifest:
$ kubectl create -f https://raw.githubusercontent.com/kubevirt/containerized-data-importer/$VERSION/manifests/example/import-kubevirt-datavolume.yamlYou should end up with a DataVolume object that progresses through its population and leaves behind a PVC with a disk on it, which is what a KubeVirt virtual machine then references.
The repository has a substantial set of example manifests, and the README points at them as a reference for creating DataVolumes from additional sources such as registries, S3, GCS and your local system. Those are the files to read when you want a source that the main document only mentions in passing.
The Go codebase, the operator shape and the build system
The primary language is Go, and the module path is kubevirt.io/containerized-data-importer, targeting Go 1.26.0. The dependency list is long and tells you what the project actually integrates with.
The cloud storage libraries are the first group, with cloud.google.com/go/storage and the AWS SDK v2 with its S3 service client and credentials package, plus gophercloud for OpenStack and the oVirt Go client for the imageio source. Image handling runs through containers/image/v5, which is what lets the registry source pull a Container Disk.
The Kubernetes side is the second group, and it is the part that shows CDI is built for a real cluster rather than a test harness. There is the CSI external-snapshotter client and the CSI lib-volume-populator, which is how the volume population integrates with the storage ecosystem, plus the volume-data-source-validator client. OpenShift APIs and library-go appear for the operator-framework and operator-lifecycle-manager integration, and the kubevirt monitoring metrics parser is there for the project's own telemetry.
The repository layout matches a Kubernetes project of this kind. There is api/ for the types, cmd/ for the entry points, pkg/ for the implementation, manifests/ with the example manifests, staging/ for the split-out repositories that the Go workspace file references through go.work, hack/ for build scripts, a Makefile, and Bazel files including BUILD.bazel and WORKSPACE. The Makefile shows the build is containerised by default, with DOCKER set to 1 and the entrypoint script hack/build/in-docker.sh used unless you override it.
The releases are v1.66.1 from 2026-09-06, v1.66.0 from 2026-08-05 and v1.65.0 from 2026-03-31, and the last push was on 2026-10-13. A project on a version 1.66 line with monthly minor releases is being maintained on a schedule, and the licence is Apache 2.0 with a LICENSE file in the repository root.
Where the DataVolume model stops working
Three limits are visible in the documentation and they are structural.
The first is the storage backend. The whole model assumes a PVC that an importer pod can write into while the population runs. A storage class that cannot support the required volume mode, or a driver that does not allow a volume to be written and then attached elsewhere, will not work with this flow, and CDI is not in a position to work around that. The efficient clone path for the pvc source is the clearest example, since whether a clone is a cheap block copy or a full network transfer between two pods depends entirely on what the backend can do.
The second is scope. A DataVolume is for getting data into a volume, not for managing it afterwards. There is no backup, no snapshot policy, no lifecycle beyond the population, and no rollback if the wrong image was imported. If you need a volume to be periodically refreshed from a source, or audited after the fact, this is not the tool.
The third is that a DataVolume is a heavyweight object for a small job. Every population runs a pod, and for many use cases an init container that curls a file into an emptyDir is less machinery.
One more practical point about the deployment itself. Because the operator creates pods that write into volumes, and because the documentation recommends a protected namespace, an environment where you cannot create privileged or write-capable pods, or where admission control restricts what the operator may do, is an environment where CDI is harder to run than the API surface suggests.
Editorial conclusion
Adopt Containerized Data Importer if you run virtual machines on Kubernetes and need their disks to appear from a declarative source rather than a manual image copy, or if you want a supported way to seed ordinary volumes with data. Do not adopt it if your storage driver cannot support the required volume modes, since the whole model depends on a PVC that can be written to by an importer pod, and do not reach for it to fetch a single file, where an init container does the same job. Verify first that your storage class can back the volume while the importer writes to it, that the contentType and source combination you intend is one of the supported pairs, and that the operator and CRD are installed into a namespace regular users cannot reach.
Frequently asked questions
What is the Containerized Data Importer used for?
CDI is a persistent storage management add-on for Kubernetes that provides a declarative way to build virtual machine disks on PVCs for KubeVirt virtual machines. It works with standard core Kubernetes resources, is storage device agnostic, and can also be used outside KubeVirt to initialize volumes with data.
What is a DataVolume in CDI?
It is a CustomResourceDefinition provided by CDI, an abstraction on top of the standard Kubernetes PVC that automates creation and population of a PVC with data. The documentation says DataVolumes are preferred over using PVCs directly because they offer full functionality, a stable API, and better integration with KubeVirt.
Which sources can a CDI DataVolume import from?
The supported sources are http, registry, pvc for cloning, upload, blank for an empty KubeVirt disk, imageio for a running oVirt installation, and vddk for VMware disks. Not every source works with every contentType: the README lists the valid combinations explicitly.
How do I deploy the CDI operator?
Resolve the latest release tag into a VERSION variable, then create cdi-operator.yaml and cdi-cr.yaml from that release with kubectl. The documentation notes that while the default namespace is used in the example, a production setup should use a protected namespace that is inaccessible to regular users.
What licence is the Containerized Data Importer under?
Apache License 2.0, with the licence text in the LICENSE file at the root of the repository. The recent releases are v1.66.1 from 2026-09-06, v1.66.0 from 2026-08-05 and v1.65.0 from 2026-03-31, and the last push was on 2026-10-13.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/kubevirt-containerized-data-importer)Was this useful?