ADR-008: Cloudflare R2 for File Storage
Status: Accepted
Date: 2026-02-01
Deciders: Abdisamed Mohamed
Related ADRs: ADR-002 (Edge-First Architecture), ADR-005 (Durable Objects Usage), ADR-007 (Database Layer)
Context
CepatEdge requires robust file storage for maintenance request attachments including:
- Before/after photos of maintenance work
- Documentation files (PDFs, documents)
- User avatars and profile images
- System-generated reports
Requirements:
- Global accessibility with low latency
- Cost-effective storage with generous free tier
- Integration with edge computing infrastructure
- Secure access with proper authentication
- Automatic scaling and high availability
Constraints:
- Must work seamlessly with Cloudflare Workers
- Should minimize database storage overhead
- Files must be accessible globally with low latency
- Cost should remain predictable and low
Decision
Use Cloudflare R2 as the primary file storage solution for all CepatEdge file uploads and attachments.
Implementation Strategy
Storage Architecture
typescript
// R2 integration with Workers
import { PutObjectCommand, GetObjectCommand } from '@aws-sdk/client-s3';
import { getSignedUrl } from '@aws-sdk/s3-request-presigner';
// R2 client configuration
const r2Client = new S3Client({
region: 'auto',
endpoint: `https://${env.CLOUDFLARE_ACCOUNT_ID}.r2.cloudflarestorage.com`,
credentials: {
accessKeyId: env.R2_ACCESS_KEY_ID,
secretAccessKey: env.R2_SECRET_ACCESS_KEY,
},
});File Organization
/cepatedge/
├── maintenance/
│ ├── {requestId}/
│ │ ├── before-{timestamp}.jpg
│ │ ├── after-{timestamp}.jpg
│ │ └── documentation.pdf
├── avatars/
│ └── {userId}/
│ └── profile-{timestamp}.jpg
└── reports/
└── {reportId}/
└── generated-{timestamp}.pdfUpload Process
typescript
// Secure file upload with validation
app.post('/api/uploads', async (c) => {
const user = c.get('user');
const formData = await c.req.formData();
const file = formData.get('file') as File;
// Validate file type and size
if (!isValidFile(file)) {
return c.json({ error: 'Invalid file' }, 400);
}
// Generate secure filename
const filename = generateSecureFilename(file.name, user.id);
// Upload to R2
const uploadUrl = await generatePresignedUploadUrl(filename);
return c.json({ uploadUrl, filename });
});Consequences
Positive
- Edge Integration: Native Cloudflare integration with Workers
- Global CDN: Automatic global distribution via Cloudflare CDN
- Cost Effective: 10GB free storage, pay-per-GB after
- Security: Built-in access controls and signed URLs
- Performance: Low latency file access worldwide
- Scalability: Automatic scaling with usage
Negative
- Learning Curve: S3-compatible API different from other providers
- Cloudflare Lock-in: Tied to Cloudflare ecosystem
- Free Tier Limits: 10GB initial storage limit
- Migration Complexity: Vendor-specific implementation
Mitigation
- Abstraction Layer: Create unified file storage interface
- Migration Planning: Design for future storage provider changes
- Usage Monitoring: Track storage usage and plan for scaling
- Documentation: Comprehensive file handling documentation
Performance Characteristics
Upload Performance
- Direct Upload: <5 seconds for typical files (<10MB)
- Chunked Upload: Support for large files with resumable uploads
- Parallel Uploads: Multiple file uploads simultaneously
Access Performance
- Global CDN: <100ms access time worldwide
- Caching: Automatic CDN caching for frequently accessed files
- Signed URLs: Secure temporary access links
Scalability
- Storage Limits: Virtually unlimited scaling
- Bandwidth: Global CDN handles traffic spikes
- Concurrent Access: Thousands of simultaneous file accesses
Cost Optimization
Free Tier Utilization
- Storage: 10GB included free
- Bandwidth: Generous free bandwidth allocation
- Requests: Included in Workers free tier
Scaling Costs
Storage (per GB/month) | $0.015
Bandwidth (per GB) | $0.010 (first 10TB)
Class A Operations | $4.50 per million
Class B Operations | $0.36 per millionOptimization Strategies
- Compression: Automatic image compression and optimization
- CDN Caching: Maximize cache hit ratios
- File Deduplication: Avoid storing duplicate files
- Lifecycle Policies: Automatic cleanup of old files
Security Implementation
Access Control
typescript
// Signed URLs for secure access
const signedUrl = await getSignedUrl(
r2Client,
new GetObjectCommand({
Bucket: 'cepatedge', // Workers binding: env.CEPATEDGE_STORAGE
Key: filename,
}),
{ expiresIn: 3600 } // 1 hour expiry
);File Validation
- Type Checking: MIME type validation
- Size Limits: Configurable file size restrictions
- Virus Scanning: Integration with security services
- Content Moderation: AI-powered content analysis
Audit Logging
- Upload Tracking: Log all file uploads with user context
- Access Logging: Track file access patterns
- Security Events: Monitor for suspicious activity
Integration Points
Database Integration
sql
-- File metadata in database
CREATE TABLE attachments (
id TEXT PRIMARY KEY,
filename TEXT NOT NULL,
original_name TEXT NOT NULL,
mime_type TEXT NOT NULL,
size INTEGER NOT NULL,
r2_key TEXT NOT NULL UNIQUE,
uploaded_by TEXT NOT NULL,
request_id TEXT,
created_at TIMESTAMP DEFAULT NOW()
);API Integration
- Upload Endpoints: Secure file upload APIs
- Download Links: Temporary signed URLs
- Metadata APIs: File information and management
- Bulk Operations: Batch file operations
Alternatives Considered
AWS S3
- Pros: Industry standard, extensive ecosystem
- Cons: Higher latency, additional Cloudflare complexity
Cloudflare Images
- Pros: Optimized for images, automatic optimization
- Cons: Image-only, additional cost layer
Database Storage
- Pros: Simple, transactional
- Cons: Performance issues, storage limits
Local File System
- Pros: Simple development
- Cons: Not scalable, no global access
Migration Strategy
Current Implementation
- Files stored locally during development
- Database references with file paths
- Simple file serving via web server
Migration Steps
- R2 Setup: Configure R2 bucket and access keys
- Upload Migration: Update upload endpoints to use R2
- Download Migration: Generate signed URLs for file access
- Database Update: Migrate file metadata to R2 references
- CDN Configuration: Set up Cloudflare CDN for file delivery
Rollback Plan
- Maintain local file storage as fallback
- Database supports both local and R2 references
- Gradual migration with feature flags
Monitoring & Observability
Usage Metrics
- Storage Usage: Track total storage consumption
- Bandwidth Usage: Monitor data transfer costs
- Request Patterns: Analyze file access patterns
- Performance Metrics: Upload/download speed tracking
Operational Monitoring
- File Upload Success Rate: Track upload reliability
- Access Latency: Monitor file access performance
- Error Rates: Track upload/download failures
- Security Events: Monitor for unauthorized access attempts
Future Considerations
Advanced Features
- Image Optimization: Automatic resizing and format conversion
- Video Processing: Video upload and streaming capabilities
- Backup Strategy: Cross-region replication for disaster recovery
- Integration APIs: Third-party service integrations
Scaling Considerations
- Multi-Bucket Strategy: Separate buckets for different file types
- CDN Optimization: Advanced caching and delivery strategies
- Global Distribution: Multi-region deployment strategy
References
- Cloudflare R2 Documentation
- AWS S3 Compatibility Guide
- File Upload Security Best Practices
- CDN Performance Optimization