Block vs Filesystem
Kubernetes supports two ways to expose persistent storage to containers: as a filesystem (mounted directory) or as a raw block device. Understanding the difference helps you choose the right approach for your application’s performance and requirements.
Filesystem Volumes
Filesystem volumes are the most common type. The storage is formatted with a filesystem (like ext4, xfs, or ntfs) and mounted as a directory in the container. Applications interact with the volume using standard file operations (open, read, write, close).
Characteristics:
- Pre-formatted with a filesystem
- Mounted as a directory (e.g.,
/mnt/data) - Applications use standard file APIs
- File permissions and ownership apply
- Most common and easiest to use
Block Volumes
Block volumes expose the raw block device to the container. The storage is not formatted with a filesystem—it’s just raw blocks. Applications must format and manage the filesystem themselves, or use the device directly for database storage, message queues, or other applications that need direct block access.
Characteristics:
- Raw block device (no filesystem)
- Exposed as a device node (e.g.,
/dev/xvda) - Applications format and manage filesystem
- Better performance for certain workloads
- More complex to use
Comparison
When to Use Filesystem Volumes
Use filesystem volumes when:
✅ Standard file operations - Your application uses standard file I/O
✅ Multiple files - You need to store multiple files in a directory structure
✅ File permissions - You need filesystem-level permissions and ownership
✅ Simpler deployment - You want Kubernetes to handle filesystem formatting
✅ Most applications - Works for the vast majority of use cases
Example use cases:
- Web applications storing HTML files
- Application logs
- Configuration files
- User uploads
- Content management systems
When to Use Block Volumes
Use block volumes when:
✅ Maximum performance - You need the highest possible I/O performance
✅ Database storage - Databases like MySQL, PostgreSQL benefit from direct block access
✅ Raw device access - Your application needs direct block device access
✅ Custom filesystem - You need a specific filesystem type or configuration
✅ Message queues - High-performance message queues that manage their own storage
Example use cases:
- MySQL, PostgreSQL, MongoDB data directories
- High-performance databases
- Message queues (Kafka, RabbitMQ)
- Storage-intensive applications
- Applications with custom I/O patterns
Performance Comparison
Block volumes typically provide better performance because:
- Less overhead - No filesystem layer between application and storage
- Direct I/O - Applications can use direct I/O and bypass page cache
- Fewer context switches - More direct path to storage hardware
- Better for random I/O - Databases benefit from direct block access
However, the performance difference may not be significant for all workloads. Filesystem volumes are often sufficient and easier to manage.
Using Filesystem Volumes
Filesystem volumes are the default. Just create a PVC and mount it:
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: fs-pvc
spec:
accessModes:
- ReadWriteOnce
storageClassName: fast-ssd
resources:
requests:
storage: 100Gi
---
apiVersion: v1
kind: Pod
metadata:
name: app-fs
spec:
containers:
- name: app
image: nginx
volumeMounts:
- name: data
mountPath: /mnt/data # Mounted as directory
volumes:
- name: data
persistentVolumeClaim:
claimName: fs-pvc
The volume is automatically formatted and mounted as a directory.
Using Block Volumes
To use a block volume, specify volumeMode: Block in your PVC:
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: block-pvc
spec:
accessModes:
- ReadWriteOnce
volumeMode: Block # Request block device
storageClassName: fast-ssd
resources:
requests:
storage: 100Gi
---
apiVersion: v1
kind: Pod
metadata:
name: app-block
spec:
containers:
- name: app
image: mysql:8.0
volumeDevices: # Use volumeDevices instead of volumeMounts
- name: data
devicePath: /dev/xvda # Raw block device
volumes:
- name: data
persistentVolumeClaim:
claimName: block-pvc
Important differences:
- Use
volumeMode: Blockin the PVC - Use
volumeDevicesinstead ofvolumeMountsin the pod - Specify
devicePathinstead ofmountPath - Application must format the device if needed
Formatting Block Volumes
When using block volumes, you typically need to format them. You can do this in an init container:
apiVersion: v1
kind: Pod
metadata:
name: mysql-block
spec:
initContainers:
- name: format
image: busybox
command: ["/bin/sh", "-c"]
args:
- |
if ! blkid /dev/xvda; then
mkfs.ext4 /dev/xvda
fi
volumeDevices:
- name: data
devicePath: /dev/xvda
containers:
- name: mysql
image: mysql:8.0
volumeDevices:
- name: data
devicePath: /dev/xvda
volumes:
- name: data
persistentVolumeClaim:
claimName: block-pvc
Filesystem vs Block: Database Example
Here’s how you might use each for a MySQL database:
Filesystem volume (simpler):
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: mysql-fs
spec:
accessModes:
- ReadWriteOnce
storageClassName: fast-ssd
resources:
requests:
storage: 100Gi
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: mysql
spec:
template:
spec:
containers:
- name: mysql
image: mysql:8.0
volumeMounts:
- name: data
mountPath: /var/lib/mysql
volumes:
- name: data
persistentVolumeClaim:
claimName: mysql-fs
Block volume (better performance):
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: mysql-block
spec:
accessModes:
- ReadWriteOnce
volumeMode: Block
storageClassName: fast-ssd
resources:
requests:
storage: 100Gi
---
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: mysql
spec:
template:
spec:
initContainers:
- name: format
image: busybox
command: ["/bin/sh", "-c"]
args: ["if ! blkid /dev/xvda; then mkfs.ext4 -F /dev/xvda; fi"]
volumeDevices:
- name: data
devicePath: /dev/xvda
containers:
- name: mysql
image: mysql:8.0
volumeDevices:
- name: data
devicePath: /dev/xvda
volumeClaimTemplates:
- metadata:
name: data
spec:
accessModes:
- ReadWriteOnce
volumeMode: Block
storageClassName: fast-ssd
resources:
requests:
storage: 100Gi
StorageClass Support
Not all StorageClasses support block volumes. Check the CSI driver documentation to see if volumeMode: Block is supported. Most cloud providers (AWS EBS, GCE Persistent Disk, Azure Disk) support both modes.
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: fast-ssd
provisioner: pd.csi.storage.gke.io
parameters:
type: pd-ssd
# Block volumes supported if CSI driver supports it
Best Practices
- Use filesystem volumes by default - They’re simpler and sufficient for most use cases
- Consider block volumes for databases - If performance is critical, block volumes can help
- Test performance - Measure actual performance differences for your workload
- Format block devices - Use init containers to format block volumes if needed
- Check driver support - Verify your StorageClass/CSI driver supports block volumes
- Document your choice - If using block volumes, document why for future maintainers
- Consider complexity - Block volumes add complexity; ensure the performance benefit is worth it
Troubleshooting
Block Volume Not Available
If a block volume isn’t available as a device:
- Verify
volumeMode: Blockis set in the PVC - Check if the StorageClass supports block volumes
- Verify CSI driver supports block volumes
- Check pod events:
kubectl describe pod <name>
Device Formatting Issues
If you have issues formatting block devices:
- Ensure init container has necessary tools (mkfs.ext4, etc.)
- Check device path is correct
- Verify device exists before formatting:
blkid /dev/xvda - Check init container logs
See Also
- PVs & PVCs - Creating and using persistent volumes
- CSI Persistent Volumes - Modern persistent volume interface
- StatefulSets - Managing stateful applications