Skip to main content

File storage: local or S3

Attachments, inline images, profile photos and data exports are stored by one of two drivers. File metadata is in the database; the files are on disk or in a bucket. Both are needed for a complete backup.

Local volume (default)​

STORAGE_DRIVER=local
STORAGE_LOCAL_DIR=/data/uploads

The example compose.yml mounts the uploads volume at /data/uploads. Files are only ever served through the app, after an access check. Nothing else to configure.

S3-compatible object storage​

Any S3 API works: AWS S3, Cloudflare R2, Hetzner Object Storage, Garage, SeaweedFS and others. The store must support multipart uploads and pre-signed download URLs.

To use it:

  1. Create a bucket (private; no public access needed).
  2. Create credentials that may read, write and delete objects in that bucket, including multipart uploads.
  3. Set the variables and restart with docker compose up -d:
STORAGE_DRIVER=s3
S3_ENDPOINT=https://s3.eu-central-1.amazonaws.com
S3_REGION=eu-central-1
S3_BUCKET=sol2flow-files
S3_ACCESS_KEY_ID=…
S3_SECRET_ACCESS_KEY=…
S3_FORCE_PATH_STYLE=false
ProviderS3_ENDPOINTS3_REGIONS3_FORCE_PATH_STYLE
AWS S3empty, or https://s3.<region>.amazonaws.comthe bucket's regionfalse
Cloudflare R2https://<account id>.r2.cloudflarestorage.comautotrue
Self-hosted (Garage, SeaweedFS, …)the store's S3 API addressas configuredtrue

Downloads go straight to the bucket​

When someone downloads a file, the app checks access and then redirects the browser to a short-lived signed link on the bucket. So the bucket's API address must be reachable from your users' browsers. If the app reaches the store under an internal name (for example http://garage:3900), set S3_PUBLIC_ENDPOINT to the public address browsers use. No CORS rule is needed.

Upload chunks and S3​

Uploads are sent in chunks (Admin → General → Uploads → Chunk size, default 8 MB). S3 requires parts of at least 5 MB (except the last), so on S3 the chunk size can't be set lower than 5 MB. A file can have at most 10,000 parts.

Clean up unfinished uploads​

sol2flow cancels abandoned uploads every day. As a second safety net, add a lifecycle rule to the bucket that aborts incomplete multipart uploads after 2–3 days.

Switching drivers​

You can switch from local to S3 (or back) at any time. New files go to the new storage; existing files are read from where they were stored, as long as both stay configured and reachable (keep the volume mounted and the S3_* variables set). Existing files are not moved to the new storage.

Upload limits​

The maximum file size and the chunk size are set in Admin → General → Uploads (see Instance settings). Remember the proxy body limit when you raise the chunk size.