Storage and Persistence

Introduction

When I first started working with Kubernetes, I naively thought that storage would work similarly to traditional VM-based deployments. My first production incident taught me otherwise—after a pod restart, all user-uploaded images disappeared because I hadn't configured persistent storage. That painful lesson sent me deep into understanding Kubernetes storage, volumes, and persistence mechanisms.

Over the years, I've designed storage solutions for databases, media processing pipelines, and data analytics platforms. Each use case taught me valuable lessons about volume types, storage classes, and the trade-offs between performance, durability, and cost. In this comprehensive guide, I'll share everything I've learned about managing persistent storage in Kubernetes, from basic concepts to production-ready patterns.

Table of Contents

Understanding Kubernetes Storage

Kubernetes storage is fundamentally different from traditional storage management. The ephemeral nature of containers and pods means that data written to a container's filesystem disappears when the container restarts. This works fine for stateless applications but poses challenges for databases, file servers, and any application that needs to persist data.

The Storage Challenge

The core challenge in Kubernetes is reconciling the dynamic, ephemeral nature of pods with the need for persistent, durable storage. Pods can be rescheduled to different nodes, scaled up or down, or replaced during updates. Your storage solution must handle these scenarios gracefully.

spinner

Storage Layers

Kubernetes storage operates at multiple layers:

  1. Container Layer: Ephemeral storage within containers

  2. Pod Layer: Volumes shared across containers in a pod

  3. Persistent Layer: Durable storage that survives pod restarts

  4. Cluster Layer: Storage provisioning and management

Understanding these layers is crucial for designing robust storage solutions.

Volume Basics

Volumes are the fundamental storage abstraction in Kubernetes. They provide a way for containers to access storage, whether ephemeral or persistent.

EmptyDir Volumes

The simplest volume type is emptyDir, created when a pod is assigned to a node and exists as long as the pod runs on that node. All containers in the pod can read and write to it.

This example shows two containers sharing an emptyDir volume. The content generator writes HTML files that nginx serves.

Testing the shared volume:

EmptyDir with Memory Storage

For high-performance temporary storage, use memory-backed emptyDir:

This creates a tmpfs (RAM-backed) filesystem, perfect for caching scenarios where speed is critical.

HostPath Volumes

hostPath volumes mount a file or directory from the host node's filesystem into a pod. Use these carefully—they create dependencies on specific nodes.

When to use hostPath:

  • Accessing node-level logs or metrics

  • Running DaemonSets that need host access

  • Development and testing (never production for application data)

ConfigMap and Secret Volumes

ConfigMaps and Secrets can be mounted as volumes, making configuration and sensitive data available as files:

Accessing mounted configuration:

Persistent Volumes and Claims

Persistent Volumes (PV) and Persistent Volume Claims (PVC) provide a way to provision and consume durable storage independent of pod lifecycle.

The PV/PVC Model

spinner

This separation of concerns allows administrators to manage storage infrastructure while developers request storage through claims.

Creating a Persistent Volume

Key PV specifications:

  • capacity: Total storage size

  • accessModes: How the volume can be mounted

    • ReadWriteOnce (RWO): Single node read-write

    • ReadOnlyMany (ROX): Multiple nodes read-only

    • ReadWriteMany (RWX): Multiple nodes read-write

  • persistentVolumeReclaimPolicy: What happens when PVC is deleted

    • Retain: Manual reclamation required

    • Delete: Automatically delete storage

    • Recycle: Basic scrub and make available again (deprecated)

  • storageClassName: Links to StorageClass for dynamic provisioning

Creating a Persistent Volume Claim

Using PVC in a pod:

Managing PVCs:

Storage Classes

StorageClasses enable dynamic provisioning of volumes, eliminating the need to pre-create PVs manually.

Dynamic Provisioning Flow

spinner

Creating a Storage Class

StorageClass parameters vary by provisioner:

AWS EBS:

Azure Disk:

GCP Persistent Disk:

Using Dynamic Provisioning

Testing dynamic provisioning:

Volume Expansion

With allowVolumeExpansion: true, you can resize PVCs:

StatefulSets for Stateful Applications

StatefulSets provide stable, persistent identities for pods, essential for stateful applications like databases.

StatefulSet Characteristics

  • Stable, unique pod names (podname-0, podname-1, etc.)

  • Ordered, graceful deployment and scaling

  • Stable network identities via headless service

  • Persistent storage that follows the pod

PostgreSQL StatefulSet Example

Key StatefulSet features:

  • volumeClaimTemplates: Automatically creates PVCs for each pod

  • serviceName: Links to headless service for stable networking

  • Ordered pod names: postgres-0, postgres-1, postgres-2

Managing StatefulSets:

Stable Network Identity

Pods in a StatefulSet get stable DNS names:

Example DNS names:

Testing DNS resolution:

Volume Types and Use Cases

Local Volumes

Local volumes represent storage devices mounted on nodes. They offer better performance than network storage but lack mobility.

Use cases for local volumes:

  • High-performance databases

  • Caching layers

  • Build agents requiring fast I/O

NFS Volumes

NFS provides shared storage accessible from multiple pods simultaneously:

Use cases for NFS:

  • Shared configuration files

  • Media assets accessed by multiple pods

  • Collaborative workspaces

CSI (Container Storage Interface) Volumes

CSI is the modern standard for storage in Kubernetes, supporting many storage providers:

Popular CSI drivers:

  • AWS EBS CSI

  • Azure Disk CSI

  • GCP PD CSI

  • Ceph RBD CSI

  • Longhorn

  • Portworx

Dynamic Provisioning

Setting Default Storage Class

Volume Binding Modes

Immediate binding:

PV is provisioned immediately when PVC is created.

WaitForFirstConsumer:

PV provisioning is delayed until a pod using the PVC is scheduled. This ensures the volume is created in the same availability zone as the pod.

Volume Snapshots

Creating and restoring snapshots:

Backup and Disaster Recovery

Velero for Backup

Velero is the industry-standard tool for backing up Kubernetes resources and persistent volumes:

Manual Backup Strategies

Database-specific backups (PostgreSQL):

Performance Optimization

Choosing the Right Volume Type

Performance characteristics:

Volume Type
IOPS
Throughput
Latency
Use Case

Local SSD

Very High

Very High

Very Low

Databases, caching

Network SSD (Premium)

High

High

Low

General databases

Network HDD

Low

Medium

Medium

Logs, archives

Object Storage

Medium

Very High

Medium

Media, backups

I/O Performance Testing

Volume Performance Tuning

For AWS EBS:

For local volumes with direct I/O:

Security and Access Control

Volume Security Context

Encrypted Volumes

AWS EBS encryption:

Application-level encryption:

Read-Only Volumes

Troubleshooting Storage Issues

Common PVC Issues

PVC stuck in Pending:

Volume mount failures:

Performance Issues

Diagnosing slow I/O:

Volume Not Detaching

Data Corruption

Best Practices

1. Always Use Persistent Storage for Stateful Apps

2. Set Resource Requests and Limits

3. Use Storage Classes for Dynamic Provisioning

4. Implement Backup Strategy

5. Monitor Storage Usage

Monitoring with Prometheus:

6. Use Appropriate Access Modes

7. Set Proper Reclaim Policies

8. Use Volume Snapshots for Backups

9. Implement Storage Quotas

10. Test Disaster Recovery

What I Learned

After years of managing Kubernetes storage in production, here are my key takeaways:

1. Storage is Different: Kubernetes storage requires a mental shift from traditional infrastructure. Embrace dynamic provisioning, understand the PV/PVC model, and design for pod mobility.

2. Data is Sacred: Implement robust backup and disaster recovery from day one. I've seen too many incidents where proper backups were the only thing preventing catastrophic data loss.

3. Performance Matters: Choose appropriate storage types for your workload. Don't use network HDD for databases or local SSDs for shared media files. Understand IOPS, throughput, and latency characteristics.

4. Test Everything: Test volume provisioning, pod rescheduling with volumes attached, backup and restore procedures, and disaster recovery scenarios. Storage failures in production are stressful—preparation reduces that stress.

5. Monitor Relentlessly: Track storage usage, I/O performance, and volume health. Set alerts for approaching capacity limits and performance degradation.

6. Security First: Use encryption at rest, implement proper access controls with fsGroup and runAsUser, and audit who can create and access volumes.

7. StatefulSets for State: Use StatefulSets for stateful applications. The stable network identities and ordered deployments are essential for databases and similar workloads.

8. Cost Awareness: Storage costs add up quickly. Implement quotas, monitor unused volumes, and choose appropriate storage tiers. That 10TB of provisioned but unused SSD storage gets expensive.

9. Understand Your Provisioner: Each cloud provider's CSI driver has different capabilities and limitations. Read the documentation, understand the parameters, and test thoroughly.

10. Plan for Growth: Design your storage architecture to scale. Use StorageClasses that support volume expansion, implement monitoring to forecast capacity needs, and have procedures for migrating to larger volumes.

The difference between a working storage solution and a production-ready one is preparation, testing, and operational discipline. Storage problems often can't be fixed quickly—proper design and robust procedures are your best defense.

Start with simple patterns, use managed storage services when possible, implement comprehensive monitoring and backups, and evolve your storage strategy as your needs grow. Your future self (and your team) will thank you when that inevitable storage incident occurs.

Last updated