speak-mintlify uploads generated audio files to S3-compatible storage for efficient delivery. This guide covers setup for AWS S3, Cloudflare R2, and self-hosted MinIO.
Storage Providers Overview
AWS S3
Industry standard, global CDN, pay-per-use pricing
Cloudflare R2
Zero egress fees, fast global delivery, simple pricing
MinIO
Self-hosted, full control, no vendor lock-in
AWS S3 Setup
Create an S3 bucket
- Log in to AWS Console
- Click Create bucket
- Choose a unique bucket name (e.g.,
my-docs-audio) - Select a region close to your users (e.g.,
us-east-1) - Uncheck “Block all public access” (we need public read access)
- Click Create bucket
Configure bucket policy
Add a bucket policy to allow public read access:
- Go to your bucket → Permissions → Bucket Policy
- Add this policy (replace
YOUR_BUCKET_NAME):
Create IAM user
Create an IAM user with programmatic access:
- Go to IAM Console → Users
- Click Add users
- User name:
speak-mintlify-uploader - Select Access key - Programmatic access
- Click Next: Permissions
Attach permissions
Create an inline policy with these permissions:Complete the user creation and save the Access Key ID and Secret Access Key.
Optional: CloudFront CDN
For better performance, serve audio through CloudFront:Create CloudFront distribution
- Go to CloudFront Console
- Click Create distribution
- Origin domain: Select your S3 bucket
- Origin access: Legacy access identities → Create new OAI
- Viewer protocol policy: Redirect HTTP to HTTPS
- Click Create distribution
Update bucket policy
CloudFront will update your bucket policy automatically. Remove the public access policy if using CloudFront exclusively.
Cloudflare R2 Setup
Cloudflare R2 offers zero egress fees and is more cost-effective for high-traffic sites.Create R2 bucket
- Log in to Cloudflare Dashboard
- Go to R2 from the sidebar
- Click Create bucket
- Enter bucket name (e.g.,
docs-audio) - Choose a location hint (optional)
- Click Create bucket
Enable public access
- Go to your bucket → Settings
- Scroll to Public access
- Click Allow Access and confirm
- Copy the Public bucket URL (e.g.,
https://pub-abc123.r2.dev)
Create API token
- Go to R2 → Manage R2 API Tokens
- Click Create API token
- Token name:
speak-mintlify - Permissions: Object Read & Write
- (Optional) Apply to specific buckets only
- Click Create API token
- Save the Access Key ID and Secret Access Key
Custom Domain for R2
Use your own domain for cleaner URLs:Add custom domain
- Go to your R2 bucket → Settings → Custom Domains
- Click Connect domain
- Enter your domain (e.g.,
audio.docs.com) - Follow DNS setup instructions
MinIO Setup (Self-Hosted)
MinIO is perfect for self-hosted or on-premise deployments.Create bucket
- Access MinIO Console at
http://localhost:9001 - Login with root credentials
- Go to Buckets → Create Bucket
- Bucket name:
docs-audio - Click Create Bucket
Set bucket to public
- Go to your bucket → Anonymous → Add Access Rule
- Prefix:
*(all objects) - Access: readonly
- Click Add
Create access key
- Go to Access Keys → Create access key
- Copy the Access Key and Secret Key
- Click Create
Testing Your Setup
Verify your storage configuration works:- Files upload successfully
- Public URLs are accessible
- Audio plays in your browser
- Cleanup correctly identifies orphaned files
Environment Variables Reference
Your S3 bucket name
S3 access key ID (or R2/MinIO equivalent)
S3 secret access key
Public URL where files are accessible. Examples:
- AWS S3:
https://bucket.s3.region.amazonaws.com - CloudFront:
https://d123.cloudfront.net - R2:
https://pub-abc.r2.dev - MinIO:
https://minio.example.com/bucket
AWS region (or
auto for R2)Custom S3 endpoint (required for R2 and MinIO). Examples:
- R2:
https://abc123.r2.cloudflarestorage.com - MinIO:
https://minio.example.com - Not needed for AWS S3
Troubleshooting
Upload fails with 403 Forbidden
Upload fails with 403 Forbidden
AWS S3:
- Check IAM user has
s3:PutObjectpermission - Verify bucket name in IAM policy matches actual bucket
- Ensure access keys are correct
- Verify API token has “Object Read & Write” permissions
- Check token is applied to correct bucket
- Ensure endpoint URL includes account ID
- Check access key has write permissions
- Verify bucket policy allows uploads
- Check MinIO server is running
Public URLs return 403 or 404
Public URLs return 403 or 404
Check public access:
- AWS S3: Verify bucket policy allows public read
- R2: Ensure “Allow Access” is enabled
- MinIO: Check anonymous access rules
- Remove trailing slashes from
S3_PUBLIC_URL - Verify bucket name is correct
- Test URL directly in browser
CORS errors in browser
CORS errors in browser
Add CORS configuration to your bucket:AWS S3:Cloudflare R2:
CORS is automatically configured for public buckets.MinIO:
Connection timeout to S3_ENDPOINT
Connection timeout to S3_ENDPOINT
- Verify endpoint URL is correct (including protocol)
- Check firewall/security groups allow connections
- For MinIO, ensure server is accessible from CI/CD
- Test with
curloraws s3 ls
Files uploaded but not accessible
Files uploaded but not accessible
- Check
S3_PUBLIC_URLmatches actual public URL - Verify content-type is set correctly (
audio/mpeg) - Check bucket/object ACLs (legacy AWS S3)
- Test URL in incognito/private browser
Best Practices
Use separate buckets
Keep audio files in a dedicated bucket separate from other assets for easier management.
Enable versioning
Enable S3 versioning to recover from accidental deletions.
Set lifecycle policies
Automatically delete old versions or move to cheaper storage tiers.
Monitor costs
Track storage and bandwidth usage, especially with AWS S3.
Use CDN
Serve through CloudFront, Cloudflare, or similar for better performance.
Secure credentials
Never commit credentials. Use environment variables or secrets managers.
Cost Optimization
- AWS S3
- Cloudflare R2
- MinIO
- Storage: ~$0.023/GB/month (Standard)
- Bandwidth: $0.09/GB egress (first 10TB/month)
- Requests: Minimal cost for PUT/GET
- Use S3 Intelligent-Tiering for infrequently accessed files
- Enable CloudFront to reduce egress costs
- Set lifecycle rules to delete old versions
Next Steps
Fish Audio Setup
Configure Fish Audio API and choose voices
CI/CD Integration
Automate audio generation in your workflow
