Cloudflare R2 Overview
Cloudflare R2 is an S3-compatible object storage service that:- No Egress Fees: Unlike S3, R2 doesn’t charge for data transfer
- S3-Compatible API: Works with existing S3 tools and libraries
- Global CDN: Integrates with Cloudflare’s network for fast delivery
- Custom Domains: Support for custom domains with SSL
File Upload Process
Automatic File Watching
Livetran usesfsnotify to monitor the output directory for new files:
- FFmpeg Creates File: Writes
.m3u8or.tsfile tooutput/{stream_id}/ - File Watcher Detects:
fsnotifytriggers on file creation/write - Upload Initiated: File is queued for upload to R2
- Retry Logic: Up to 3 attempts with exponential backoff
- Public URL Generated: On successful upload, public URL is constructed
Upload Timing
- Segments (
.ts): Uploaded immediately on creation - Playlists (
.m3u8): Uploaded on both creation and write (updates) - Delay: 200ms delay to ensure file is fully written before upload
Retry Mechanism
Failed uploads are retried up to 3 times:- Attempt 1: Immediate
- Attempt 2: 1 second delay
- Attempt 3: 2 second delay
File Structure in R2
Single-Profile Streams
Whenabr=false (default):
ABR Streams
Whenabr=true:
R2 Configuration
Environment Variables
Configure R2 access via environment variables:Setting Up R2
-
Create Bucket:
- Log into Cloudflare Dashboard
- Navigate to R2 → Create bucket
- Choose a unique bucket name
-
Create API Token:
- Go to R2 → Manage R2 API Tokens
- Click “Create API Token”
- Set permissions: Object Read & Write
- Save the Access Key ID and Secret Access Key
-
Get Account ID:
- Found in the R2 dashboard URL:
https://dash.cloudflare.com/{account_id}/r2 - Or in Account Settings → Account ID
- Found in the R2 dashboard URL:
-
Configure Public Access:
- Option A: Use R2.dev subdomain (automatic)
- Option B: Set up custom domain (recommended for production)
- Set
CLOUDFLARE_PUBLIC_URLto your public endpoint
Custom Domain Setup
For production, use a custom domain:-
Add Custom Domain in R2:
- Go to R2 → Your Bucket → Settings
- Add custom domain (e.g.,
streams.yourdomain.com) - Cloudflare will provision SSL automatically
-
Set Public URL:
-
CORS Configuration (if needed for web players):
- Go to R2 → Your Bucket → Settings → CORS Policy
- Add CORS rules for your player domain
Public URL Generation
Livetran constructs public URLs using the pattern:Webhook Integration
When the first playlist is uploaded, Livetran:- Generates Public URL: Constructs URL from
CLOUDFLARE_PUBLIC_URL - Updates Stream Status: Changes to
STREAMING - Sends Webhook: Includes
StreamLinkin webhook payload
Content Types
Livetran automatically sets correct MIME types:.m3u8files:application/vnd.apple.mpegurl.tsfiles:video/MP2T
HLS Playlist Structure
Single-Profile Playlist
ABR Master Playlist
Storage Considerations
File Lifecycle
- Active Streams: Files are continuously created and uploaded
- Stopped Streams: Files remain in R2 (not automatically deleted)
- Segment Retention: FFmpeg keeps last 10 segments in playlist (configurable)
Storage Costs
R2 pricing (as of 2024):- Storage: $0.015 per GB/month
- Class A Operations (writes): $4.50 per million
- Class B Operations (reads): $0.36 per million
- Egress: Free (unlike S3)
- 1 hour stream at 5 Mbps = ~2.25 GB
- Storage: $0.034/month
- Write operations: ~900 segments = $0.004
- Total: ~$0.04 per hour of stored content
Cleanup Strategy
Livetran doesn’t automatically delete old files. Consider:- R2 Lifecycle Rules: Set up automatic deletion after N days
- Manual Cleanup: Script to delete old streams
- Stream Management: Track stream end times and schedule cleanup
Performance & Optimization
Upload Speed
- Concurrent Uploads: Multiple files upload in parallel
- Network Bandwidth: Ensure sufficient upload bandwidth
- Retry Overhead: Failed uploads add latency
Monitoring Uploads
Check server logs for upload status:Optimization Tips
- Geographic Proximity: Deploy Livetran close to R2 endpoints
- Network Quality: Use stable, high-bandwidth connections
- Monitor Failures: Set up alerts for repeated upload failures
- CDN Caching: Use Cloudflare CDN for faster delivery
Troubleshooting
”Failed to initialise Uploader”
Cause: Invalid R2 credentials or network issues Solutions:- Verify
R2_ACCOUNT_ID,R2_ACCESS_KEY, andR2_SECRET_KEY - Check network connectivity to R2 endpoints
- Verify bucket exists and is accessible
”Upload failed, retrying…”
Cause: Network issues, rate limiting, or permissions Solutions:- Check network connectivity
- Verify R2 API token permissions
- Check R2 service status
- Review server logs for specific error messages
Files Not Appearing in R2
Cause: Upload failures or incorrect bucket name Solutions:- Verify
BUCKET_NAMEmatches actual bucket - Check upload logs for errors
- Verify file watcher is running (check process status)
- Ensure output directory is writable
Public URLs Not Working
Cause: IncorrectCLOUDFLARE_PUBLIC_URL or CORS issues
Solutions:
- Verify
CLOUDFLARE_PUBLIC_URLmatches your R2 public endpoint - Check CORS configuration in R2 bucket settings
- Test URL directly in browser
- Verify custom domain is properly configured
Playlist Not Updating
Cause: Playlist file not being re-uploaded on updates Solutions:- Verify file watcher detects write events
- Check that FFmpeg is updating the playlist
- Review upload logs for playlist uploads
- Ensure sufficient disk space
Best Practices
- Monitor Uploads: Set up logging/alerting for upload failures
- Lifecycle Management: Configure R2 lifecycle rules for cleanup
- CDN Integration: Use Cloudflare CDN for global delivery
- Custom Domain: Use custom domain for production (better branding)
- CORS Configuration: Set up CORS if using web-based players
- Backup Strategy: Consider backing up critical streams
- Cost Monitoring: Monitor R2 usage and costs
- Network Redundancy: Ensure reliable network connection
Integration with Video Players
HLS.js (Web)
Video.js
Native iOS/macOS
Next Steps
- Review Deployment for R2 setup
- Understand Ingestion for stream processing
- Check API Reference for webhook details
- Explore Introduction for architecture overview