Docs · Configure
Storage
Open an environment’s Storage tab and choose Add storage. You get a private S3-compatible bucket for that environment, with a key that can reach it and nothing else, set as variables. Any S3 client or SDK works with it, and downloads are free.
The variables it sets
Both the names the AWS SDKs and Laravel read, and a plain set:
AWS_ACCESS_KEY_ID=…
AWS_SECRET_ACCESS_KEY=…
AWS_DEFAULT_REGION=auto
AWS_BUCKET=…
AWS_ENDPOINT=https://…
AWS_USE_PATH_STYLE_ENDPOINT=true
S3_BUCKET=…
S3_ENDPOINT=…
S3_REGION=auto
S3_ACCESS_KEY_ID=…
S3_SECRET_ACCESS_KEY=…A Laravel app also gets FILESYSTEM_DISK=s3, so Storage::put() and uploads go to the bucket with no code changes. Install the S3 driver if you have not: composer require league/flysystem-aws-s3-v3.
Like a database’s, these variables are managed: they cannot be edited by hand, and storage cannot be added while you have a variable of one of these names already. Reset keys makes a new key, restarts the app with it, then revokes the old one.
Using it
- The bucket is private. Serve files through your app, or hand out presigned URLs from your S3 SDK for direct downloads.
- Each environment has its own bucket, so staging never writes to production’s files.
- Empty bucket deletes every file in it, after you type its name. The bucket and its key stay.
import { S3Client } from 'bun'
const s3 = new S3Client({
bucket: process.env.S3_BUCKET,
endpoint: process.env.S3_ENDPOINT,
accessKeyId: process.env.S3_ACCESS_KEY_ID,
secretAccessKey: process.env.S3_SECRET_ACCESS_KEY,
})
await s3.write('hello.txt', 'Hello')Public buckets
On a paid plan, an environment can also have a public bucket, beside its private one or alone. Every file in it is served to anyone at its own HTTPS address, which suits images, downloads and other assets. The whole bucket is public; a private bucket is never served.
- Its variables have
PUBLICin their names, likeAWS_PUBLIC_BUCKETandAWS_PUBLIC_URL, so they never replace the private bucket’s. It is never Laravel’s default disk: a plainStorage::put()can’t land where anyone can read it. Point ans3-publicdisk at it and useStorage::disk('s3-public'). - It is billed like any bucket, and serving its files is free: reads are counted as reads.
- Starter has 2 public buckets of up to 25 GB each, Growth 10 of up to 250 GB, Business as many as it uses.
Deleting and restoring
Deleting storage removes its variables and revokes its key at once, but keeps the files for 7 days. Until then, any of the team’s environments can restore it, with a new key. After 7 days the files are deleted for good.
What it costs
| Free | Paid plans | |
|---|---|---|
| Buckets | 1 per Free team | One per environment, as many as you need |
| Data stored | Up to 1 GB | $0.02 per GB-month |
| Writes and lists (Class A) | 100,000 a month | $6 per million |
| Reads (Class B) | 1,000,000 a month | $0.50 per million |
| Data out | Free | Free |
| Billed | Never | From your plan’s included usage first |
10 GB of files with 100,000 uploads and a million reads a month comes to $0.20 + $0.60 + $0.50: $1.30, inside what any paid plan includes.
On Free
- One bucket, never billed.
- Past 100,000 writes in a calendar month, or while it holds 1 GB or more, it becomes read-only. Past 1,000,000 reads it is switched off until the 1st. The team’s owners are emailed, and no file is deleted.
- Deleting files below 1 GB lets it write again. A paid plan lifts the limits at once.