STORAGE_DIR, a Docker volume). That works out of the box, but an S3 backend adds presigned or proxied transfers, multipart uploads, and — most importantly — enables the S3-compatible gateway at /storage/v1/s3, so aws CLI, rclone, boto3, and Terraform can talk to your InsForge Storage directly.
You have three options:
Bring your own S3-compatible store
Set these in your.env (all production compose files pass them through to the backend) and restart:
AWS_S3_BUCKET / AWS_REGION names still work as fallbacks, but the S3_* names are preferred — the store doesn’t have to be AWS.
Storage auto-wires to the bucket on startup — no other configuration. The S3 gateway turns on automatically.
Provider notes:
- AWS S3, Wasabi, MinIO (public endpoint) — works with the defaults. Clients upload/download via presigned URLs directly against the store.
- Cloudflare R2 — set
S3_USE_PRESIGNED_URLS=false. R2 does not support S3 POST policies, which presigned browser uploads require; proxy mode routes all bytes through the backend instead. - Tencent COS, Aliyun OSS — set
S3_FORCE_PATH_STYLE=false(they require virtual-hosted-style addressing). - Store on a private network (a MinIO/RustFS/Ceph the browser can’t reach) — set
S3_USE_PRESIGNED_URLS=false.
Presigned vs. proxy mode
S3_USE_PRESIGNED_URLS (default true) decides how object bytes reach clients:
- Presigned (default): the backend hands clients short-lived URLs signed against
S3_ENDPOINT_URL; browsers transfer directly with the store. Requires the endpoint to be reachable by browsers and to support POST policies. - Proxy (
false): strategies point at the backend’s own routes and every byte streams through it — the store can stay completely private. Ranged downloads (Rangeheaders, media seeking) are supported. Uploads through the REST API/SDK are capped by the storage max-file-size setting (Dashboard → Storage → Settings, default 50 MB); larger objects go through the S3 gateway, which streams and supports multipart up toS3_MAX_OBJECT_SIZE_BYTES(default 5 GB per part/put).
Bundled MinIO
Run MinIO next to InsForge with a single overlay — no.env editing:
minio, creates the backing bucket, and points the backend at it in proxy mode. MinIO stays on the internal Docker network — it exposes no host ports and never needs TLS or a domain of its own.
Change the default credentials before production use:
Bundled RustFS
Same shape with RustFS (Apache-2.0 licensed, written in Rust):docker-compose.yml) and the deploy/docker-compose/docker-compose.yml self-host file (place the overlay next to it).
Dokploy
Dokploy takes a single compose file, so overlays don’t apply. Two options:- External store — set
S3_BUCKET,S3_ENDPOINT_URL,S3_ACCESS_KEY_ID,S3_SECRET_ACCESS_KEY,S3_FORCE_PATH_STYLE=true(andS3_USE_PRESIGNED_URLS=falsefor private endpoints) in Dokploy’s environment UI.docker-compose.dokploy.ymlpasses them through. - MinIO/RustFS on the same Dokploy host — deploy the store as its own Dokploy service, then point the env vars above at its internal address.
Using the S3-compatible gateway
With any S3 backend configured, the gateway is live at<your-backend-url>/storage/v1/s3:
- Open Dashboard → Storage → Settings → S3 Configuration and create an access key. The secret is shown once.
- Point any SigV4 client at the gateway with path-style addressing:
Reference
The
AWS_* variables (AWS_S3_BUCKET, AWS_REGION, AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY, AWS_CLOUDFRONT_*) are cloud-project and legacy names: the first two work as fallbacks for S3_BUCKET/S3_REGION, the credentials double as CloudWatch/CloudFront credentials, and CloudFront only applies to AWS S3 without a custom endpoint. Self-hosted deployments should stick to S3_*.
Upgrading from an AWS_* configuration
Older versions of deploy/docker-compose/docker-compose.yml did not pass storage variables at all, and the deployment guide recommended hand-adding AWS_S3_BUCKET (and friends) to the insforge service. If you did that: the current compose file passes only the S3_* names. Before adopting it, rename the variables in your .env to S3_BUCKET / S3_REGION / S3_ACCESS_KEY_ID / S3_SECRET_ACCESS_KEY — or keep your hand-edited environment block. The backend itself still honors AWS_* as fallbacks, so any value that reaches the container keeps working; the risk is only a compose file that no longer forwards them, which would silently fall back to local storage.