How-to guide Package task guides
Provision persistent storage
Create a first-class PVC instance and mount it into an application package.
Provision storage as a persistent-volume-claim instance, then consume that
instance as a package dependency. This keeps the claim visible in the deploy
graph and lets Kix treat it as stateful during change planning.
Kix requires this pattern. Storage size, StorageClass, and lifecycle belong to
the cluster operator, so evaluation rejects application packages that create
their own PersistentVolumeClaims and directs them to use a storage
dependency. Packages that must create claims, such as the canonical
persistent-volume-claim package or a Helm chart with persistence enabled,
need an explicit meta.unsafe = [ "raw-pvc" ] grant. The scorecard reports
every such grant for review.
This guide assumes your target cluster flavor provides a default StorageClass (kind, k3s, GKE, GKE Autopilot, AKS, OVH, and Scaleway do; rke2, kubeadm, talos, openshift, and EKS do not), or that you know the StorageClass name to request.
Add the PVC and application instances
Section titled “Add the PVC and application instances”Create the PVC in the same namespace as the application:
instances.storage-example.storage = { package = packages."persistent-volume-claim"; config = { size = "5Gi"; accessModes = [ "ReadWriteOnce" ]; }; };
instances.storage-example.worker = { package = storageApp; };The PVC package advertises the storage alias. The application’s storage
build argument therefore resolves to the claim in the same namespace.
Set size to a Kubernetes storage quantity such as "5Gi". It is required:
a generic claim cannot know how much its consumer stores. A package that owns
its own claim template, such as a StatefulSet’s volume, may default the size
because it knows what it stores.
Choose access modes supported by the storage provider. If the cluster does not
provide a default through its flavor, or the application needs another class,
set config.storageClassName = "<class-name>" on the PVC instance. Other PVC
spec fields, such as volumeMode, dataSource, selector, and volumeName,
go in config.extraSpec.
When a cluster sets availablePackages and declares no claim, Kix
auto-instantiates persistent-volume-claim for a package’s required storage
dependency. That claim has no size, so the build fails with
The option `instances.<namespace>.persistent-volume-claim.config.size' was accessed but has no value defined
after a [kix] auto-instantiated: persistent-volume-claim (<namespace>) trace.
Declare the claim as a full instance with its size, as in the snippet above.
Create the package mount
Section titled “Create the package mount”Accept storage in the package’s build arguments, then create a PVC mount:
dataMount = kix.mount.pvc storage { mountPath = "/var/lib/app"; };The mount uses the resolved claim’s name and carries its dependency edge.
Apply the mount before sealing the workload:
deployment = { name = scope.instanceName; spec = { replicas = 1; selector.matchLabels = scope.selectorLabels; template.spec.containers = [ { name = "worker"; image = "docker.io/library/busybox:1.37"; command = [ "sh" "-c" "sleep infinity" ]; } ]; }; } |> kix.withMounts [ self.dataMount ] |> scope.mkDeployment;kix.withMounts adds the PVC volume and the matching container mount. Use
kix.withMountsOn when only one container in a multi-container pod should
receive it.
Check the result
Section titled “Check the result”Evaluate the cluster:
❱ kix check how-to-package-storage
TOOL RESULT DETAILS
eval pass 11 manifests evaluated
scorecard pass 0 errors, 9 warnings, 2 info Inspect the generated claim and workload volume:
❱ kix build how-to-package-storage --output json
[
{
"apiVersion": "apps/v1",
"kind": "Deployment",
"metadata": {
"name": "worker",
"namespace": "storage-example"
},
"podSpec": {
"containers": [
{
"name": "worker",
"volumeMounts": [
{
"mountPath": "/var/lib/app",
"name": "storage"
}
]
}
],
"volumes": [
{
"name": "storage",
"persistentVolumeClaim": {
"claimName": "storage"
}
}
]
}
},
{
"apiVersion": "v1",
"kind": "PersistentVolumeClaim",
"metadata": {
"name": "storage",
"namespace": "storage-example"
},
"spec": {
"accessModes": [
"ReadWriteOnce"
],
"resources": {
"requests": {
"storage": "5Gi"
}
},
"storageClassName": "standard"
}
}
] In this kind-based example, the flavor supplies the standard StorageClass.
The PVC requests 5 GiB, and the Deployment mounts it at /var/lib/app.
The kind StorageClass stores data inside the kind node. It is suitable for local testing, but deleting the kind cluster deletes that data. Check the reclaim policy and failure behavior of your real storage provider before using the same configuration for application data.