PlatformFile storage
A private bucket, for your app's files.
Uploads need somewhere to live that survives a deploy. Your coding agent gives your app a private S3 bucket while it builds. Any S3 client works with it, and its key reaches that bucket and nothing else.
- S3-compatibleAny S3 client, SDK or library works with it
- Private by defaultEvery request needs the key or a link signed with it
- A key for one bucketIt reaches that bucket and nothing else
- Not meteredStorage is not metered or capped today
01How it works
Created while your agent builds.
Uploads written to the container's disk vanish on the next deploy. Your agent creates the bucket first, so uploads are written and tested against the real thing.
One call, before the first upload.
Your agent asks for the bucket before it writes the upload code. It is created, its variables are written to your .env by a redirect that never prints the key, and the deploy that follows gives the running app the same variables.
- Asking twice returns the same bucket, nothing is made twice
- The key goes into a file, never into the chat or the terminal
- A project that uses an S3 client and brought no key of its own gets a bucket at its first deploy, even without this call
$ API=https://antideploy.com/api/v1
# 1. create the bucket, once per project
$ curl -X POST "$API/storage?applicationId=$APP_ID" \
-H "Authorization: Bearer $TOKEN"
# 2. write its variables into .env (the answer holds a key)
$ curl -fsS "$API/storage/env?applicationId=$APP_ID" \
-H "Authorization: Bearer $TOKEN" >> .env
Use any S3 client. Let the browser upload directly.
Your code uses an S3 client the way it would anywhere. The AWS SDKs and boto3 read the AWS_* variables from the environment, so existing code that reads them and sets nothing else already works. For an upload from a browser, have your server return a presigned PUT URL and the file goes straight to the bucket.
- Path-style addressing, which current AWS SDKs choose for this bucket's name by themselves
- In new JavaScript code, set forcePathStyle to true anyway
- Browsers on any origin may call the bucket, and every request still needs the key or a signed link
// Node: the AWS SDK reads the AWS_* variables itself
import { S3Client, PutObjectCommand } from "@aws-sdk/client-s3";
import { getSignedUrl } from "@aws-sdk/s3-request-presigner";
const s3 = new S3Client({ forcePathStyle: true });
const url = await getSignedUrl(
s3,
new PutObjectCommand({ Bucket: process.env.BUCKET_NAME, Key: "avatars/maya.webp" }),
{ expiresIn: 300 },
);
// hand url to the browser, which PUTs the file straight to the bucket
# Python: boto3 reads the AWS_* variables itself
import os, boto3
s3 = boto3.client("s3")
s3.upload_file("avatar.webp", os.environ["BUCKET_NAME"], "avatars/maya.webp")
02What you get
Files that survive a deploy.
A container is replaced on every deploy and its disk goes with it. A bucket is not, and it belongs to your application.
-
A private bucket per app. One bucket for each application, on Neon Object Storage in Singapore, beside your app. Anonymous requests are refused.
-
A key that reaches one bucket. The access key is scoped to that bucket and nothing else, so a leaked key exposes one bucket's files and not an account.
-
Browsers upload straight to it. Your server hands the browser a presigned PUT URL and the file goes directly to the bucket without passing through your app. The same works for presigned downloads.
-
Uploads that outlive the container. Anything written to local disk is gone on the next deploy. Antideploy flags code that saves uploads to disk before anything is built, and points you at a bucket.
-
There at the first deploy, if your code needs it. For a project that uses an S3 client and brought no key of its own, the platform creates the bucket on the first deploy. A key you set yourself is never replaced.
-
Not metered. Storage is not metered or capped today. The number of buckets in your plan is the limit, and the table below is all of it.
03Limits by plan
Counted per account, not per app.
- 1
- bucket on Free
- 2
- buckets on Go
- 3
- buckets on Pro
- 5
- buckets on Scale
A bucket counts toward your allowance however it came about, whether your agent created it before the first deploy or a deploy created it. The storage inside it is not metered.
04Reference
The variables your app receives.
Set by the platform in the running app, and written to your .env by your agent for local work. Do not copy them into the secrets store.
| Variable | What it holds |
|---|---|
AWS_ACCESS_KEY_ID | The access key id for your bucket. |
AWS_SECRET_ACCESS_KEY | The secret for that key. It is never shown after it is written. |
AWS_ENDPOINT_URL_S3 | Where the S3 API answers, which current AWS SDKs read from the environment. |
AWS_REGION | The region the bucket is in. |
BUCKET_NAME | The bucket's name. It is also set as AWS_S3_BUCKET and in the common S3_* spellings. |
AWS_S3_FORCE_PATH_STYLE | A flag for libraries that read it. The AWS SDKs choose path style by themselves for this bucket. |
05Before you commit
Here's where it stops.
A platform that only tells you what it is good at is one you find the edges of in production. These are file storage's.
-
No public bucket
Every object is private. To show a file to a visitor, serve it through your app or hand out a signed link. There is no public URL and no built-in CDN.
-
Path-style addressing only
Current AWS SDKs and boto3 choose it by themselves for this bucket's name. Code that forces virtual-hosted addressing will not work.
-
Only S3 code gets a bucket
Code that talks to Cloudinary, UploadThing, Vercel Blob or Google Cloud Storage keeps using those. Antideploy only provisions a bucket for S3 code.
-
One region
Buckets are in Singapore, beside your app. Choosing a region is not offered today.
06Questions
Storage questions, answered.
Anything else? Write to us and a person answers.
support@antideploy.comWhere are my files stored?
In a private S3-compatible bucket on Neon Object Storage in Singapore, created for your application.
Which S3 clients work?
Any S3 client. Current AWS SDKs and boto3 read the AWS_* variables and choose path-style addressing by themselves. In new JavaScript code, set forcePathStyle: true anyway.
How do I let a browser upload a file?
Have your server create a presigned PUT URL and return it. The browser sends the file straight to the bucket. Browsers on any origin may call the bucket, and every request still needs the key or a link signed with it.
Can visitors open a file by its URL?
Not directly, because the bucket is private. Serve the file through your app, or give the visitor a signed link that expires.
How much can I store?
Storage is not metered or capped today. The limit is the number of buckets: 1 on Free, 2 on Go, 3 on Pro and 5 on Scale, counted per account.
Why not save uploads to disk?
The container's disk is replaced on every deploy, so anything saved to it disappears. Antideploy flags code that does this before it builds.
Can I use Cloudinary, UploadThing or Vercel Blob instead?
Yes. Antideploy only creates a bucket for code that uses an S3 client and brought no key of its own, and a key you set yourself is never replaced.
Should I copy the variables into my secrets?
No. The platform sets them in the running app, and a copy would shadow the real ones if the bucket were ever made again.
What if I reach my bucket limit?
Creating another answers with a plan-limit error and your agent tells you. Nothing already created is affected. You can delete an application you no longer need, or move to a plan with more.
+The rest of the platform
Everything else your app can use.
Every service is created by your coding agent, wired into your app, and included in the plans. See the whole platform.
- Hosting
- Static sites
- Deployments
- Database
- Authentication
- AI models
- Cron jobs
- Custom domains
- Environment variables
- Logs and monitoring
- Security checks
- Console
- Agent API
Facts on this page were checked against the live platform on 5 October 2026.
Give your app somewhere to keep its files.
Paste the sentence into your agent. It creates the bucket, writes the variables into your project and builds the upload code against the real thing.