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).

graph LR A[Block Storage] --> B[Filesystem Formatted] B --> C[Mounted as Directory] C --> D[Container Accesses Files] style A fill:#e1f5ff style B fill:#fff4e1 style C fill:#e8f5e9 style D fill:#f3e5f5

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.

graph LR A[Block Storage] --> B[Raw Block Device] B --> C[Exposed as Device Node] C --> D[Application Manages Filesystem] style A fill:#e1f5ff style B fill:#fff4e1 style C fill:#e8f5e9 style D fill:#f3e5f5

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

graph TB subgraph filesystem[Filesystem Volume] A[Mounted Directory] --> B[Pre-formatted] B --> C[Standard File APIs] C --> D[Easy to Use] D --> E[File Permissions] end style D fill:#e8f5e9
graph TB subgraph block[Block Volume] F[Raw Device] --> G[No Filesystem] G --> H[Direct Block Access] H --> I[Better Performance] I --> J[App Manages FS] end style I fill:#fff4e1

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:

  1. Less overhead - No filesystem layer between application and storage
  2. Direct I/O - Applications can use direct I/O and bypass page cache
  3. Fewer context switches - More direct path to storage hardware
  4. 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: Block in the PVC
  • Use volumeDevices instead of volumeMounts in the pod
  • Specify devicePath instead of mountPath
  • 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

  1. Use filesystem volumes by default - They’re simpler and sufficient for most use cases
  2. Consider block volumes for databases - If performance is critical, block volumes can help
  3. Test performance - Measure actual performance differences for your workload
  4. Format block devices - Use init containers to format block volumes if needed
  5. Check driver support - Verify your StorageClass/CSI driver supports block volumes
  6. Document your choice - If using block volumes, document why for future maintainers
  7. 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:

  1. Verify volumeMode: Block is set in the PVC
  2. Check if the StorageClass supports block volumes
  3. Verify CSI driver supports block volumes
  4. Check pod events: kubectl describe pod <name>

Device Formatting Issues

If you have issues formatting block devices:

  1. Ensure init container has necessary tools (mkfs.ext4, etc.)
  2. Check device path is correct
  3. Verify device exists before formatting: blkid /dev/xvda
  4. Check init container logs

See Also