Object Storage
Kuploy provides managed object storage — S3-compatible buckets for the files your app produces: user uploads, images, video, generated assets, exports. Like a database, a bucket is a resource you create in your project and connect to your app; the platform provisions it and injects the credentials for you, so you never paste keys.
When to use it
Reach for a bucket whenever your app stores files that must outlive a redeploy and be shared across replicas — anything you'd otherwise put on local disk (which is ephemeral and per-pod). Because it speaks the S3 API, any S3 client works: boto3, the AWS SDKs, aws-cli, mc, rclone, and most frameworks' file-storage adapters.
Creating a bucket
- Navigate to your project's environment.
- Click Create Service → Bucket (under the Storage category).
- Configure:
- Name — a label for the bucket (e.g.
media). - Quota (GiB) — the maximum the bucket may hold.
0means unlimited.
- Name — a label for the bucket (e.g.
- Click Create, then Provision.
Provisioning takes a few seconds. When it's done, the bucket's page shows its endpoint, bucket name, region, access key, and secret key (the secret is hidden until you reveal it, and every value has a copy button).
Connecting a bucket to your app
Don't copy the credentials by hand — connect the bucket to your service and Kuploy injects them as environment variables. In a stack, draw a connection from the bucket to your app and map each field to the env var your app expects:
| Bucket field | What it is | Typical env var |
|---|---|---|
endpoint | S3 API endpoint (private to your cluster) | S3_ENDPOINT / AWS_ENDPOINT_URL |
bucket | The provisioned bucket name | S3_BUCKET |
accessKey | Access key ID | AWS_ACCESS_KEY_ID |
secretKey | Secret access key | AWS_SECRET_ACCESS_KEY |
region | Region (arbitrary but required by most SDKs) | AWS_REGION |
The resolved values land in your app's Environment tab inside a managed block (marked do not edit) and update automatically — if you rotate or re-provision the bucket, connected apps pick up the new values on their next deploy.
The connection is one-directional and server-side: credentials are resolved into the consumer's environment at deploy time and never exposed to the browser. Keep them server-side in your app too (don't ship them to client-side code).
Example (Python / boto3)
With the env vars above injected, no credentials appear in your code:
import boto3, os
s3 = boto3.client(
"s3",
endpoint_url=os.environ["S3_ENDPOINT"],
aws_access_key_id=os.environ["AWS_ACCESS_KEY_ID"],
aws_secret_access_key=os.environ["AWS_SECRET_ACCESS_KEY"],
region_name=os.environ["AWS_REGION"],
)
s3.upload_file("thumbnail.png", os.environ["S3_BUCKET"], "courses/1/thumbnail.png")
Quotas
Each bucket has its own capacity limit. When a bucket reaches its quota, further uploads to that bucket fail with a quota error — other buckets and services are unaffected. Raise a bucket's quota from its settings, or set 0 for unlimited when you create it.
Monitoring usage
Once a bucket is provisioned, its detail page shows a Storage usage panel with live figures read straight from the storage backend:
- Objects — how many objects the bucket holds.
- Total size — the bytes stored.
- Quota fill — a bar showing how full the bucket is against its quota (hidden when the quota is
0/ unlimited). - A Reachable / Unreachable badge for the shared storage backend — if it can't be reached, the badge carries the reason instead of silently showing zeros.
For the whole picture across every bucket in your organization, open Settings → Organization storage (organization admins). It aggregates total objects, total size, and summed quota, with a per-bucket breakdown and each bucket's reachability — a quick way to see where your storage is going without opening each bucket in turn.
Access & isolation
Every bucket gets its own dedicated access key, scoped to that bucket alone — a bucket's credentials cannot read or write any other bucket. The endpoint is reachable only from within your cluster (your other services), not the public internet, so uploaded objects are served through your app rather than exposed directly.
Deleting a bucket
Delete a bucket from its detail page (you'll be asked to type its name to confirm). This permanently removes the bucket, everything stored in it, and its access key. There is no undo — download anything you need first.
Best practices
- Connect, don't paste — wire the bucket to your app so credentials inject automatically and rotate cleanly.
- One bucket per concern — separate, say, public media from private exports; each gets its own scoped key and quota.
- Set a quota — cap growth so one runaway feature can't fill the bucket unexpectedly.
- Keep keys server-side — never expose the injected
AWS_*vars to browser code; serve or proxy files through your app. - Store the path, not the file — keep object keys/paths in your database and stream from the bucket on demand.