Customize Storage

How to adjust PersistentVolumeClaim sizes and storage classes for Mailu components.

Problem

You need to customize storage sizes for mailboxes, queues, and other persistent data based on your expected user count, email volume, or available storage classes.

Solution

The MailuChart accepts a storage configuration object that controls PVC sizes and storage classes for each component. Configure a global storage class and override sizes per component.

Understanding Storage Requirements

Different Mailu components have different storage needs:

Component

Purpose

Typical Size

Growth Rate

Dovecot

User mailboxes (largest)

50-500Gi

1-5GB per active user

Postfix

Mail queue

5-10Gi

Temporary, self-cleaning

Rspamd

Spam learning data

5Gi

Slow growth

Admin

DKIM keys, config

5Gi

Minimal growth

ClamAV

Virus signatures

5Gi

Periodic updates

Webdav

Calendars, contacts

5-20Gi

50-200MB per user

Set Global Storage Class

Configure a default storage class for all components:

import { App } from 'cdk8s';
import { MailuChart } from 'cdk8s-mailu';

const app = new App();

new MailuChart(app, 'mailu', {
  namespace: 'mailu',
  domain: 'example.com',
  hostnames: ['mail.example.com'],
  subnet: '10.42.0.0/16',

  // ... other config ...

  storage: {
    storageClass: 'longhorn',  // Applied to all components
  },
});

app.synth();

Common storage classes:

  • longhorn - Longhorn distributed storage (recommended for production)

  • local-path - K3s default local storage (testing only)

  • standard - Cloud provider default

  • gp2, gp3 - AWS EBS

  • pd-standard, pd-ssd - GCP Persistent Disk

Customize Component Storage Sizes

Small Deployment (< 50 users)

Minimal storage footprint for personal or small team use:

storage: {
  storageClass: 'longhorn',

  admin: { size: '5Gi' },      // Default
  postfix: { size: '5Gi' },    // Default
  dovecot: { size: '50Gi' },   // ~1GB per user
  rspamd: { size: '5Gi' },     // Default
},

Medium Deployment (50-500 users)

Scale mailbox storage for more users:

storage: {
  storageClass: 'longhorn',

  admin: { size: '5Gi' },
  postfix: { size: '10Gi' },    // Larger queue for volume
  dovecot: { size: '200Gi' },   // ~4GB per user average
  rspamd: { size: '10Gi' },     // More spam learning data
},

Large Deployment (500+ users)

Production-scale storage with capacity planning:

storage: {
  storageClass: 'longhorn',

  admin: { size: '10Gi' },
  postfix: { size: '20Gi' },
  dovecot: { size: '1000Gi' },  // Plan for 2GB per user
  rspamd: { size: '20Gi' },
},

Override Storage Class Per Component

Use different storage classes for different performance tiers:

storage: {
  // Default storage class (standard tier)
  storageClass: 'longhorn',

  // High-performance storage for mailboxes (SSD-backed)
  dovecot: {
    size: '200Gi',
    storageClass: 'longhorn-fast',  // Override with faster class
  },

  // Standard storage for queues and caches
  postfix: { size: '10Gi' },  // Uses default 'longhorn'
  rspamd: { size: '10Gi' },   // Uses default 'longhorn'
},

Use cases for per-component storage classes:

  • Fast SSDs for Dovecot mailboxes (high IOPS)

  • Standard disks for Postfix queue (temporary data)

  • Cheaper storage for backups

Enable Optional Component Storage

When enabling optional components, configure their storage:

components: {
  clamav: true,
  webdav: true,
},

storage: {
  storageClass: 'longhorn',

  // Required for enabled optional components
  clamav: { size: '10Gi' },  // Virus signature database
  webdav: { size: '20Gi' },  // Calendars and contacts
},

Expand Existing PVCs

Important: PVC sizes can be increased but never decreased. Shrinking requires deleting and recreating the PVC (data loss).

Step 1: Update configuration

Increase the size in your CDK8S configuration:

storage: {
  storageClass: 'longhorn',
  dovecot: { size: '500Gi' },  // Increased from 200Gi
},

Step 2: Regenerate and apply

# Regenerate manifests with new size
npm run synth

# Apply changes
kubectl apply -f dist/mailu.k8s.yaml

Step 3: Verify expansion

# Check PVC status
kubectl get pvc -n mailu

# Verify new size (should show RESIZING or new size)
kubectl describe pvc -n mailu mailu-dovecot-pvc

Note: The storage class must support volume expansion (allowVolumeExpansion: true). Check with:

kubectl get storageclass <storage-class-name> -o yaml | grep allowVolumeExpansion

Storage Performance Optimization

Longhorn Replica Configuration

For Longhorn storage, consider replica settings based on data criticality:

High availability (mailboxes):

  • 3 replicas across nodes

  • Use longhorn storage class with 2-3 replicas

Standard reliability (queues, caches):

  • 2 replicas

  • Use default longhorn storage class

Single replica (temporary data):

  • 1 replica

  • Lower storage overhead

  • Not recommended for mailboxes

Note: Replica configuration is set at the storage class level, not per-PVC.

Monitor Storage Usage

Track storage consumption to plan capacity:

# Check PVC usage
kubectl get pvc -n mailu

# Detailed usage per pod
kubectl exec -n mailu <dovecot-pod-name> -- df -h /mail

# Check available capacity on nodes (Longhorn)
kubectl get nodes.longhorn.io -n longhorn-system

Set up monitoring alerts at 70% and 85% capacity thresholds.

Troubleshooting

PVC stuck in Pending

Symptom: PVC shows Pending status, pod cannot start.

Solution: Check storage class exists and has available capacity.

# Check PVC status
kubectl describe pvc -n mailu <pvc-name>

# Check storage class
kubectl get storageclass

# Check node storage capacity (Longhorn)
kubectl get nodes.longhorn.io -n longhorn-system

PVC expansion not working

Symptom: PVC size unchanged after applying new configuration.

Solution: Verify storage class supports expansion:

# Check storage class expansion support
kubectl get storageclass <storage-class-name> -o jsonpath='{.allowVolumeExpansion}'

# If false, expansion is not supported
# Alternative: Create new PVC with larger size and migrate data

Out of storage space

Symptom: Pods show disk pressure, writes failing.

Immediate fix: Increase PVC size (see expansion section above).

Long-term:

  • Set up storage monitoring and alerting

  • Review mailbox quotas in Mailu admin UI

  • Enable email compression in Dovecot

  • Implement email retention policies

Estimate Storage Requirements

Calculating Dovecot mailbox storage

Formula: Total Storage = (Active Users × Average MB per User × Growth Factor) + Buffer

Example (100 users):

Active users: 100
Average per user: 3GB (typical)
Growth factor: 1.5 (50% growth over 2 years)
Buffer: 20% (for spikes)

Calculation: (100 × 3GB × 1.5) + 20% = 450GB × 1.2 = 540GB
Recommended: 600Gi PVC

Monitoring actual usage

Deploy with initial estimates, then adjust based on actual usage:

# Check current mailbox usage
kubectl exec -n mailu <dovecot-pod> -- du -sh /mail

# Per-user breakdown (if accessible)
kubectl exec -n mailu <dovecot-pod> -- du -sh /mail/*

See Also