# Kubernetes Storage

> Read [`../../references/kubernetes.md`](../../references/kubernetes.md) before changing storage. It provides the kubectl-only boundary, identity labels, PVC naming defaults, and safe mutation loop.

- Skill: `zeabur/kubernetes-storage` (Agent Skill)
- Install (CLI): `npx skillmds@latest add zeabur/kubernetes-storage`
- Raw SKILL.md: https://api.skillmd.com/api/skills/zeabur/kubernetes-storage/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: DevOps & Infra
- Author: zeabur (https://skillmd.com/u/zeabur)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/zeabur/kubernetes-storage

---


Read [`../../references/kubernetes.md`](../../references/kubernetes.md) before changing storage. It provides the kubectl-only boundary, identity labels, PVC naming defaults, and safe mutation loop.

## Establish the volume contract

Require the environment ID, service ID, volume ID for an existing Zeabur-style claim, requested capacity, mount path, and access mode. Inventory claims, storage classes, workload mounts, Pods, and events before modifying anything:

```sh
kubectl -n "$NS" get pvc -l "zeabur_service_id=$ZEABUR_SERVICE_ID"
kubectl -n "$NS" get storageclass
kubectl -n "$NS" describe pvc "$PVC_NAME"
kubectl -n "$NS" get events --sort-by=.lastTimestamp
```

The source backend creates a volume claim named `<volume-id>-service-<service-id>`, labels it with the project, environment, service, and user IDs, requests the declared capacity, and uses `ReadWriteOnce`. Retain that identity when managing existing claims. Never replace a bound claim merely to change its mount path or capacity.

Complete this step only when the claim name, storage class, access mode, current capacity, selected workload, and existing mount name are known.

## Create and mount a PVC

For a new volume, use the cluster's selected StorageClass and a storage request. Use `ReadWriteOnce` for one writable service Pod; choose `ReadWriteMany` only after the StorageClass documents that capability and the workload requires shared writes.

```yaml
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: <volume-id>-service-<service-id>
  namespace: environment-<environment-id>
  labels:
    zeabur_type: user-service
    zeabur_project_id: <project-id>
    zeabur_service_id: <service-id>
    zeabur_environment_id: <environment-id>
    zeabur_user_id: <user-id>
spec:
  storageClassName: <storage-class>
  accessModes: [ReadWriteOnce]
  resources:
    requests:
      storage: 10Gi
```

Mount the claim through a minimal Deployment fragment using a stable volume name and the target container name:

```yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: <workload>
  namespace: environment-<environment-id>
spec:
  template:
    spec:
      volumes:
        - name: data
          persistentVolumeClaim:
            claimName: <volume-id>-service-<service-id>
      containers:
        - name: <container>
          volumeMounts:
            - name: data
              mountPath: /data
```

Apply the claim, wait until `status.phase=Bound`, then apply the workload fragment and wait for rollout. A volume mount change recreates Pods; report that expected restart.

## Resize

Read `allowVolumeExpansion` on the selected StorageClass. Patch only `spec.resources.requests.storage` to a larger quantity; Kubernetes does not support shrinking a PVC in place. Wait for the PVC capacity and any `FileSystemResizePending` condition to settle, then verify the mounted filesystem from the target container.

```sh
kubectl -n "$NS" get pvc "$PVC_NAME" -o wide
kubectl -n "$NS" exec "$POD" -c "$CONTAINER" -- df -h "$MOUNT_PATH"
```

With `ReadWriteOnce`, scaling a service above one replica or running a second writable maintenance Pod can fail on attachment. Preserve availability by using an existing mounted Pod for file work. A maintenance Pod that needs exclusive mounting requires explicit downtime approval, scaling the workload down, verifying termination, doing the work, and restoring the recorded replica count.

## Files on a live mounted volume

Select one Ready Pod by its service label and container name. Confine every operation to the user-approved mount path. Inspect before modifying:

```sh
kubectl -n "$NS" exec "$POD" -c "$CONTAINER" -- sh -c 'df -h "$1" && find "$1" -maxdepth 2 -type f -print' sh "$MOUNT_PATH"
kubectl -n "$NS" cp "$POD:$MOUNT_PATH/relative-file" ./relative-file -c "$CONTAINER"
kubectl -n "$NS" cp ./replacement-file "$POD:$MOUNT_PATH/relative-file" -c "$CONTAINER"
```

`kubectl cp` depends on `tar` in the container. If it is unavailable, use a narrowly scoped `kubectl exec` stream only after confirming the container has compatible tools. Preserve owner and mode expectations; use a separate, explicitly approved maintenance job for recursive ownership changes.

For a write, verify the destination path, record the original checksum and permissions when replacing a file, copy the new data, then verify the new checksum and application health. Never expose Secret-backed files or values in the report.

## Completion

Report PVC name, StorageClass, access mode, requested and bound capacity, workload mount path, and Pod used for verification. A storage task is complete only when the PVC is Bound, the intended workload has rolled out, and `df` or the requested file operation confirms the mounted filesystem reflects the requested state.

