Storage Architecture¶
Understanding persistent storage requirements and configuration in cdk8s-mailu deployments.
Introduction¶
cdk8s-mailu components require persistent storage for:
Mail data: User mailboxes (largest storage requirement)
Configuration state: DKIM keys, learned spam patterns, virus signatures
Operational queues: Mail queues, temporary files
Application data: Database, Roundcube attachments, calendars/contacts
This document explains what each component stores, sizing recommendations, and storage class considerations.
Storage Components Overview¶
Component |
Storage Type |
Purpose |
Typical Size |
Growth Rate |
|---|---|---|---|---|
Admin |
PVC |
DKIM keys, admin data |
1-5Gi |
Low (static) |
Postfix |
PVC |
Mail queue, spool files |
5-20Gi |
Medium (transient) |
Dovecot |
PVC |
User mailboxes (emails) |
50-500Gi |
High (user dependent) |
Rspamd |
PVC |
Learned spam patterns |
1-5Gi |
Low (limited by bayes expiry) |
ClamAV |
PVC |
Virus signature database |
2-5Gi |
Low (signatures rotate) |
Webmail |
PVC |
Roundcube session data |
1-5Gi |
Low (session expiry) |
WebDAV |
PVC |
Calendars and contacts |
5-50Gi |
Medium (user dependent) |
External Storage (not in cdk8s-mailu scope):
PostgreSQL: Admin database, user accounts, domains, aliases (managed separately)
Redis: Caching, rate limiting state (ephemeral, can use emptyDir)
Component Storage Details¶
Admin Storage¶
What is stored:
DKIM private keys: One key per domain (RSA 2048-bit, ~1.6KB each)
Admin instance data: Configuration state, logs
SQLite database (if not using PostgreSQL): User accounts, domains, aliases
Size calculation:
100 domains × 2KB DKIM keys = 200MB
SQLite database (if used): 100-500MB for thousands of users
Logs and state: 100-500MB
Recommended minimum: 1Gi
Typical deployment: 2-5Gi
Growth pattern:
Mostly static after initial setup
DKIM keys only added when new domains are created
SQLite grows slowly with user/domain additions
Access pattern:
Low I/O (read DKIM keys on message signing)
Not performance-critical
StorageClass considerations:
Standard HDD storage acceptable
No need for high-IOPS SSD
Backup recommended (losing DKIM keys breaks DKIM signatures)
Postfix Storage¶
What is stored:
Mail queue: Outgoing and deferred messages awaiting delivery
Spool directory: Temporary files during message processing
Postfix state: Queue manager state, TLS session cache
Size calculation:
Active queue: Typically <100MB (messages deliver quickly)
Deferred queue: Can grow to 1-10GB if remote servers are down
Recommended minimum: 5Gi
High-volume sites: 20Gi
Growth pattern:
Highly variable (depends on delivery success rate)
Grows when remote mail servers are unreachable
Shrinks as deferred messages are retried and delivered
Queue should drain to near-zero in healthy operation
Access pattern:
High I/O during message processing
Many small files (one per queued message)
Random read/write access
StorageClass considerations:
SSD recommended for high-volume sites (>10k messages/day)
Low-latency storage improves queue processing speed
HDD acceptable for low-volume (<1k messages/day)
Backup not critical (queued messages are transient)
Dovecot Storage¶
What is stored:
User mailboxes: All received emails for all users
Mail indexes: Dovecot index files for fast IMAP access
Mailbox metadata: Folder subscriptions, flags, seen state
Size calculation (most critical sizing decision):
Per-user estimate: 1-10GB (varies widely by usage)
Example: 100 users × 5GB average = 500GB
Growth: 100-500MB per user per year (typical)
Overhead: Indexes add ~5-10% to mail size
Recommended: Start with 2-3× current mail size for growth
Growth pattern:
Continuous linear growth (users receive mail daily)
Rate depends on user behavior and retention policies
Can implement quota limits to control growth
Access pattern:
High I/O (IMAP clients sync frequently)
Mix of sequential (reading messages) and random (searching)
Index files heavily accessed
StorageClass considerations:
SSD strongly recommended (IMAP performance critical)
High IOPS needed for responsive webmail/IMAP
Consider tiered storage: Hot (SSD) + Cold (HDD archive)
Backup essential (user mail is irreplaceable)
Snapshots recommended for quick recovery
Special considerations:
Largest storage consumer in Mailu deployment
Plan for 3-5 year growth horizon
Monitor closely and expand proactively
Consider retention policies to limit growth
Rspamd Storage¶
What is stored:
Bayes spam classifier: Learned tokens from spam/ham training
Fuzzy hashes: Spam signatures for fuzzy matching
Statistics: Spam detection statistics and metadata
Size calculation:
Bayes database: 500MB - 2GB (depends on training corpus)
Fuzzy storage: 100-500MB
Statistics and logs: 100-500MB
Recommended minimum: 2Gi
Typical deployment: 5Gi
Growth pattern:
Grows initially as spam/ham is learned
Stabilizes after training (bayes token expiry limits size)
Old tokens are expired automatically (typically 30-90 days)
Access pattern:
High read I/O during spam scanning
Low write I/O (only on learning/updates)
Sequential reads (token lookups)
StorageClass considerations:
SSD beneficial but not critical
Fast reads improve spam scanning speed
HDD acceptable for low-volume sites
Backup recommended (losing training means relearning)
ClamAV Storage (Optional Component)¶
What is stored:
Virus signature database: ClamAV definitions (updated daily)
Temporary scan files: Extracted attachments during scanning
Size calculation:
Signature database: 1-3GB (compressed)
Temporary files: 100-500MB (transient)
Recommended minimum: 3Gi
Typical deployment: 5Gi
Growth pattern:
Signature database grows slowly (~100MB/year)
Daily updates replace old signatures
Size relatively stable
Access pattern:
High read I/O during virus scanning
Sequential reads (scanning attachments)
Daily write for signature updates
StorageClass considerations:
SSD improves scan speed (reduces mail delay)
HDD acceptable for small deployments
Backup not critical (signatures can be re-downloaded)
Webmail Storage (Optional Component)¶
What is stored:
Roundcube database (if not using PostgreSQL): Sessions, preferences, cache
Temporary files: Attachment uploads during composition
Session data: Active user sessions
Size calculation:
SQLite database: 100-500MB
Session data: 100MB per 1000 concurrent users
Temporary files: 100-500MB
Recommended minimum: 2Gi
Typical deployment: 5Gi
Growth pattern:
Grows with number of active sessions
Session data expires (typically 24-72 hours)
Size relatively stable after initial growth
Access pattern:
Medium I/O (session reads/writes)
Random access (session lookups)
StorageClass considerations:
SSD improves webmail responsiveness
HDD acceptable for low-concurrency (<100 users)
Backup not critical (sessions are transient)
WebDAV Storage (Optional Component)¶
What is stored:
CalDAV data: User calendars, events, todos
CardDAV data: Contact books, vcards
Radicale database: Collection metadata, sync tokens
Size calculation:
Per-user estimate: 10-100MB (calendars + contacts)
Example: 100 users × 50MB = 5GB
Recommended minimum: 5Gi
Typical deployment: 10-50Gi
Growth pattern:
Grows with user calendar/contact data
Slower than email growth (calendars are smaller)
Historical events accumulate over time
Access pattern:
Low-medium I/O (sync on schedule changes)
Random access (event lookups)
StorageClass considerations:
HDD acceptable (CalDAV/CardDAV not latency-sensitive)
SSD beneficial for large deployments (>500 users)
Backup recommended (user data is valuable)
Configuration in cdk8s-mailu¶
Storage Configuration Interface¶
Storage is configured in the MailuChartConfig.storage section:
import { MailuChart } from '@example/cdk8s-mailu';
new MailuChart(app, 'mailu', {
// ... other config
storage: {
// Global default storage class
storageClass: 'longhorn',
// Per-component overrides
admin: {
size: '2Gi',
storageClass: 'longhorn', // Optional override
},
postfix: {
size: '10Gi',
},
dovecot: {
size: '200Gi', // Largest allocation
storageClass: 'longhorn-ssd', // Use faster storage
},
rspamd: {
size: '5Gi',
},
// Optional components (only used if enabled)
clamav: {
size: '5Gi',
},
webmail: {
size: '3Gi',
},
webdav: {
size: '20Gi',
},
},
});
Storage Class Selection¶
Global default (storage.storageClass):
Applied to all components unless overridden
Example:
'longhorn','standard','gp2'
Per-component override (storage.dovecot.storageClass):
Useful for tiered storage (SSD for Dovecot, HDD for others)
Example: High-performance SSD for Dovecot, standard HDD for Admin
Example tiered storage:
storage: {
storageClass: 'longhorn', // Default: Standard HDD
dovecot: {
size: '500Gi',
storageClass: 'longhorn-ssd', // Override: Fast SSD
},
postfix: {
size: '20Gi',
storageClass: 'longhorn-ssd', // Override: Fast SSD
},
// Other components use default 'longhorn' HDD
admin: { size: '2Gi' },
rspamd: { size: '5Gi' },
}
Default Sizes (if not specified)¶
cdk8s-mailu uses these defaults when storage size is not configured:
Admin:
1GiPostfix:
10GiDovecot:
50Gi(⚠️ likely too small for production)Rspamd:
2GiClamAV:
3GiWebmail:
2GiWebDAV:
5Gi
Recommendation: Always explicitly set storage sizes (don’t rely on defaults).
Sizing Recommendations by Deployment Size¶
Small Deployment (1-50 users)¶
storage: {
storageClass: 'standard',
admin: { size: '2Gi' },
postfix: { size: '5Gi' },
dovecot: { size: '50Gi' }, // 1GB per user
rspamd: { size: '2Gi' },
webmail: { size: '2Gi' },
webdav: { size: '5Gi' },
}
Total: ~66Gi
Medium Deployment (50-500 users)¶
storage: {
storageClass: 'longhorn',
admin: { size: '5Gi' },
postfix: { size: '10Gi' },
dovecot: { size: '250Gi', storageClass: 'longhorn-ssd' }, // 500MB per user
rspamd: { size: '5Gi' },
clamav: { size: '5Gi' },
webmail: { size: '5Gi' },
webdav: { size: '25Gi' },
}
Total: ~305Gi (250Gi SSD, 55Gi HDD)
Large Deployment (500-5000 users)¶
storage: {
storageClass: 'longhorn',
admin: { size: '10Gi' },
postfix: { size: '50Gi', storageClass: 'longhorn-ssd' },
dovecot: { size: '2000Gi', storageClass: 'longhorn-ssd' }, // 400MB per user
rspamd: { size: '10Gi' },
clamav: { size: '5Gi' },
webmail: { size: '10Gi' },
webdav: { size: '100Gi' },
}
Total: ~2185Gi (2050Gi SSD, 135Gi HDD)
Storage Best Practices¶
1. Plan for Growth¶
Dovecot: Allocate 2-3× current mail size
Postfix: Size for 3-7 days of deferred queue
Monitor usage: Set alerts at 70% capacity
Expand proactively: Before hitting 85% full
2. Use Appropriate Storage Classes¶
Critical performance: Dovecot, Postfix → SSD
Moderate performance: Rspamd, ClamAV → SSD or fast HDD
Low performance: Admin, Webmail, WebDAV → HDD
3. Implement Backup Strategy¶
Essential backups:
Dovecot mailboxes (user mail data)
Admin DKIM keys
WebDAV calendars/contacts
Nice-to-have backups:
Rspamd learned patterns
PostgreSQL database
Not needed:
Postfix queue (transient)
ClamAV signatures (can re-download)
Webmail sessions (ephemeral)
4. Retention Policies¶
Implement policies to control growth:
User quotas: Limit per-user mailbox size
Auto-expunge: Delete trash/spam after 30 days
Archive policies: Move old mail to cheaper storage tiers
5. Monitoring and Alerts¶
Monitor these metrics:
PVC usage percentage (alert at 70%)
Growth rate (predict when expansion needed)
IOPS saturation (indicates need for faster storage)
# Check PVC usage
kubectl get pvc -n mailu
kubectl exec -n mailu <dovecot-pod> -- df -h /mail
# Monitor IOPS (if storage supports metrics)
kubectl top pod -n mailu --containers
Troubleshooting¶
PVC Full (Out of Space)¶
Symptoms:
Mail delivery failures
Dovecot errors: “Not enough disk space”
Postfix queue buildup
Immediate fix:
Identify full PVC:
kubectl exec -n mailu <pod> -- df -hClean temporary files:
# Postfix queue kubectl exec -n mailu <postfix-pod> -- postqueue -p | grep -c "^[A-F0-9]" # Dovecot indexes (can regenerate) kubectl exec -n mailu <dovecot-pod> -- rm -rf /mail/*/dovecot.index*
Expand PVC (if storage class supports it):
kubectl patch pvc dovecot-pvc -n mailu -p '{"spec":{"resources":{"requests":{"storage":"300Gi"}}}}'
Long-term fix:
Implement user quotas
Enable auto-expunge policies
Plan capacity expansion
Slow Mail Access (Performance Issues)¶
Symptoms:
Slow IMAP sync
Webmail timeouts
Message delivery delays
Check:
Verify storage class performance:
kubectl get pvc -n mailu -o jsonpath='{.items[*].spec.storageClassName}'Check IOPS limits (cloud providers):
AWS EBS: Throughput based on volume size
Azure Disk: IOPS tier selection
Monitor pod I/O wait:
kubectl exec -n mailu <dovecot-pod> -- iostat -x 1 10
Fix:
Migrate to faster storage class (SSD)
Increase PVC size (some classes scale IOPS with size)
Optimize Dovecot indexes (rebuild corrupted indexes)
PVC Not Binding¶
Symptoms: Pod stuck in Pending, PVC shows “Pending”
Check:
Verify storage class exists:
kubectl get storageclassCheck provisioner logs:
kubectl logs -n kube-system -l app=<provisioner-name>Verify node resources (some storage requires specific node features)
Fix:
Correct storage class name typo
Install storage provisioner if missing
Check node selectors/taints
See Also¶
Configuration Options Reference - Complete storage config API
Architecture Overview - How components interact
Backup and Restore - Backup procedures
Monitoring - Storage metrics and alerts