This document contains implementation details for the Cloudflare R2 integration that replaced the original filesystem-based audio storage.
Date: 2025-06-15
Reason: Reduce server bandwidth costs on Render by moving audio storage to Cloudflare R2
Result: Zero egress costs + global CDN distribution
Client → Server (multipart/form-data) → Local filesystem (/uploads/audio/)
Client ← Server (direct file serving) ← Local filesystem
- Server handled all file transfers
- Linear bandwidth scaling with users
- Single point of failure
- No CDN distribution
Client → Server (get presigned URL) → R2 API
Client → R2 (direct upload via presigned URL)
Client → Server (upload confirmation)
Client ← R2 Public CDN (direct audio access)
- Server only coordinates, no file transfers
- Zero bandwidth costs for audio serving
- Global CDN distribution
- High availability
POST /api/upload-url- Generate presigned upload URLsPOST /api/upload-complete- Confirm successful uploadsPOST /audio- Now redirects to R2 public URLs (was direct file serving)POST /upload- Deprecated with helpful error message
apps/server/src/lib/r2.ts- New R2 utility functionsapps/server/src/routes/upload.ts- Complete rewrite for presigned URL flowapps/server/src/routes/audio.ts- Changed from file serving to R2 redirectsapps/server/src/config.ts- Added R2 configurationapps/client/src/lib/api.ts- Updated to 3-step upload process
Old Flow:
- Client creates FormData with audio file
- POST to /upload with multipart/form-data
- Server saves file to local filesystem
- Server broadcasts room update via WebSocket
New Flow:
- Client requests presigned URL from server
- Server generates presigned URL via R2 API
- Client uploads directly to R2 using presigned URL
- Client notifies server of successful upload
- Server broadcasts room update via WebSocket
# Required environment variables in apps/server/.env
CLOUDFLARE_ACCOUNT_ID=your_cloudflare_account_id
CLOUDFLARE_R2_ACCESS_KEY_ID=your_r2_access_key_id
CLOUDFLARE_R2_SECRET_ACCESS_KEY=your_r2_secret_access_key
CLOUDFLARE_R2_BUCKET_NAME=beatsync-audiobeatsync-audio/
├── room-000000/
│ └── 1749916933051.mp3
└── room-232344/
└── 1745553666739.mp3
@aws-sdk/client-s3@^3.828.0- AWS SDK v3 (R2 compatible)
- Upload: 5MB file = 5MB server bandwidth
- Distribution: 5MB × 9 users = 45MB server bandwidth
- Total per file: 50MB server bandwidth
- Monthly cost: Linear scaling with usage
- Upload: Direct to R2, zero server bandwidth
- Distribution: R2 CDN, zero egress costs
- Storage: ~$0.015 per GB/month
- Operations: Minimal costs for API calls
- Total bandwidth cost: $0
- Old
/uploadendpoint returns deprecation message - File ID format remains unchanged (
room-{roomId}/{filename}) - WebSocket room notifications unchanged
- Client audio fetching flow unchanged (just redirects to R2)
- Requires valid R2 credentials even in development
- Alternative: Mock R2 responses for local testing
- File uploads will go to production R2 bucket
- Verify R2 bucket public access is configured
- Test upload flow end-to-end
- Monitor R2 usage and costs
- Validate global CDN performance
- Revert
apps/server/src/routes/upload.tsto original implementation - Revert
apps/server/src/routes/audio.tsto file serving - Revert
apps/client/src/lib/api.tsto FormData upload - Remove R2 dependencies and configuration
- Ensure
/uploads/audio/directory exists on server
- Server-side audio transcoding for bandwidth optimization
- Chunked/resumable uploads for large files
- File compression before R2 upload
- Automatic cleanup of old room files
- R2 usage metrics integration
- Upload success/failure tracking
- CDN performance monitoring
- Cost alerting for R2 usage
- Presigned URLs have 1-hour expiration by default
- R2 bucket configured for public read access
- Upload validation maintains audio file type checking
- No sensitive data in audio file metadata
- Global latency: R2 CDN reduces audio loading times worldwide
- Server resources: Eliminated file I/O operations
- Scalability: No longer limited by server storage/bandwidth
- Additional network round-trip for presigned URL generation
- Dependency on Cloudflare infrastructure
- Requires internet connectivity for all audio operations
This migration was implemented to address bandwidth cost scaling issues on Render while improving global performance and reliability.