Skip to content
STORAGE

A private bucket.
Any S3 library.

Every project gets its own S3-compatible bucket and a key that only works there. Upload straight from the browser with presigned URLs and manage files in a built-in browser.

S3-compatible Presigned uploads 500 MB on Free
STORAGE · LIVE
UPLOADPUTPRIVATEproj-appPNGPDFMP4FILESavatars/ada.pnginvoices/0042.pdf_backups/UPLOAD DOMAINShttps://app.acme.comhttps://*.acme.comhttp://localhost:*PRESIGNED URL?X-Amz-Expires=3600
500 MBincluded on the Free plan
$0.08per GB-month on Pro, with no size limit
1 hourlifetime of a link from Copy URL
WHAT YOU GET

Everything included.
Nothing to wire up.

A private S3-compatible bucket per project, with a file browser and presigned URLs.

Any S3 SDK

The AWS SDK or any other S3 library works. Point it at S3_ENDPOINT and set forcePathStyle to true.

Direct browser uploads

Your server signs a PUT URL and the browser sends the file straight to the bucket. It never passes through your server.

Private by default

The bucket is never public and its key can only list, read, write and delete in that one bucket. Share files with links that expire.

File browser

Browse folders, preview images, video, audio and PDFs, upload with progress, and see what uses the space.

Upload domains per environment

List the sites allowed to upload. Development already allows localhost on any port; production allows only what you add.

Backups live here

Nightly Postgres backups are stored in the bucket under _backups/postgres/, next to your own files.

HOW IT WORKS

Three steps
to production.

  1. 01
    Deploy

    Click Deploy on the Storage card. You get a bucket named proj-<slug> and a key for that bucket only.

  2. 02
    Connect

    Copy the five S3_ variables to your server and create an S3 client with forcePathStyle set to true.

  3. 03
    Upload

    Add your site under Allowed upload domains, sign PUT URLs on the server and let the browser send the files.

Read the Storage guide
lib/s3.ts
import { S3Client, PutObjectCommand } from "@aws-sdk/client-s3";
import { getSignedUrl } from "@aws-sdk/s3-request-presigner";

export const s3 = new S3Client({
  endpoint: process.env.S3_ENDPOINT,
  region: process.env.S3_REGION,
  forcePathStyle: true,
  credentials: {
    accessKeyId: process.env.S3_ACCESS_KEY_ID!,
    secretAccessKey: process.env.S3_SECRET_ACCESS_KEY!,
  },
});

// A link the browser can PUT a file to for one hour.
export const uploadUrl = (key: string, contentType: string) =>
  getSignedUrl(s3, new PutObjectCommand({
    Bucket: process.env.S3_BUCKET!, Key: key, ContentType: contentType,
  }), { expiresIn: 3600 });
BETTER TOGETHER

One project.
Every service connected.

Same project, same .env, same bill.

FAQ

Questions about
Storage.

Does it work with the AWS SDK?
Yes. Any S3 library works. Use S3_ENDPOINT and S3_REGION, and set forcePathStyle to true so the bucket name goes in the path.
Can I make files public?
No. Buckets are private. Share files with presigned links, which expire, or serve them through your own server.
Why does a browser upload fail with a CORS error?
The page's origin is missing from Allowed upload domains for that environment. Add it under Storage → Settings and click Save; the bucket is updated within a few seconds.
What happens when a Free bucket is full?
Uploads that would go over the limit are refused up front. A Free bucket over its limit for 3 days is paused, but deleting still works. Pro buckets have no size limit.

Add Storage to your project.
Free to start.

Postgres, auth, storage, Redis, email and AI models. One API key.