Skip to content
CAMPUX Cloud Bootcamp Lab · Class 27d · AKS ← All labs
Hands-On Lab · Beginner
~20 min · Free control plane · Cloud Shell
AKS & kubectl · torn down
Class 27d · Storage

Delete the pod. The file stays.

A pod's own filesystem dies with the pod — that is the entire point of a container being disposable. This lab proves the one exception: a PersistentVolumeClaim backed by an Azure Disk lives on after the pod that mounted it is gone. You write a file, delete the pod on purpose, and watch the replacement read the same file back. Then you prove the limit of that promise — a ReadWriteOnce disk belongs to one node at a time, not one pod, and a second pod elsewhere cannot have it.

Fig. 1 · The claim outlives the pod
A PersistentVolumeClaim on Azure Disk outlives the pod that mounts it — a replacement pod reads back what the deleted pod wrote. PVC Azure Disk pod (v1) writes note.txt pod (v2) reads note.txt same disk, same file
Why

Pods are disposable. Some of what they touch is not.

Delete a pod and its container filesystem goes with it — every log line, every temp file, every write, gone the instant the container stops. That is by design: pods are meant to be replaced, not nursed. But a database, a queue, anything stateful needs the opposite guarantee, and Kubernetes gives it one through a separate object: the PersistentVolumeClaim. A PVC is a request for storage that outlives whatever pod currently has it mounted — on AKS, backed by an Azure Disk, provisioned through the built-in managed-csi storage class. This lab proves the claim directly: write, delete the pod, read it back from the replacement. Then it proves the fine print — an Azure Disk PVC is ReadWriteOnce, mountable read-write by pods on exactly one node at a time, which is why it is the wrong choice the moment you need two pods sharing one volume.

The pod is disposable. The claim is not.

Before you begin — one-time setup

You need a free Azure account and the Azure CLI (az), signed in with az login. First time? The 15-minute Set up your machine page covers the account, the installs, and sign-in. Prefer zero installs? Run everything in Azure Cloud Shell (Bash)az and kubectl are preinstalled and already signed in.

Cost & teardown

This lab assumes the cluster from Your first AKS cluster. If you already tore that one down, Step 1 recreates it with the same command. The smallest Azure Disk this claim can request still bills by the gigabyte-month even after the pod is deleted, until the PVC itself is deleted — the teardown step covers both.

Step 1

The cluster

If you still have the cluster from Your first AKS cluster, skip to get-credentials. Otherwise recreate it with the identical command.

# Windows/Git Bash: stop it mangling /subscriptions/... arguments (harmless on macOS/Linux)
export MSYS_NO_PATHCONV=1

RG="campux-lab-aks-rg"
az group create -n "$RG" -l eastus

# same command as the first-cluster lab: free control plane, one small node
az aks create -n campux-aks -g "$RG" \
  --tier free \
  --node-count 1 \
  --node-vm-size Standard_B2s \
  --enable-managed-identity \
  --generate-ssh-keys

az aks get-credentials -n campux-aks -g "$RG"
kubectl get nodes
kubectl get storageclass
Checkpoint kubectl get storageclass lists managed-csi (marked default) among the built-in classes — AKS installs the Azure Disk CSI driver and this class by default, so nothing extra needs installing before Step 2.
Step 2

A PersistentVolumeClaim, backed by an Azure Disk

Request the smallest disk the class allows and mount it into a pod at /data.

kubectl create namespace storage-demo

cat > pvc.yaml <<'EOF'
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: demo-pvc
  namespace: storage-demo
spec:
  accessModes:
    - ReadWriteOnce
  storageClassName: managed-csi
  resources:
    requests:
      storage: 1Gi
EOF

kubectl apply -f pvc.yaml
kubectl get pvc demo-pvc -n storage-demo -w   # Ctrl-C once STATUS is Bound
Checkpoint A PVC provisions lazily — it stays Pending until a pod actually mounts it, because CSI binds the disk on first use, not on claim creation. Move to the next command and watch it bind.
Step 3

Write a file, then delete the pod

Run a pod that mounts the claim, write a file to it, confirm the claim is now Bound, then delete the pod outright.

cat > pod-v1.yaml <<'EOF'
apiVersion: v1
kind: Pod
metadata:
  name: writer
  namespace: storage-demo
spec:
  containers:
  - name: app
    image: busybox:1.28
    command: ["sh", "-c", "echo 'the disk remembers this' > /data/note.txt && sleep 3600"]
    volumeMounts:
    - name: data
      mountPath: /data
  volumes:
  - name: data
    persistentVolumeClaim:
      claimName: demo-pvc
EOF

kubectl apply -f pod-v1.yaml
kubectl wait --for=condition=Ready pod/writer -n storage-demo --timeout=120s
kubectl get pvc demo-pvc -n storage-demo
kubectl exec writer -n storage-demo -- cat /data/note.txt
Checkpoint demo-pvc now reads STATUS: Bound, and cat prints the disk remembers this. Now delete the pod and confirm it is really gone before moving on.
kubectl delete pod writer -n storage-demo
kubectl get pod writer -n storage-demo   # -> Error from server (NotFound)
Step 4

The replacement reads it back

Start a new pod — different name, does not write anything — that mounts the same claim and reads the file the deleted pod left behind.

cat > pod-v2.yaml <<'EOF'
apiVersion: v1
kind: Pod
metadata:
  name: reader
  namespace: storage-demo
spec:
  containers:
  - name: app
    image: busybox:1.28
    command: ["sh", "-c", "sleep 3600"]
    volumeMounts:
    - name: data
      mountPath: /data
  volumes:
  - name: data
    persistentVolumeClaim:
      claimName: demo-pvc
EOF

kubectl apply -f pod-v2.yaml
kubectl wait --for=condition=Ready pod/reader -n storage-demo --timeout=120s
kubectl exec reader -n storage-demo -- cat /data/note.txt
Checkpoint cat prints the disk remembers this — a pod that never wrote the file reads back exactly what a now-deleted pod wrote. The PVC, and the Azure Disk behind it, never went away; only the pod referencing it did.
What just happened

Deleting a pod removes the pod object and, with it, the ephemeral container filesystem layered on top of its image — but the volume the pod mounted was never part of the pod. A PersistentVolumeClaim is a separate Kubernetes object bound to a PersistentVolume, which the Azure Disk CSI driver backs with a real managed disk in your subscription. The pod spec just points at the claim by name. Delete the pod, the pointer disappears; the disk, and the claim pointing at it, stay exactly where they were. Any future pod that references claimName: demo-pvc gets the same disk, same bytes.

Step 5

ReadWriteOnce means one node, not one pod

With reader still running and holding the claim, try to schedule a second pod that mounts the same PVC.

cat > pod-v3.yaml <<'EOF'
apiVersion: v1
kind: Pod
metadata:
  name: second-reader
  namespace: storage-demo
spec:
  containers:
  - name: app
    image: busybox:1.28
    command: ["sh", "-c", "sleep 3600"]
    volumeMounts:
    - name: data
      mountPath: /data
  volumes:
  - name: data
    persistentVolumeClaim:
      claimName: demo-pvc
EOF

kubectl apply -f pod-v3.yaml
kubectl get pod second-reader -n storage-demo -w   # Ctrl-C once it stops changing
Checkpoint On this lab's single-node cluster, second-reader is scheduled onto the same node as reader — there is no other node to place it on — and Kubernetes lets a ReadWriteOnce volume be mounted by multiple pods as long as they share a node, so it may reach Running. Confirm what ReadWriteOnce actually means with the events, not the pod status: kubectl describe pvc demo-pvc -n storage-demo and kubectl get events -n storage-demo --sort-by=.lastTimestamp show the volume is attached to one node only. On a multi-node cluster, scheduling second-reader onto a different node produces a FailedAttachVolume/FailedMount event, because an Azure Disk can be attached to exactly one node at a time — that is the constraint ReadWriteOnce is naming.
What just happened

ReadWriteOnce is a node-level constraint, not a pod-level one — the access mode governs which nodes may mount the volume, and Azure Disk allows exactly one. Multiple pods on that same node can share it; a pod scheduled to a second node cannot attach it at all, and sits in ContainerCreating until it times out or is rescheduled onto the node that already holds it. The fix for genuinely shared, multi-node storage is a different access mode and a different backing store — Azure Files over ReadWriteMany, not Azure Disk — and it is a real design decision, not a bug to route around.

kubectl delete pod reader second-reader -n storage-demo
Down

Tear it down

Delete the PVC before the namespace — otherwise wait for it to finish reclaiming, since the disk itself keeps billing until it does. Then delete the cluster.

kubectl delete pvc demo-pvc -n storage-demo
kubectl delete namespace storage-demo

az group delete -n campux-lab-aks-rg --yes --no-wait
az group exists -n campux-lab-aks-rg      # -> false once complete
Checkpoint Confirm in the portal that the managed disk backing demo-pvc is gone (search "Disks" in your resource group before it disappears with the group), and that both the cluster's resource group and its auto-managed node group (named like MC_campux-lab-aks-rg_campux-aks_eastus) are gone.
End

What you can now honestly claim

You created a PersistentVolumeClaim backed by a real Azure Disk, wrote a file from one pod, deleted that pod, and read the identical file back from a pod that never wrote it — proof that a PVC's lifetime is independent of any pod that mounts it. You then scheduled a second pod against the same claim and read the exact meaning of ReadWriteOnce off the events, not off a guess: one node, not one pod, and a real limit that decides whether Azure Disk or Azure Files is the right call for a given workload.