Skip to main content

Cloud Storage

Not for databases or stateful engines

Do not run databases, message queues, or any workload with strong durability + locking requirements on cloud object storage. NFSv3 has no real file-locking semantics and orders-of-magnitude higher latency than block storage; engines like Postgres, MySQL, MongoDB, Redis (with persistence), Elasticsearch, Kafka, etc. will silently corrupt or fail under load.

Use block storage (default / managed-csi / EBS / Persistent Disk) for those — it's what they're designed against. See Storage → Picking the Right Storage Type.

Cloud Storage gives a Platform Application a persistent, cloud-provider-backed volume mount. The platform provisions a PersistentVolumeClaim against a per-cluster StorageClass and mounts the resulting volume into your application pods.

Storage is requested under spec.deployment.volumeMounts[].source on the PlatformApplication CRD:

apiVersion: meta.p6m.dev/v1alpha1
kind: PlatformApplication
# ... snip ...
spec:
deployment:
volumeMounts:
- name: uploads
mountPath: /app/uploads
source:
size: 10Gi
storageClassName: azureblob-default
accessModes: [ReadWriteMany]

The fields the operator reads:

FieldRequiredDefaultNotes
sizeyes (provision mode)Triggers PVC provisioning. Without it, the volume is an emptyDir (ephemeral).
storageClassNameyes, on this pagecluster defaultSet explicitly. The cluster default is typically a ReadWriteOnce block-storage class (Managed Disk / EBS / PD) — omitting storageClassName will provision block storage, not blob, and a multi-replica Deployment will be rejected by the RWX CEL rule. For Azure Blob, use azureblob-default.
accessModesno[ReadWriteOnce]Multi-replica Deployments require [ReadWriteMany].

Multi-replica caveat: a Deployment with autoscaling or replicas > 1 backed by a ReadWriteOnce PVC is rejected at admission — the platform's CEL rule blocks it because all pods would deadlock on volume attach. Use [ReadWriteMany] whenever the Deployment can scale beyond one pod.

To list the StorageClasses available on your target cluster, see Find Available StorageClasses on the parent Storage page.

Working with mountPath Locally

The mountPath you set on the PlatformApplication only exists inside the container in the cluster. When you run the app locally (npm run dev, cargo run, dotnet run) there's no PVC and no /app/uploads directory — the path doesn't resolve and writes fail.

Pull the path through an environment variable so the same code reads from a repo-relative directory locally and the PVC mount in the cluster:

# .platform/kubernetes/base/application.yaml
spec:
config:
UPLOADS_DIR: /app/uploads
deployment:
volumeMounts:
- name: uploads
mountPath: /app/uploads
source:
size: 10Gi
storageClassName: azureblob-default
accessModes: [ReadWriteMany]

The platform plumbs spec.config into the container as env vars. Application code reads it with a sensible local default:

const uploadsDir = process.env.UPLOADS_DIR ?? "./uploads";
import os
uploads_dir = os.environ.get("UPLOADS_DIR", "./uploads")

Add the local directory to .gitignore (with a .gitkeep if you want the directory to exist on fresh clones) so per-developer test data doesn't end up in version control.

This pattern keeps the cluster path explicit (no surprises in production) and avoids needing root to create /app/... directories on developer laptops.

By Cloud Provider

The Azure CSI offering is the primary cloud-storage path on the platform today.

AzureBlob — azureblob-default

The default azureblob-default StorageClass is backed by an Azure Blob storage account mounted via the NFSv3 protocol. It is ReadWriteMany-capable, so the same volume can be attached to many pods of a scaled Deployment.

PropertyValue
Provisionerblob.csi.azure.com
ProtocolNFSv3
Account kindStorageV2
SKUStandard_LRS
Reclaim policyRetain
Volume bindingImmediate
Access modes supportedReadWriteMany, ReadWriteOnce
Shared key accessdisabled (allowSharedKeyAccess: false) on the storage account — blocks BlobFuse-style key-based data-plane auth. NFSv3 mounts authenticate at the network layer (storage-firewall VNet rules + AUTH_SYS), not via account keys, so this default doesn't impact the RWX path.

The driver dynamically provisions one storage account per PVC in the cluster's resource group, with hierarchical namespace + NFSv3 enabled. Storage accounts persist across PVC deletion (reclaimPolicy: Retain) — they survive kubectl delete pvc and must be removed manually from Azure if you want to reclaim the cost.

Realistic example

A 3-replica Deployment writing user uploads to an NFSv3-backed shared volume:

# .platform/kubernetes/base/application.yaml
apiVersion: meta.p6m.dev/v1alpha1
kind: PlatformApplication
metadata:
name: my-app
spec:
autoscaling:
enabled: true
minReplicas: 3
maxReplicas: 3
deployment:
# ... snip ...
volumeMounts:
- name: uploads
mountPath: /app/uploads
source:
size: 10Gi
storageClassName: azureblob-default
accessModes: [ReadWriteMany]

What this produces on the cluster:

  • A PersistentVolumeClaim named my-app-uploads in the app's namespace, requesting 10Gi RWX from azureblob-default.
  • A dynamically provisioned Azure Blob storage account in the cluster's resource group (account name has a nfs<random> prefix).
  • All three Deployment pods mount the same volume at /app/uploads, distributed across cluster nodes.

Additional StorageClasses

Clusters may expose additional Azure Blob StorageClasses beyond the default — for example azureblob-standard-lrs or azureblob-premium-zrs. These are per-cluster opt-ins and follow an azureblob-{sku-slug} naming convention. Run kubectl get storageclass against the target cluster to see what's actually available before referencing one.

Caveats

  • reclaimPolicy: Retain means storage survives kubectl delete pvc. Cost-bearing resources (the underlying Azure storage account) stay in the cloud account until manually removed. Useful for safety; surprising if you assume PVC deletion = teardown.
  • accessModes is read from the spec, not the bound PVC. PVC accessModes is immutable once bound. Changing the spec field on an existing app updates admission's view but the underlying volume keeps its original mode — to genuinely change access modes, delete the PVC and recreate the app.
  • Storage account names are not stable. The CSI driver mints a name like nfs<random> per PVC. If you need a stable account name, treat that as out-of-scope for the platform's auto-provisioning and bring your own (separate ticket / pattern).