Skip to content

Storage Configuration

The Kubernetes API uses object storage for app metadata, user content, and execution logs. The Helm chart can wire AWS S3, Azure Blob Storage, Google Cloud Storage, Cloudflare R2, or a generic S3-compatible endpoint, but the checked-in API image does not currently provide the same end-to-end support for every advertised option.

| storage.provider | Backing store | Scoped runtime credentials in the checked-in API | Current guidance | | --- | --- | --- | --- | | azure | Azure Blob Storage | Included | Supported by the stock build | | gcp | Google Cloud Storage | Included | Supported by the stock build | | r2 | R2 through its S3 API | Included | Use an existing Secret that also supplies R2_API_TOKEN | | aws | AWS S3 | The chart supports it, but the Kubernetes API target does not enable the aws feature | Rebuild the API with the aws feature before selecting it | | s3 | Generic S3-compatible endpoint | Resolves to the AWS runtime-credential implementation, which is absent from the stock build | Not an end-to-end stock-image option |

Configure three logical storage locations:

| Location | Purpose | | --- | --- | | Meta | App and board metadata | | Content | App files, user files, and workflow data | | Logs | Persisted run logs |

Create the buckets or containers before installing the chart; the chart does not provision them. They may be separate locations or the same physical bucket/container referenced by the same name. Meta falls back to content in the shared storage configuration when it is omitted, while the chart emits an explicit meta value. The optional CDN store also falls back to content.

Execution state is configured separately through the execution-state backend. It is not automatically stored in the meta bucket.

The chart can create the runtime Secret from values:

storage:
provider: azure
azure:
accountName: flowlikestorage
accountKey: replace-me
metaContainer: meta
contentContainer: content
logContainer: logs

Keep credentials in an uncommitted, access-controlled values file. For a pre-created Secret, set storage.azure.existingSecret instead. It must expose:

  • STORAGE_PROVIDER=azure
  • AZURE_STORAGE_ACCOUNT_NAME
  • AZURE_STORAGE_ACCOUNT_KEY
  • AZURE_META_CONTAINER
  • AZURE_CONTENT_CONTAINER
  • AZURE_LOG_CONTAINER

The account key is also used to mint container-scoped SAS credentials.

storage:
provider: gcp
gcp:
projectId: my-project
serviceAccountKey: |-
{"type":"service_account","project_id":"my-project"}
metaBucket: flow-like-meta
contentBucket: flow-like-content
logBucket: flow-like-logs

serviceAccountKey is the service-account JSON text consumed by GOOGLE_APPLICATION_CREDENTIALS_JSON; Kubernetes stringData performs the Secret's base64 encoding. Do not base64-encode the JSON a second time.

An existing GCP Secret must expose:

  • STORAGE_PROVIDER=gcp
  • GCP_PROJECT_ID
  • GOOGLE_APPLICATION_CREDENTIALS_JSON when the workload cannot use ambient credentials
  • GCP_META_BUCKET
  • GCP_CONTENT_BUCKET
  • GCP_LOG_BUCKET

Select it with:

storage:
provider: gcp
gcp:
existingSecret: flow-like-storage

R2 uses the AWS-compatible object-store adapter for backing storage and its own temporary-credential API for runtime scoping. Create a private environment file for the Secret:

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

Create the Secret and select it:

Terminal window
kubectl -n flow-like create secret generic flow-like-storage \
--from-env-file=flow-like-r2.env \
--dry-run=client -o yaml \
| kubectl apply -f -
storage:
provider: r2
r2:
existingSecret: flow-like-storage

Grant the API token only the account-level R2 permissions required to create temporary credentials. Keep the S3 access keys and API token out of checked-in values.

After rebuilding the Kubernetes API with the flow-like-api/aws feature, AWS storage requires:

  • A workload identity or static AWS credentials that can access the configured buckets
  • RUNTIME_ROLE_ARN, whose role can be assumed by the API identity
  • An IAM policy on that runtime role that permits the object operations Flow-Like narrows with per-request session policies

For EKS workload identity, use an existing Secret that omits AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY entirely:

STORAGE_PROVIDER=aws
AWS_REGION=eu-central-1
META_BUCKET=flow-like-meta
CONTENT_BUCKET=flow-like-content
LOG_BUCKET=flow-like-logs
RUNTIME_ROLE_ARN=arn:aws:iam::123456789012:role/FlowLikeRuntimeRole
serviceAccount:
create: true
annotations:
eks.amazonaws.com/role-arn: arn:aws:iam::123456789012:role/FlowLikeApiRole
storage:
provider: aws
aws:
existingSecret: flow-like-storage

The chart-generated AWS Secret always writes the static credential keys, even when their values are empty. An explicit empty value is still a configured environment value and can prevent the AWS credential chain from using workload identity. Omitting those keys in an existing Secret avoids that misconfiguration.

The chart's s3 block supplies both S3_* settings for the Kubernetes storage adapter and AWS_* aliases used by the shared API layer:

storage:
provider: s3
s3:
endpoint: https://objects.example.com
region: us-east-1
accessKeyId: replace-me
secretAccessKey: replace-me
metaBucket: flow-like-meta
contentBucket: flow-like-content
logBucket: flow-like-logs
usePathStyle: true

This is enough to describe backing-store access after enabling the API's aws feature. It is not proof that the service supports Flow-Like's scoped runtime-credential path: s3 selects the AWS implementation, which calls AWS STS AssumeRole and requires RUNTIME_ROLE_ARN. Do not treat MinIO or another S3-compatible service as end-to-end supported without validating that path.

Flow-Like loads a master provider from RUNTIME_CREDENTIALS_PROVIDER, falling back to STORAGE_PROVIDER when the former is absent. Explicit empty values do not trigger the fallback and are configuration errors.

For app and run operations, the API derives temporary credentials narrowed to paths such as:

  • apps/{app_id}/
  • users/{user_id}/apps/{app_id}/
  • runs/{app_id}/
  • tmp/user/{user_id}/apps/{app_id}/
  • tmp/global/apps/{app_id}/

AWS uses AssumeRole with an inline session policy, Azure creates SAS tokens, GCP creates signed or token-based scoped access, and R2 calls its temporary credentials API. Provider-side IAM remains the outer permission boundary.

Render the selected provider before installing:

Terminal window
helm lint apps/backend/kubernetes/helm \
--values flow-like-values.yaml
helm template flow-like apps/backend/kubernetes/helm \
--namespace flow-like \
--values flow-like-values.yaml

After installation, verify readiness without printing Secret values:

Terminal window
kubectl get deployment flow-like-api -n flow-like
kubectl logs deployment/flow-like-api -n flow-like

Startup errors mentioning the provider, master credentials, or a missing bucket/container usually indicate either a missing Secret key, an explicit empty value, a bucket that was not created, or a provider feature that is not present in the API image.