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

ProviderBest forTradeoff
Fly Object Storage (Tigris)Fly.io default stack with low setup overheadSmaller ecosystem than Amazon S3
Cloudflare R2CDN-heavy traffic and global edge deliveryDifferent operational model vs standard AWS workflows
Amazon S3AWS-native systems and widest integration ecosystemCan 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_REGION
  • AWS_ACCESS_KEY_ID
  • AWS_SECRET_ACCESS_KEY
  • S3_ENDPOINT
  • AWS_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

  • AccessDenied on 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.

See also: Application-layer implementation

On this page