Use persistent volumes with Cloudfleet on Hetzner

This guide will walk you through the process of creating persistent storage for your applications running on a Cloudfleet Kubernetes Engine (CFKE) cluster with nodes hosted in Hetzner. You’ll learn how to install the Hetzner Cloud CSI Driver and set up a Persistent Volume Claim (PVC) that your pods can use for storing data that needs to persist across container restarts.

Prerequisites

Before getting started, ensure you have:

  • A running CFKE cluster with a fleet already configured for Hetzner
  • Helm installed on your local machine
  • kubectl configured to interact with your cluster

Note: Since you have a fleet configured for Hetzner, the hetzner-secrets secret containing your Hetzner Cloud API token already exists in the cluster. You don’t need to create it again.

If you don’t have a CFKE cluster set up yet, you can follow this tutorial to get started.

For configuring your CFKE cluster with Hetzner, refer to the Cloudfleet documentation.

Step 1: Add the Hetzner Cloud Helm Repository

Add the Hetzner Cloud Helm repository and update it:

helm repo add hcloud https://charts.hetzner.cloud
helm repo update hcloud

Step 2: Install the Hetzner Cloud CSI Driver

The Hetzner Cloud CSI (Container Storage Interface) driver enables Kubernetes to interact with Hetzner Cloud volumes. We’ll use Helm to install it with specific configuration values.

Important: Hetzner Cloud volumes can only be attached to nodes running on Hetzner. Pods using these volumes must be scheduled on Hetzner nodes.

Create a file named hetzner-csi.yaml with the following content:

controller:
    replicaCount: 2
    priorityClassName: "system-node-critical"
    hcloudToken:
        existingSecret:
            name: fleet-1-secrets # Change "fleet-1" with your fleet name
            key: hetzner
    nodeSelector:
        cfke.io/provider: hetzner # This is the node selector that will be used to schedule the controller pods on the Hetzner nodes
    affinity:
        podAntiAffinity:
            preferredDuringSchedulingIgnoredDuringExecution:
                -   weight: 1
                    podAffinityTerm:
                        labelSelector:
                            matchExpressions:
                                -   key: app.kubernetes.io/component
                                    operator: In
                                    values:
                                        - controller
                                -   key: app.kubernetes.io/name
                                    operator: In
                                    values:
                                        - hcloud-csi
                        topologyKey: "kubernetes.io/hostname"

node:
    priorityClassName: "system-node-critical"
    hostNetwork: true
    nodeSelector:
        cfke.io/provider: hetzner

Important configuration notes:

  • The controller uses the existing fleet-1-secrets secret that was created during your Hetzner Fleet configuration. Change the “fleet-1” with the name of your Fleet. You can alternatively inject a new secret for Hetzner CSI driver. Visit here to learn about it.
  • The controller and node components are configured to run only on Hetzner nodes with the appropriate labels.

Now, install the CSI driver using Helm:

helm upgrade --install hcloud-csi hcloud/hcloud-csi -n kube-system --values hetzner-csi.yaml

Step 3: Verify the Installation

Check that the CSI driver pods are running correctly:

kubectl get pods -n kube-system -l app.kubernetes.io/name=hcloud-csi

You should see controller and node pods in a Running state.

Verify that the storage class has been created:

kubectl get storageclass hcloud-volumes

Step 4: Create a Persistent Volume Claim (PVC)

Now that the CSI driver is installed, you can create a PVC that will use Hetzner Cloud volumes for storage.

Create a file named pvc-example.yaml with the following content:

apiVersion: v1
kind: PersistentVolumeClaim
metadata:
    name: csi-pvc
spec:
    accessModes:
        - ReadWriteOnce
    resources:
        requests:
            storage: 10Gi
    storageClassName: hcloud-volumes
---
kind: Pod
apiVersion: v1
metadata:
    name: my-csi-app
spec:
    containers:
        - name: my-frontend
          image: busybox
          volumeMounts:
              - mountPath: "/data"
                name: my-csi-volume
          command: [ "sleep", "1000000" ]
    volumes:
        - name: my-csi-volume
          persistentVolumeClaim:
              claimName: csi-pvc

This manifest creates:

  1. A PVC requesting 10GB of storage from the hcloud-volumes storage class
  2. A pod that mounts this volume at /data

Apply the manifest:

kubectl apply -f pvc-example.yaml

Step 5: Test the Volume

Check that the PVC has been provisioned and bound:

kubectl get pvc csi-pvc

It should show as Bound once the Hetzner volume has been created.

To test the volume, you can write some data to it and verify it persists:

# Create a test file in the volume
kubectl exec my-csi-app -- sh -c "echo 'This data will persist!' > /data/test-file.txt"

# Verify the file exists
kubectl exec my-csi-app -- cat /data/test-file.txt

Troubleshooting

If you encounter issues:

  1. Verify Volume Creation: Check in the Hetzner Cloud Console if volumes are being created

  2. Check PVC Status:

    kubectl describe pvc csi-pvc
    

Conclusion

You’ve successfully set up the Hetzner Cloud CSI driver on your Cloudfleet Kubernetes Engine cluster and created a persistent volume claim. Your applications can now use persistent storage that survives pod restarts and rescheduling, making your workloads more reliable and stateful.

This integration between CFKE and Hetzner Cloud provides a powerful, cost-effective storage solution for your containerized applications.