
Since this summer, it’s possible to create encrypted OVHcloud Block Storage with OMK (OVHcloud managed key) in RBX, SBG, Paris & BHS regions. More regions will come in the coming months 💪.
And the good news is that you can use encrypted Block Storage using Persistent Volumes
in your OVHcloud Managed Kubernetes Service (MKS) clusters 🎉.
In this post, we’ll show you how to encrypt persistent volumes on an OVHcloud Managed Kubernetes (MKS) cluster using a csi-cinder-high-speed-gen2-luks
Storage Class
. Leveraging LUKS-based encryption at the storage layer, you’ll learn how to protect your data at rest without sacrificing the performance of NVMe-backed volumes.
We’ll guide you step by step: defining the Storage Class
, creating a Persistent Volume Claim
(PVC), and deploying a Pod
that mounts the encrypted volume.
This practical walkthrough is designed for developers and platform engineers looking to secure their Kubernetes workloads on OVHcloud in a straightforward way.
How to
You will create a Persistent Volume Claim
(PVC), linked to a Storage Class
, that will automatically create a Persistent Volume
(PV) that will automatically create an associated encrypted Public Cloud Block Storage volume.
Then you will create a Pod
attached to the PVC
.

Let’s create an encrypted Persistent Volume in our OVHcloud MKS cluster
Prerequisite: Have an OVHcloud MKS cluster.
First, create a csi-cinder-high-speed-gen2-luks.yaml
file with the following content:
💡 Note that if you deploy in on a MKS 1AZ cluster (instead of my 3AZ MKS cluster), you should define the volumeBindingMode
to Immediate
instead.
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: csi-cinder-high-speed-gen2-luks
allowVolumeExpansion: true
parameters:
fsType: ext4
type: high-speed-gen2-luks
provisioner: cinder.csi.openstack.org
reclaimPolicy: Delete
volumeBindingMode: WaitForFirstConsumer
This StorageClass is using the same configuration as existing csi-cinder-high-speed-gen2
but with the high-speed-gen2-luks
type.
So the result will be the usage of SSD disks with NVMe interfaces encrypted with LUKS (Linux Unified Key Setup) which is a standard on-disk format for hard disk encryption.
Apply the manifest file:
kubectl apply -f csi-cinder-high-speed-gen2-luks.yaml
⚠️ You can’t modify the volumeBindingMode
value for an existing Storage Class
, you have to delete it and create a new one.
List the Storage Class
es in the cluster:
$ kubectl get sc
NAME PROVISIONER RECLAIMPOLICY VOLUMEBINDINGMODE ALLOWVOLUMEEXPANSION AGE
csi-cinder-high-speed (default) cinder.csi.openstack.org Delete WaitForFirstConsumer true 33d
csi-cinder-high-speed-gen-2 cinder.csi.openstack.org Delete WaitForFirstConsumer true 33d
csi-cinder-high-speed-gen2-luks cinder.csi.openstack.org Delete WaitForFirstConsumer true 4s
Create a pvc-luks.yaml
file with the following content:
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: pvc-luks
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 10Gi
storageClassName: csi-cinder-high-speed-gen2-luks
Create a new namespace and apply the manifest file into it:
kubectl create ns test-pvc-luks
kubectl apply -f pvc-luks.yaml -n test-pvc-luks
Check the status of our newly created PVC
:
$ kubectl get pvc -n test-pvc-luks
NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS VOLUMEATTRIBUTESCLASS AGE
pvc-luks Pending csi-cinder-high-speed-gen2-luks <unset> 3s
$ kubectl describe pvc pvc-luks test-pvc-luks
Name: pvc-luks
Namespace: test-pvc-luks
StorageClass: csi-cinder-high-speed-gen2-luks
Status: Pending
Volume:
Labels: <none>
Annotations: <none>
Finalizers: [kubernetes.io/pvc-protection]
Capacity:
Access Modes:
VolumeMode: Filesystem
Used By: <none>
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal WaitForFirstConsumer 10s (x2 over 10s) persistentvolume-controller waiting for first consumer to be created before binding
$ kubectl describe pvc pvc-luks
Name: pvc-luks
Namespace: test-pvc-luks
StorageClass: csi-cinder-high-speed-gen2-luks
Status: Pending
Volume:
Labels: <none>
Annotations: <none>
Finalizers: [kubernetes.io/pvc-protection]
Capacity:
Access Modes:
VolumeMode: Filesystem
Used By: <none>
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal WaitForFirstConsumer 10s (x2 over 10s) persistentvolume-controller waiting for first consumer to be created before binding
As you can see, your PVC
have been creating, with the luks Storage Class
, and is Pending to be Bound, until the creation of a Pod
with a volume (because of the WaitForFirstConsumer
value):
Create a pod.yaml
file with the following content:
apiVersion: v1
kind: Pod
metadata:
name: pod-with-encrypted-volume
spec:
containers:
- name: nginx
image: nginx
volumeMounts:
- mountPath: "/usr/share/nginx/html"
name: encrypted-volume
volumes:
- name: encrypted-volume
persistentVolumeClaim:
claimName: pvc-luks
Create a new namespace
and apply the manifest file into it:
kubectl apply -f pod.yaml -n test-pvc-luks
The PVC
should now be Bound and a new PV
should be created:
$ kubectl get pvc -n test-pvc-luks
NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS VOLUMEATTRIBUTESCLASS AGE
pvc-luks Bound ovh-managed-kubernetes-siti343p-pvc-3a3b1d2e-ebdf-41a2-8f8f-4ee6984b6149 10Gi RWO csi-cinder-high-speed-gen2-luks <unset> 3m27s
$ kubectl get pv -n test-pvc-luks
NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS VOLUMEATTRIBUTESCLASS REASON AGE
ovh-managed-kubernetes-siti343p-pvc-3a3b1d2e-ebdf-41a2-8f8f-4ee6984b6149 10Gi RWO Delete Bound test-pvc-luks/pvc-luks csi-cinder-high-speed-gen2-luks <unset> 32s
First the Pod
should be in ContainerCreating
state (waiting the creation and the attachment of the volume) and after few seconds it will be Running:
$ kubectl get pod pod-with-encrypted-volume -n test-pvc-luks
NAME READY STATUS RESTARTS AGE
pod-with-encrypted-volume 0/1 ContainerCreating 0 44s
# Wait a little...
$ kubectl get pod pod-with-encrypted-volume -n test-pvc-luks
NAME READY STATUS RESTARTS AGE
pod-with-encrypted-volume 1/1 Running 0 2m10s
The Pod
is now created with an attached volume:
$ kubectl describe pod pod-with-encrypted-volume -n test-pvc-luks
Name: pod-with-encrypted-volume
Namespace: test-pvc-luks
Priority: 0
Service Account: default
Node: my-pool-zone-c-h5xjf-7n7kt/192.168.142.174
Start Time: Tue, 19 Aug 2025 10:10:41 +0200
Labels: <none>
Annotations: <none>
Status: Running
IP: 10.240.0.203
IPs:
IP: 10.240.0.203
Containers:
nginx:
Container ID: containerd://c38c0a0e19970503ad1bfaa0c74b5cc320cb9df08456c7613b9a9a8c908b9190
Image: nginx
Image ID: docker.io/library/nginx@sha256:33e0bbc7ca9ecf108140af6288c7c9d1ecc77548cbfd3952fd8466a75edefe57
Port: <none>
Host Port: <none>
State: Running
Started: Tue, 19 Aug 2025 10:11:42 +0200
Ready: True
Restart Count: 0
Environment: <none>
Mounts:
/usr/share/nginx/html from encrypted-volume (rw)
/var/run/secrets/kubernetes.io/serviceaccount from kube-api-access-vbcnk (ro)
Conditions:
Type Status
PodReadyToStartContainers True
Initialized True
Ready True
ContainersReady True
PodScheduled True
Volumes:
encrypted-volume:
Type: PersistentVolumeClaim (a reference to a PersistentVolumeClaim in the same namespace)
ClaimName: pvc-luks
ReadOnly: false
kube-api-access-vbcnk:
Type: Projected (a volume that contains injected data from multiple sources)
TokenExpirationSeconds: 3607
ConfigMapName: kube-root-ca.crt
ConfigMapOptional: <nil>
DownwardAPI: true
QoS Class: BestEffort
Node-Selectors: <none>
Tolerations: node.kubernetes.io/not-ready:NoExecute op=Exists for 300s
node.kubernetes.io/unreachable:NoExecute op=Exists for 300s
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal Scheduled 3m48s default-scheduler Successfully assigned test-pvc-luks/pod-with-encrypted-volume to my-pool-zone-c-h5xjf-7n7kt
Warning FailedAttachVolume 3m25s (x6 over 3m43s) attachdetach-controller AttachVolume.Attach failed for volume "ovh-managed-kubernetes-siti343p-pvc-3a3b1d2e-ebdf-41a2-8f8f-4ee6984b6149" : rpc error: code = Internal desc = [ControllerPublishVolume] Attach Volume failed with error failed to attach b76d1025-9473-4050-86be-4880f0f625cb volume to 516c41cf-9637-4b08-a75e-1d265d1773f4 compute: Bad request with: [POST https://compute.eu-west-par.cloud.ovh.net/v2.1/a212a1e43b614c4ba27a247b890fcf59/servers/516c41cf-9637-4b08-a75e-1d265d1773f4/os-volume_attachments], error message: {"badRequest": {"code": 400, "message": "Invalid input received: Invalid volume: Volume b76d1025-9473-4050-86be-4880f0f625cb status must be available or downloading to reserve, but the current status is creating. (HTTP 400) (Request-ID: req-e94505fd-39d6-496c-bc6d-275cd2604dda)"}}
Normal SuccessfulAttachVolume 3m8s attachdetach-controller AttachVolume.Attach succeeded for volume "ovh-managed-kubernetes-siti343p-pvc-3a3b1d2e-ebdf-41a2-8f8f-4ee6984b6149"
Normal Pulling 2m53s kubelet Pulling image "nginx"
Normal Pulled 2m48s kubelet Successfully pulled image "nginx" in 5.072s (5.072s including waiting). Image size: 72324501 bytes.
Normal Created 2m48s kubelet Created container: nginx
Normal Started 2m48s kubelet Started container nginx
Logging in the OVHcloud Control Panel, you can see that the encrypted volume have been successfully created:

Finally, you can use your volume.
Execute a shell in the Nginx Pod
and create an index.html
file into it:
$ kubectl exec -it pod-with-encrypted-volume -n test-pvc-luks -- /bin/bash
root@pod-with-encrypted-volume:/# echo "Hello from OVHcloud encrypted Block Storage!" > /usr/share/nginx/html/index.html
And curl the webserver:
root@pod-with-encrypted-volume:/# apt update
root@pod-with-encrypted-volume:/# apt install curl
root@pod-with-encrypted-volume:/# curl http://localhost/
Hello from OVHcloud encrypted Block Storage!
🎉
What’s next?
In this blog post we saw a basic (but concrete) usage of the encrypted Persistent Volume
on OVHcloud Kubernetes clusters that just bee released, don’t hesitate to think about it for your sensitive data.
In the coming months, the encrypted Block Storage will be available worldwide. Follow the Encrypted Block Volumes issue on GitHub to stay informed.
And don’t hesitate to take a look to our Cloud Roadmap & Changelog to see the state of all of the coming features in OVHcloud Public Cloud products.
Developer Advocate at OVHcloud, specialized in Cloud Native and Infrastructure as Code (IaC).
She is Docker Captain, CNCF ambassador, GDE, Women techmakers Ambassador & GitPod Hero. She has been working as a Developer and Ops for over 20 years. Cloud enthusiast and advocates DevOps/Cloud/Golang best practices.
Conferences and meetups organizer since 2016. Technical writer, a book author & reviewer, a sketchnoter and a speaker at international conferences.
Mentor and promote diversity and accessibility in technology.
Book author, she created a new visual way for people to learn and understand Cloud technologies: "Understanding Kubernetes / Docker in a visual way" in sketchnotes, books and videos.