Skip to main content
Paygo requires available spending authority. New managed-storage provisioning, signed URLs, and restores are blocked when prepaid funds or the monthly hard limit are exhausted. A hard stop disables S3 keys. Adding funds does not raise an existing positive hard limit; check both on Billing. See Prepaid paygo before provisioning.
Declare storage in floo.app.toml, then deploy:
floo.app.toml
The CLI alternative, floo services add storage --name files, provisions through the API and does not write floo.app.toml. Keep the declaration in Git for auditable configuration. Pick a name other than default if your app already has a [managed.default] section; the starter template uses it for Postgres. The deploy creates private, separate buckets for dev and prod. If your app has multiple services, attach storage to each service that needs it with managed = ["storage:files"]. See Managed services.

Environment variables

A named service appends its name: [managed.files] gets STORAGE_BUCKET_FILES, STORAGE_S3_ENDPOINT_FILES, and the other keys with _FILES appended, as in the examples below. Only a service named default gets the unsuffixed keys in the table. floo does not set AWS_* variables, so your own AWS credentials stay separate. Pass the STORAGE_S3_* values to each client explicitly. S3 keys exist only for storage services created on or after v2026.09.27.1 (September 27, 2026). An older service has no S3 keys: keep using the Google SDK below, or provision a new named service and migrate and verify its data before removing the old one. Legacy STORAGE_URL values target an admin-only endpoint that apps cannot call; use the SDK variables instead.

Use it from code

The S3 examples need boto3/botocore 1.36 or later, or a current AWS SDK in other languages: older versions do not accept request_checksum_calculation. floo’s S3 interface uses the XML API with HMAC credentials. It supports object get, put, list, delete, multipart upload, and presigned SigV4 URLs. Configure the endpoint and credentials explicitly.
Recent AWS SDKs send optional checksums that this XML API rejects. The settings above send them only when an operation requires them. See the AWS checksum settings.

Rails Active Storage

Add gem "aws-sdk-s3", require: false and configure the S3 service:
config/storage.yml
Set config.active_storage.service = :amazon in the environment config. See Rails and the Active Storage S3 service.

Google SDK alternative

The Google storage SDK still uses Application Default Credentials in a deployed app. It needs only STORAGE_BUCKET_FILES:
Python

Uploads from a browser

A backend or agent with an ADMIN-scoped floo API key can request a signed upload policy. Keep that key on the server. Send this request to the floo API:
The response contains url and fields. Give those to the browser, which submits every returned field plus the file as a multipart form:
Browser
For a GET download, send {"method":"GET","object_path":"uploads/photo.png"} to the same endpoint and use the returned url. GET expiry is 60–3600 seconds (default 900). PUT and POST requests both return a POST policy, require an uploads/ path and content_type, expire in 60–900 seconds, and allow 1–100 MiB (max_bytes defaults to 10 MiB). The admin endpoint refuses new URLs and policies during a spend-cap hard stop or when prepaid spending authority is exhausted. An app can also sign its own S3 GET URL with its injected keys. Using the Python client above:
Python
This uses SigV4 with HMAC credentials and the boto3 presigning API. Give the URL only to its intended recipient. New buckets allow browser GET, POST, and HEAD requests through CORS; browser PUT requests are not allowed by that default policy.

RAG and AI ingestion

Store source documents under a prefix such as sources/, then load them into your chunking and embedding pipeline. LangChain’s S3FileLoader and S3DirectoryLoader accept endpoint and credentials directly:
Python
For one file, use S3FileLoader(bucket, key, ...) with the same keyword arguments. LlamaIndex S3Reader uses bucket, prefix or key, s3_endpoint_url, aws_access_id, and aws_access_secret. Feed the loaded documents into your parser, splitter, and index; the bucket stores the source files, not the index.

Large files and multipart

Use an S3 client’s multipart upload for files larger than the signed POST policy’s 100 MiB ceiling. XML API multipart uploads upload parts and then assemble the object. Unfinished parts count toward storage until aborted or cleaned up. Multipart uploads have different checksum behavior from single PUTs; do not rely on a multipart ETag as an MD5 hash.

Deleting, versions, and restore

ls, usage, and rm require CLI v2026.09.27 or later. For the API, use /v1/apps/{app_id}/managed-services/{service_id} as the base path. Object listing and usage require a READ-scoped key; delete, versions, and restore require an ADMIN-scoped key: Use env=prod for the production bucket. Delete returns 409 if the bucket cannot guarantee recovery. See the CLI reference for flags. Deleted and overwritten versions remain restorable for 30 days. Restore copies a selected generation over the live path. Use versions to get its generation ID before restoring.

Previews and dev versus prod

Dev and prod have separate buckets and keys. Dev and preview keys can reach only that app’s dev or preview buckets, never prod. Prod keys can reach only prod. floo dev injects STORAGE_BUCKET and the S3 endpoint, key ID and region locally but withholds the write-only secret, and your own local Google credentials have no access to floo’s buckets. Run storage code against your deployed dev environment for now.

Pricing and limits

Stored bytes (including old versions retained for 30 days), write and list operations, read operations, and internet downloads from the bucket are metered. Retained data and late usage can continue to accrue charges during a stop; prepaid paygo collects unpaid usage under the saved-card consent. A saved card does not authorize new spending. Rates are on getfloo.com/pricing. Reads from your app’s floo services in the same region incur no storage egress charge. A spend-cap hard stop disables S3 keys and new floo signed URLs until the stop clears. An app can declare at most 10 storage services.

Not supported

S3 bucket policies, bucket notifications, and requester-pays settings do not map directly to the XML API. Object ACL changes are unavailable on floo’s private buckets. Signed V4 requests cannot use chunked transfer encoding. Browser PUT uploads are not in the default CORS policy.