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.

Read the API contract
  • 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
See what your agent reads
Your agent runs
$ 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
POST /api/v1/storageGET /api/v1/storage/envGET /api/v1/storage

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
Pair it with a database
In your code
// 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.

VariableWhat it holds
AWS_ACCESS_KEY_IDThe access key id for your bucket.
AWS_SECRET_ACCESS_KEYThe secret for that key. It is never shown after it is written.
AWS_ENDPOINT_URL_S3Where the S3 API answers, which current AWS SDKs read from the environment.
AWS_REGIONThe region the bucket is in.
BUCKET_NAMEThe bucket's name. It is also set as AWS_S3_BUCKET and in the common S3_* spellings.
AWS_S3_FORCE_PATH_STYLEA 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.

  1. 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.

  2. 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.

  3. 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.

  4. 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.com
Where 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.

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.

Start from GitHub or a folder
Prompt copied Paste it into Claude Code, Codex or Cursor and press Enter.