Overview
Kaneo uses S3-compatible object storage for uploads in:- task descriptions
- task comments
- images render inline
- non-image files such as CSV, PDF, and ZIP render as attachment cards/links
- assets are private by default
S3_ENDPOINTmust be reachable by the browser
http://minio:9000 for a public deployment unless the browser can actually reach it.
Required Kaneo variables
S3_FORCE_PATH_STYLE=trueis usually needed for MinIO.S3_FORCE_PATH_STYLE=falseis usually correct for AWS S3 and R2.S3_PUBLIC_BASE_URLis optional and not required for the current private asset flow.
MinIO
MinIO is the recommended self-hosted option.Local Docker setup
For local development, this is fine:localhost.
Public deployment
For a public deployment, expose MinIO on its own hostname through your reverse proxy. Example:- Kaneo:
https://cloud.kaneo.app - MinIO:
https://files.cloud.kaneo.app
- a created bucket
- a dedicated, non-root MinIO user with access limited to that bucket
- MinIO CORS allowing your Kaneo origin
- no anonymous bucket read policy is required
AWS S3
AWS S3 is the simplest managed option. Use a bucket and an IAM user with access to that bucket. Example:S3_ENDPOINT
Using an IAM role instead of an access key
If Kaneo runs on AWS (EC2, ECS, or EKS), you can omitS3_ACCESS_KEY_ID and
S3_SECRET_ACCESS_KEY entirely and let Kaneo authenticate with the instance’s
IAM role. Leave both unset and the AWS SDK resolves credentials from its default
provider chain (instance profile, ECS task role, or EKS IRSA).
s3:PutObject, s3:GetObject, and s3:DeleteObject on the bucket.
Recommended S3 CORS policy:
Cloudflare R2
R2 works well because it exposes an S3-compatible API. Use your account endpoint, bucket, and R2 access keys. Example:S3_REGION=autois typical for R2- a public bucket is not required for Kaneo’s current private asset flow
One copy-paste self-hosted example
This example gives you Kaneo + MinIO in one Compose file..env:
- Create the
kaneo-uploadsbucket. - Configure MinIO CORS for your Kaneo origin.
- Make sure
files.cloud.kaneo.appresolves publicly.
Troubleshooting
If uploads fail:- check that the bucket exists
- check that
S3_ENDPOINTis public and browser-reachable - check CORS on your storage backend
- check that the access key can
PutObjectandGetObject