This guide covers storage configuration options for StackGres clusters, including volume sizing, storage classes, and advanced security settings.
Every SGCluster requires persistent storage for PostgreSQL data. Configure storage in the spec.pods.persistentVolume section:
apiVersion: stackgres.io/v1
kind: SGCluster
metadata:
name: my-cluster
spec:
pods:
persistentVolume:
size: '50Gi'
storageClass: 'fast-ssd'
| Setting | Description |
|---|---|
size |
Volume size (e.g., 10Gi, 100Gi, 1Ti) |
| Setting | Description | Default |
|---|---|---|
storageClass |
Kubernetes StorageClass name | Cluster default |
fsGroupChangePolicy |
Volume permission policy | OnRootMismatch |
volumeAttributesClassName |
Kubernetes VolumeAttributesClass applied to the PVCs | None |
ioLimits |
Per-volume cgroup v2 io.max I/O throttling |
None |
Specify volume size using Kubernetes quantity format:
spec:
pods:
persistentVolume:
size: '100Gi' # 100 Gibibytes
Supported units:
Mi - Mebibytes (1024 KiB)Gi - Gibibytes (1024 MiB)Ti - Tebibytes (1024 GiB)| Workload | Recommended Size | Notes |
|---|---|---|
| Development | 10-50Gi | Minimal testing |
| Small production | 50-200Gi | Light workloads |
| Medium production | 200Gi-1Ti | Standard workloads |
| Large production | 1Ti+ | Heavy workloads, analytics |
Consider:
The storage class determines the underlying storage technology:
spec:
pods:
persistentVolume:
size: '100Gi'
storageClass: 'premium-ssd'
Cloud Providers:
# AWS EBS (gp3)
storageClass: 'gp3'
# GCP Persistent Disk (SSD)
storageClass: 'premium-rwo'
# Azure Managed Disk (Premium SSD)
storageClass: 'managed-premium'
On-premises:
# Local NVMe storage
storageClass: 'local-nvme'
# Ceph RBD
storageClass: 'rook-ceph-block'
# OpenEBS
storageClass: 'openebs-cstor-sparse'
For PostgreSQL workloads, storage classes should support:
ReadWriteOnce access modeThe fsGroupChangePolicy setting controls how Kubernetes handles file ownership when mounting volumes. This affects pod startup time and security.
spec:
pods:
persistentVolume:
size: '100Gi'
fsGroupChangePolicy: 'OnRootMismatch'
| Policy | Description | Use Case |
|---|---|---|
OnRootMismatch |
Only change ownership if root directory permissions don’t match | Recommended - Faster startup, minimal overhead |
Always |
Always recursively change ownership on mount | Strict security, slower startup |
The default and recommended setting. Kubernetes only changes file ownership if the root directory of the volume has incorrect permissions:
fsGroupChangePolicy: 'OnRootMismatch'
Benefits:
Forces Kubernetes to recursively change ownership of all files every time the volume is mounted:
fsGroupChangePolicy: 'Always'
Use when:
Warning: With large data volumes,
Alwayscan significantly increase pod startup time.
| Volume Size | OnRootMismatch Startup |
Always Startup |
|---|---|---|
| 10Gi | ~1 second | 1-5 seconds |
| 100Gi | ~1 second | 10-60 seconds |
| 1Ti | ~1 second | 1-10 minutes |
The difference becomes significant with large volumes or many small files.
The volumeAttributesClassName setting references a Kubernetes VolumeAttributesClass that is applied to the PVCs created for the cluster. This lets you tune mutable volume attributes (for example IOPS or throughput on supporting CSI drivers) without recreating the volume:
spec:
pods:
persistentVolume:
size: '100Gi'
volumeAttributesClassName: 'high-iops'
Note: This requires the Kubernetes VolumeAttributesClass feature to be available in the cluster and a CSI driver that supports the referenced attributes.
On an SGShardedCluster, configure it per cluster type:
apiVersion: stackgres.io/v1beta1
kind: SGShardedCluster
metadata:
name: sharded-cluster
spec:
coordinator:
pods:
persistentVolume:
size: '50Gi'
volumeAttributesClassName: 'high-iops'
workers:
pods:
persistentVolume:
size: '100Gi'
volumeAttributesClassName: 'high-iops'
The ioLimits object sets per-volume cgroup v2 io.max I/O throttling on the data persistent volume. Each field is an optional integer; leave a field empty to remove that particular limit.
Warning: This is an alpha feature.
Warning: Enabling I/O limits requires root permissions on the node. When any
ioLimitsvalue is set, StackGres adds an init container namedsetup-io-limitsto each Pod that runs as root (with only theCHOWNcapability), and both that init container and thecluster-controllercontainer mount the host path/sys/fs/cgroupin order to write theio.maxfile. The nodes must use cgroup v2. On clusters that forbid root containers (for example a restrictive OpenShift SCC) this feature cannot be used.
spec:
pods:
persistentVolume:
size: '100Gi'
ioLimits:
readIops: 10000
writeIops: 8000
readMiBps: 500
writeMiBps: 400
| Field | Description |
|---|---|
readIops |
Maximum read operations per second |
writeIops |
Maximum write operations per second |
readMiBps |
Maximum read throughput in MiB per second |
writeMiBps |
Maximum write throughput in MiB per second |
The same ioLimits object is available on SGShardedCluster.spec.coordinator.pods.persistentVolume and SGShardedCluster.spec.workers.pods.persistentVolume.
If your storage class supports expansion, you can increase volume size:
apiVersion: stackgres.io/v1
kind: SGCluster
metadata:
name: my-cluster
spec:
pods:
persistentVolume:
size: '200Gi' # Increased from 100Gi
kubectl apply -f cluster.yaml
# Monitor PVC status
kubectl get pvc -l stackgres.io/cluster-name=my-cluster -w
Note: Volume expansion may require a pod restart depending on the storage provider.
The primary data volume for PostgreSQL:
spec:
pods:
persistentVolume:
size: '100Gi'
Separate storage for distributed logs:
apiVersion: stackgres.io/v1
kind: SGDistributedLogs
metadata:
name: logs-cluster
spec:
persistentVolume:
size: '50Gi'
storageClass: 'standard'
Configure storage per cluster type (coordinator and worker):
apiVersion: stackgres.io/v1beta1
kind: SGShardedCluster
metadata:
name: sharded-cluster
spec:
coordinator:
pods:
persistentVolume:
size: '50Gi'
workers:
pods:
persistentVolume:
size: '100Gi' # Each worker gets this size
apiVersion: stackgres.io/v1
kind: SGCluster
metadata:
name: dev-cluster
spec:
instances: 1
postgres:
version: '16'
pods:
persistentVolume:
size: '10Gi'
apiVersion: stackgres.io/v1
kind: SGCluster
metadata:
name: prod-cluster
spec:
instances: 3
postgres:
version: '16'
pods:
persistentVolume:
size: '500Gi'
storageClass: 'premium-ssd'
fsGroupChangePolicy: 'OnRootMismatch'
apiVersion: stackgres.io/v1
kind: SGCluster
metadata:
name: secure-cluster
spec:
instances: 3
postgres:
version: '16'
pods:
persistentVolume:
size: '200Gi'
storageClass: 'encrypted-ssd'
fsGroupChangePolicy: 'Always' # Strict ownership enforcement