Skip to content

Storage Providers

Flow-Like uses object storage for app metadata, user content, CDN/public content, and execution logs. The Compose stack does not create buckets or containers.

| Configuration | Checked-in Compose images | Notes | | --- | --- | --- | | Azure Blob Storage | Supported | API includes Azure runtime credentials | | Google Cloud Storage | Supported | API includes GCP runtime credentials | | Cloudflare R2 | Supported with an explicit R2 runtime override | Uses the AWS-compatible backing-store adapter and R2 temporary credentials | | AWS S3 | Backing-store adapter is present, but the API image omits the flow-like-api/aws feature | Rebuild the API with that feature | | Other S3-compatible services | Same missing AWS runtime feature, plus unverified STS behavior | Not a stock end-to-end option |

The Docker Compose API target enables azure, gcp, and r2 runtime credentials. It does not enable aws, even though the Compose file's interpolation default and .env.example currently select aws.

The Compose template injects the runtime-provider, CDN, and provider-specific bucket variables even when their .env values are empty. The Rust configuration treats an explicit empty value as configured; it does not fall through to another variable.

For the selected provider:

  • set RUNTIME_CREDENTIALS_PROVIDER to a non-empty provider name;
  • set CDN_BUCKET_NAME to a real bucket/container, commonly the content location;
  • set all selected provider-specific meta, content, and log names;
  • do not expect empty credential fields to activate an ambient credential chain.

The generic logical names should also be set:

META_BUCKET=flow-like-meta
CONTENT_BUCKET=flow-like-content
LOG_BUCKET=flow-like-logs
CDN_BUCKET_NAME=flow-like-content

Meta and CDN can share the content location by using the same name. Execution state is configured separately through EXECUTION_STATE_BACKEND; it is not stored in the meta bucket by default.

STORAGE_PROVIDER=azure
RUNTIME_CREDENTIALS_PROVIDER=azure
AZURE_STORAGE_ACCOUNT_NAME=replace-me
AZURE_STORAGE_ACCOUNT_KEY=replace-me
AZURE_META_CONTAINER=meta
AZURE_CONTENT_CONTAINER=content
AZURE_LOG_CONTAINER=logs
META_BUCKET=meta
CONTENT_BUCKET=content
LOG_BUCKET=logs
CDN_BUCKET_NAME=content

Create all three containers before starting the API. The account key is also used to mint short-lived SAS credentials for scoped access.

Terminal window
az storage container create --name meta --account-name replace-me
az storage container create --name content --account-name replace-me
az storage container create --name logs --account-name replace-me
STORAGE_PROVIDER=gcp
RUNTIME_CREDENTIALS_PROVIDER=gcp
GCP_PROJECT_ID=replace-me
GOOGLE_APPLICATION_CREDENTIALS_JSON={"type":"service_account","project_id":"replace-me"}
GCP_META_BUCKET=flow-like-meta
GCP_CONTENT_BUCKET=flow-like-content
GCP_LOG_BUCKET=flow-like-logs
META_BUCKET=flow-like-meta
CONTENT_BUCKET=flow-like-content
LOG_BUCKET=flow-like-logs
CDN_BUCKET_NAME=flow-like-content

GOOGLE_APPLICATION_CREDENTIALS_JSON is JSON text, not a path. The Compose template injects this variable even when it is empty, so provide valid JSON instead of relying on an empty value to select ambient credentials.

Grant the service account access only to the configured buckets and validate the token-exchange/downscoping path as well as ordinary object access.

Keep the backing-store selector on the AWS-compatible adapter and select R2 only for runtime credentials:

STORAGE_PROVIDER=aws
RUNTIME_CREDENTIALS_PROVIDER=r2
AWS_ENDPOINT=https://replace-me.r2.cloudflarestorage.com
AWS_REGION=auto
AWS_USE_PATH_STYLE=true
AWS_ACCESS_KEY_ID=replace-me
AWS_SECRET_ACCESS_KEY=replace-me
AWS_META_BUCKET=flow-like-meta
AWS_CONTENT_BUCKET=flow-like-content
AWS_LOG_BUCKET=flow-like-logs
R2_ENDPOINT=https://replace-me.r2.cloudflarestorage.com
R2_ACCOUNT_ID=replace-me
R2_ACCESS_KEY_ID=replace-me
R2_SECRET_ACCESS_KEY=replace-me
R2_API_TOKEN=replace-me
META_BUCKET=flow-like-meta
CONTENT_BUCKET=flow-like-content
LOG_BUCKET=flow-like-logs
CDN_BUCKET_NAME=flow-like-content

The R2 API token is separate from the S3-compatible key pair. It is used to mint prefix-scoped temporary credentials, so restrict it to the required account and R2 operations.

AWS requires rebuilding docker-compose-api with the flow-like-api/aws feature. After doing so, configure both backing storage and credential scoping:

STORAGE_PROVIDER=aws
RUNTIME_CREDENTIALS_PROVIDER=aws
AWS_REGION=eu-central-1
AWS_USE_PATH_STYLE=false
AWS_ACCESS_KEY_ID=replace-me
AWS_SECRET_ACCESS_KEY=replace-me
AWS_META_BUCKET=flow-like-meta
AWS_CONTENT_BUCKET=flow-like-content
AWS_LOG_BUCKET=flow-like-logs
META_BUCKET=flow-like-meta
CONTENT_BUCKET=flow-like-content
LOG_BUCKET=flow-like-logs
CDN_BUCKET_NAME=flow-like-content
RUNTIME_ROLE_ARN=arn:aws:iam::123456789012:role/FlowLikeRuntimeRole

The API identity needs bucket access and permission to assume RUNTIME_ROLE_ARN. Flow-Like adds per-request inline session policies that narrow the role to app, user, temporary, and run-log prefixes.

The supplied Compose service always injects the static AWS keys. Empty values remain explicit credentials and can prevent the AWS credential chain from using workload identity. To use an instance or web identity, add a Compose override that removes those environment entries entirely and mounts the identity material required by the platform.

The META_BUCKET_EXPRESS_ZONE, CONTENT_BUCKET_EXPRESS_ZONE, and LOGS_BUCKET_EXPRESS_ZONE names still appear in the Compose template, but the current storage code does not read them. They do not enable S3 Express.

An endpoint such as MinIO can implement the object operations used by the backing-store adapter, but STORAGE_PROVIDER=aws also selects the AWS runtime credential implementation. That implementation calls AWS STS AssumeRole; basic S3 API compatibility is insufficient.

Do not describe an S3-compatible provider as end-to-end supported until its scoped runtime-credential path has been implemented and tested. The API does not read MINIO_* variables.

Check interpolation without printing secrets:

Terminal window
docker compose config --quiet

Then inspect only the non-secret selection and names:

Terminal window
docker compose run --rm --no-deps api sh -lc \
'printf "storage=%s runtime=%s meta=%s content=%s logs=%s cdn=%s\n" \
"$STORAGE_PROVIDER" "$RUNTIME_CREDENTIALS_PROVIDER" \
"$META_BUCKET" "$CONTENT_BUCKET" "$LOG_BUCKET" "$CDN_BUCKET_NAME"'

Start or recreate the API and review its logs:

Terminal window
docker compose up -d --build api api-gateway
docker compose logs api

Test metadata, content, temporary-file, database, and log operations. A successful bucket listing does not exercise temporary credential scoping.