Skip to content

Provision persistent storage

This content is for the v0.1 version. Switch to the latest version for up-to-date documentation.

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, or that you know the StorageClass name to request.

Create the PVC in the same namespace as the application:

how-to/package-stacks/storage-cluster.nix (L19–L29)
instances.storage-example.storage = {
package = packages."persistent-volume-claim";
config = {
size = "5Gi";
accessModes = [ "ReadWriteOnce" ];
};
};
instances.storage-example.worker = {
package = storageApp;
};

View source on GitHub ↗

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 and 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.

Accept storage in the package’s build arguments, then create a PVC mount:

how-to/package-stacks/storage-app.nix (L29–L31)
dataMount = kix.mount.pvc storage {
mountPath = "/var/lib/app";
};

View source on GitHub ↗

The mount uses the resolved claim’s name and carries its dependency edge.

Apply the mount before sealing the workload:

how-to/package-stacks/storage-app.nix (L35–L55)
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;

View source on GitHub ↗

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.

Evaluate the cluster:

Run in kix-examples/
❱ kix check how-to-package-storage
 TOOL         RESULT  DETAILS                                                       
 eval         pass    10 manifests evaluated                                        
 kubeconform  pass    skipped (this validation tool is not yet integrated with Kix) 
 pluto        pass    skipped (this validation tool is not yet integrated with Kix) 
 kyverno      pass    skipped (this validation tool is not yet integrated with Kix) 
 scorecard    pass    0 errors, 9 warnings, 2 info

Inspect the generated claim and workload volume:

Run in kix-examples/ Output excerpt
❱ 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.