Nextcloud S3 Architecture Analysis ¶
Design Choices, Trade-offs, and Cost Implications ¶
Date: October 27, 2025
Context: 30 users, 100 TB storage, AWS eu-south-1 (Milan)
Executive Summary ¶
RECOMMENDED ARCHITECTURE: S3 Primary Storage with S3 Versioning + Object Lock on replica bucket
Key Benefits:
- Better performance than External Storage (metadata in database)
- Native S3 versioning provides file-level protection
- Object Lock on replica bucket prevents ransomware/accidental deletion
- Lower cost (no chunking overhead, fewer API requests)
- Cleaner recovery process (restore by object version, not file paths)
Monthly Cost: €1,786-1,836 (47% savings vs. original design)
Nextcloud S3 Architecture Options ¶
Option 1: S3 as Primary Storage (RECOMMENDED) ¶
How it works:
- Nextcloud stores ALL files in S3 with UUID-based object keys (e.g.,
urn:oid:12345...) - File metadata (names, paths, permissions, sharing) stored in PostgreSQL database
- Files stored as complete objects, NOT visible as traditional file structure in S3
- Nextcloud manages all file operations through its application layer
S3 bucket structure:
s3://nextcloud-primary-bucket/
├── urn:oid:1234567890abcdef... (user1's photo.jpg)
├── urn:oid:fedcba0987654321... (user2's document.pdf)
├── urn:oid:abcd1234efgh5678... (user3's video.mp4)
└── ...
Advantages:
✅ Better performance (metadata in database, not S3 prefixes)
✅ Fewer S3 API requests (no directory listings, direct object access by UUID)
✅ Efficient multipart upload with Redis/Memcached (streaming chunks directly to S3)
✅ No filesystem overhead on EC2 instance
✅ Native S3 versioning works perfectly (each file = 1 S3 object with version history)
✅ Object Lock can protect entire dataset from ransomware/deletion
Disadvantages:
❌ Files not accessible outside Nextcloud (UUID-based naming)
❌ Cannot browse S3 bucket manually to find specific files
❌ Requires database backup to be tightly synchronized with S3 (metadata coupling)
❌ Migration/disaster recovery requires both database + S3 restore
Cost implications:
- Lower S3 API costs: Direct object access by UUID (fewer LIST operations)
- Lower data transfer: Streaming multipart uploads (no intermediate file assembly on EC2)
- Higher database load: All metadata queries hit PostgreSQL
- Estimated API cost: ~€50-100/month for 30 users with moderate file operations
Option 2: S3 as External Storage ¶
How it works:
- Nextcloud stores files in S3 with traditional file/folder structure
- File paths mirror user directory structure (e.g.,
/user1/Documents/file.pdf) - Nextcloud accesses files through External Storage app
- Files can be accessed outside Nextcloud using standard S3 tools
S3 bucket structure:
s3://nextcloud-external-bucket/
├── user1/
│ ├── Documents/
│ │ ├── file.pdf
│ │ └── report.docx
│ └── Photos/
│ └── vacation.jpg
├── user2/
│ └── ...
Advantages:
✅ Files accessible outside Nextcloud (standard file paths)
✅ Can browse/manage files using AWS Console, CLI, or other S3 tools
✅ Easier migration from traditional filesystems
✅ Can mount same bucket in multiple Nextcloud instances (read-only scenarios)
Disadvantages:
❌ Slower performance (metadata partially in S3, requires LIST operations)
❌ Higher S3 API costs (directory traversal, prefix listings)
❌ Chunked upload overhead (files assembled on EC2 before final upload)
❌ S3 versioning is problematic (versioning entire directory structure is complex)
❌ Object Lock is harder to implement (mixed file/folder structure)
❌ More EC2 disk space needed for temporary file assembly
Cost implications:
- Higher S3 API costs: Frequent LIST operations for directory traversal
- Higher data transfer: Chunked uploads assembled on EC2 before S3 upload
- Lower database load: Some metadata inferred from S3 paths
- Estimated API cost: ~€150-250/month for 30 users (3-5x higher due to LIST operations)
S3 Versioning + Object Lock: Design Choices ¶
Why S3 Versioning is the Right Choice for Primary Storage ¶
Problem: Nextcloud users need protection against:
- Accidental file deletion
- File overwrites (saving bad version over good version)
- Ransomware attacks (mass file encryption/deletion)
- Insider threats (malicious deletion)
Solution: S3 Versioning at the object level
How it works with Primary Storage:
- Each Nextcloud file = 1 S3 object with UUID
- When user "deletes" file in Nextcloud:
- Nextcloud marks file as deleted in database
- S3 object gets delete marker (object NOT permanently deleted)
- Previous versions retained for 30 days
- When user "overwrites" file in Nextcloud:
- Nextcloud uploads new version with same UUID
- S3 creates new object version
- Previous versions retained for 30 days
Recovery process:
- User: "I deleted my file yesterday!"
- Admin restores database to timestamp before deletion (PITR)
- Admin restores S3 object version from before deletion
- File reappears in Nextcloud with all metadata intact
Cost: €22/month (assumes 2% file churn = 2TB versioned data at €11/TB for One Zone-IA)
Why Object Lock is Essential for Ransomware Protection ¶
Problem: What if ransomware compromises Nextcloud application AND has AWS credentials?
Without Object Lock:
- Ransomware can delete S3 object versions via AWS API
- Even with versioning, attacker can purge all versions
- Backup is useless if attacker has access to delete it
With Object Lock (GOVERNANCE mode):
- S3 replica bucket in eu-west-1 has 90-day Object Lock
- Even with root AWS credentials, objects CANNOT be deleted for 90 days
- Only way to delete: Remove Object Lock (requires
s3:BypassGovernanceRetentionpermission) - Application role explicitly DENIES this permission
Protection layers:
- Versioning (source bucket): Protects against accidental deletion/overwrite
- IAM restrictions: Application cannot delete object versions
- Cross-region replication: Geographic separation
- Object Lock (replica bucket): Immutable protection for 90 days
Cost: €94/month (100TB in Glacier Deep Archive at €0.00094/GB/month)
Design Trade-offs Analysis ¶
Primary Storage vs. External Storage ¶
| Factor | Primary Storage | External Storage |
|---|---|---|
| Performance | ⭐⭐⭐⭐⭐ Fast | ⭐⭐⭐ Moderate |
| S3 API Cost | €50-100/month | €150-250/month |
| Versioning Support | ⭐⭐⭐⭐⭐ Excellent | ⭐⭐ Poor |
| Object Lock Support | ⭐⭐⭐⭐⭐ Excellent | ⭐⭐ Difficult |
| File Accessibility | ❌ Nextcloud only | ✅ Direct S3 access |
| Database Coupling | ❌ Tight coupling | ✅ Loose coupling |
| Migration Complexity | ❌ High | ✅ Low |
| EC2 Disk Usage | ✅ Minimal | ❌ High (chunking) |
Winner: Primary Storage for cost, performance, and data protection
Versioning Strategy: Source vs. Replica ¶
Option A: Versioning on Source Bucket Only
- ✅ Fast recovery (instant restore from noncurrent versions)
- ✅ Perfect for 0-30 day operational recovery
- ❌ If source bucket compromised, versions can be deleted
- Cost: €22/month
Option B: Versioning on Replica Bucket Only
- ✅ Protected by Object Lock (immutable)
- ❌ Slow recovery (12-48 hours from Deep Archive)
- ❌ No instant recovery for recent deletions
- Cost: €0 (included in replication)
Option C: Versioning on BOTH Buckets (RECOMMENDED)
- ✅ Fast recovery for 0-30 days (source bucket)
- ✅ Immutable protection for 30-90 days (replica bucket)
- ✅ Multi-layer defense
- Cost: €22/month (only source bucket noncurrent versions cost extra)
Object Lock Mode: GOVERNANCE vs. COMPLIANCE ¶
GOVERNANCE mode (RECOMMENDED):
- ✅ Allows authorized admins to remove lock if needed
- ✅ Requires
s3:BypassGovernanceRetentionpermission (explicitly denied to application) - ✅ Good for disaster recovery (can restore sooner if needed)
- ✅ Meets most compliance requirements
- Use case: Protect against ransomware and accidental deletion
COMPLIANCE mode:
- ❌ CANNOT be removed by ANYONE (including AWS root account)
- ❌ Objects cannot be deleted until retention period expires
- ❌ Too restrictive for operational needs
- Use case: Legal hold, regulatory compliance (WORM storage)
Winner: GOVERNANCE mode provides adequate protection with operational flexibility
Performance Impact Analysis ¶
API Request Patterns ¶
Primary Storage with Versioning:
User uploads 10 MB file:
- 1x PutObject (€0.000005)
- 1x CopyObject for versioning (€0.000005)
Total: 2 API requests
User deletes file:
- 1x DELETE (adds delete marker) (€0.000005)
Total: 1 API request
User downloads file:
- 1x GetObject (€0.0000004)
Total: 1 API request
External Storage with Versioning:
User uploads 10 MB file:
- 5x ListBucket (directory traversal) (€0.000025)
- 1x PutObject (€0.000005)
- 1x CopyObject for versioning (€0.000005)
Total: 7 API requests (3.5x higher)
User deletes file:
- 3x ListBucket (€0.000015)
- 1x DELETE (€0.000005)
Total: 4 API requests (4x higher)
User downloads file:
- 2x ListBucket (€0.00001)
- 1x GetObject (€0.0000004)
Total: 3 API requests (3x higher)
Annual API cost difference: €840-1,200/year savings with Primary Storage
Latency Comparison ¶
Primary Storage:
- Average file operation: 1-2 API calls
- Typical latency: 50-100ms (within same region)
- Concurrent operations: Limited by Nextcloud application threads
External Storage:
- Average file operation: 3-7 API calls (directory traversal overhead)
- Typical latency: 150-350ms (multiple round-trips)
- Concurrent operations: Limited by S3 LIST operation rate (5,500/sec/prefix)
Performance impact: Primary Storage is 2-3x faster for typical operations
Cost-Benefit Analysis ¶
Monthly Cost Breakdown (Primary Storage + Versioning + Object Lock) ¶
| Component | Cost | Benefit |
|---|---|---|
| S3 One Zone-IA (100TB) | €1,100 | Base storage |
| Noncurrent versions (2TB, 30 days) | €22 | Instant recovery (0-30 days) |
| Cross-region replication | €50-100 | Geographic redundancy |
| Replica Deep Archive (100TB) | €94 | Long-term immutable backup |
| Object Lock (included) | €0 | Ransomware protection |
| RDS backup (PITR + snapshots) | €178 | Database consistency |
| Total Data Protection | €344-394 | Multi-layer defense |
Cost per TB per month: €3.44-3.94 for comprehensive protection
Industry benchmark: €5-10/TB/month for enterprise backup solutions
Savings: 50-65% vs. traditional backup solutions
Alternative Architectures (NOT Recommended) ¶
Alternative 1: No Versioning + Nightly Snapshots ¶
Approach: Use AWS Backup to snapshot entire bucket nightly
- Cost: €200-300/month (snapshot storage)
- Recovery: Restore entire bucket to specific date
- Disadvantage: Cannot recover individual files deleted mid-day
- Use case: Only if acceptable to lose up to 24 hours of changes
Alternative 2: Nextcloud Built-in Versioning ¶
Approach: Let Nextcloud manage file versions (store multiple copies in S3)
- Cost: 2-3x storage cost (duplicated files)
- Recovery: Through Nextcloud UI only
- Disadvantage: If database corrupted, version metadata is lost
- Use case: Small deployments with <1TB data
Alternative 3: External Storage + EFS/FSx ¶
Approach: Use AWS EFS or FSx for Lustre instead of S3
- Cost: €3,000-5,000/month for 100TB (10-30x more expensive)
- Performance: Better for small file operations
- Disadvantage: Much higher cost, regional limitation
- Use case: Extremely high-performance requirements (video editing, etc.)
Recommendations ¶
Recommended Architecture ¶
Storage Configuration:
Primary Storage: S3 One Zone-IA (eu-south-1)
├── Storage Class: One Zone-IA
├── Versioning: Enabled (30-day noncurrent retention)
├── Lifecycle: Expire noncurrent versions after 30 days
└── IAM: Allow read/write, DENY delete object versions
Replica Storage: S3 Glacier Deep Archive (eu-west-1)
├── Storage Class: Deep Archive
├── Versioning: Enabled
├── Object Lock: GOVERNANCE mode (90-day retention)
├── Replication: Enabled from primary bucket
└── IAM: DENY all application access
Database Configuration:
RDS PostgreSQL: Single-AZ db.m6i.large (eu-south-1)
├── Automated Backup: 30-day retention + PITR
├── AWS Backup: Cross-region snapshots every 6 hours (90-day retention)
└── Consistency: PITR ensures database matches S3 file state
Application Configuration:
Nextcloud config.php:
'objectstore' => [
'class' => 'OC\\Files\\ObjectStore\\S3',
'arguments' => [
'bucket' => 'nextcloud-primary-bucket-eu-south-1',
'key' => 'use-iam-role',
'secret' => 'use-iam-role',
'region' => 'eu-south-1',
'use_ssl' => true,
'use_path_style' => false,
],
],
'objectstore.multibucket' => false, // Use single bucket for 30 users
Redis configuration (required for efficient multipart uploads):
'memcache.distributed' => '\\OC\\Memcache\\Redis',
'redis' => [
'host' => 'localhost',
'port' => 6379,
],
When to Consider Alternative Architectures ¶
Use External Storage instead if:
- Need to access files outside Nextcloud (e.g., integrate with other apps)
- Already have existing S3 bucket with traditional file structure
- Multiple systems need to access same files
- Compliance requires human-readable file paths
Use EFS/FSx instead if:
- Budget allows 10x storage cost (€30,000+/month)
- Extremely high IOPS requirements (>100,000 IOPS sustained)
- Need sub-10ms latency for small file operations
- Running high-performance computing workloads
Disable versioning if:
- All files are reproducible/regenerable (scratch data, temporary files)
- Users never need to recover deleted/overwritten files
- Cost reduction of €22/month is critical
Implementation Checklist ¶
Phase 1: Infrastructure Setup (Week 1) ¶
- Create S3 source bucket (One Zone-IA, versioning enabled)
- Create S3 replica bucket (Deep Archive, Object Lock GOVERNANCE 90 days)
- Configure Cross-Region Replication (eu-south-1 → eu-west-1)
- Set up lifecycle policies (noncurrent version expiration)
- Create IAM role for Nextcloud application (read/write, deny delete versions)
- Launch RDS PostgreSQL Single-AZ instance
- Configure RDS automated backups (30-day retention + PITR)
- Set up AWS Backup plan (cross-region snapshots every 6 hours)
Phase 2: Nextcloud Configuration (Week 2) ¶
- Launch EC2 m6i.xlarge instance
- Install Nextcloud + Redis + PostgreSQL client
- Configure Nextcloud to use S3 as primary storage
- Test file upload/download operations
- Verify S3 versioning is working (upload, overwrite, check versions)
- Validate cross-region replication (check replica bucket)
- Test file recovery from noncurrent version
Phase 3: Testing & Validation (Week 3) ¶
- Disaster Recovery Drill #1: Restore deleted file (0-30 days)
- Disaster Recovery Drill #2: Restore from Deep Archive (30-90 days)
- Disaster Recovery Drill #3: Region failure (restore RDS + S3 in eu-west-1)
- Performance testing: Measure upload/download speeds
- Load testing: Simulate 30 concurrent users
- Validate API request costs (enable S3 request metrics)
Phase 4: Monitoring Setup (Week 4) ¶
- Configure CloudWatch alarms (EC2 CPU, RDS CPU, S3 replication lag)
- Set up AWS Cost Explorer alerts (€1,900 and €2,200 thresholds)
- Enable S3 replication metrics
- Configure RDS backup failure notifications
- Document recovery procedures
- Train administrators on recovery process
Conclusion ¶
Primary Storage + S3 Versioning + Object Lock is the optimal architecture because:
- Performance: 2-3x faster than External Storage (fewer API calls)
- Cost: 50-60% lower API costs (€50-100/month vs €150-250/month)
- Protection: Native S3 versioning + Object Lock provides comprehensive defense
- Recovery: Clean recovery process (restore by object version + database PITR)
- Scalability: Scales to petabytes without performance degradation
Total monthly cost: €1,786-1,836 (47% savings vs. original €3,325-3,419)
The combination of Primary Storage architecture with multi-layer versioning and Object Lock provides enterprise-grade data protection at consumer-grade pricing.
Document Version: 1.0
Author: AWS Cost Optimization Analysis
Date: October 27, 2025
Comments
Please login to leave a comment.
No comments yet. Be the first to comment!