File Storage
S3-compatible object storage recommendations.
When to use this page
- You need durable storage for uploads, media, exports, or generated assets.
- You want to choose between Fly Tigris, Cloudflare R2, and Amazon S3.
- You need S3-compatible configuration variables for your application.
Prerequisites
- Expected data volume and access pattern roughly defined.
- Decision whether files are public, private, or mixed.
- Backend ready to generate and consume signed URLs when needed.
Provider comparison
| Provider | Best for | Tradeoff |
|---|---|---|
| Fly Object Storage (Tigris) | Fly.io default stack with low setup overhead | Smaller ecosystem than Amazon S3 |
| Cloudflare R2 | CDN-heavy traffic and global edge delivery | Different operational model vs standard AWS workflows |
| Amazon S3 | AWS-native systems and widest integration ecosystem | Can add complexity/cost if rest of stack is not on AWS |
How to choose
- Use Fly Object Storage (Tigris) when your app runs on Fly.io and you want the simplest path.
- Use Cloudflare R2 when you expect heavy download traffic or need edge-adjacent storage.
- Use Amazon S3 when you are already on AWS or rely on AWS-native integrations.
Compatibility
All of these options are S3-compatible, so the same SDKs and tooling work across providers.
Required env vars
From any S3 provider, you should obtain the following environment variables:
AWS_REGIONAWS_ACCESS_KEY_IDAWS_SECRET_ACCESS_KEYS3_ENDPOINTAWS_BUCKET_NAME
Verify
- Upload test object from app backend.
- Fetch object via expected public/signed URL path.
- Confirm bucket permissions match your intended access model.
Troubleshooting
AccessDeniedon upload: Validate access key, secret, and bucket policy/ACL settings.- Signed URL works locally but not in production: Check region, endpoint, and server clock drift.
- Slow file delivery: Add CDN caching strategy and object compression where appropriate.