.env lines. It never touches your payload.config.ts.
npx fetches the package and runs the wizard. If the plugin is already installed, npx payload-storage-bunny init works too.
Prerequisite
A Bunny account API key (dashboard → Account → API). This is the same key used foraccountApiKey / the BUNNY_ACCOUNT_API_KEY env var. The wizard reads it from --api-key, then BUNNY_ACCOUNT_API_KEY, then prompts for it (masked) and verifies it before anything else.
Flags
The wizard is interactive and needs a TTY; it exits with a message if stdout isn’t a terminal.
What it asks
- What to set up — Storage, Stream, or both.
- Collection slug — used only in the printed config block.
- Storage zone name — also becomes the pull zone and CDN hostname (
{name}.b-cdn.net). - Access method — HTTP API (default) or S3-compatible. S3 creates the zone S3-enabled (irreversible after creation) and unlocks presigned client uploads.
- Storage tier — Standard (HDD, cheaper) or Edge (SSD, faster).
- Main storage region — for the Standard tier only (default Frankfurt, DE): HTTP offers 9 regions, S3 offers 8 (the 9 minus São Paulo, which S3 doesn’t support). The Edge (SSD) tier is always created in Frankfurt (DE) — the main region is fixed and not prompted.
- Replication (availability) regions — optional; excludes the main region. The list depends on access and tier (S3: 8, HTTP + Standard: 9, HTTP + Edge: 15). ⚠️ Replication regions cannot be removed once the zone is created — the only way is to delete and recreate the zone. You can also add them later in the Bunny dashboard.
- Image optimization — optionally enables Bunny Optimizer on the pull zone (flat $9.50/month per pull zone, unlimited WebP/AVIF conversion, compression and
?width=-style resizing; pairs withthumbnail.queryParams). Default off. CSS/JS minification is force-disabled so the CDN never rewrites your stored files. With automatic optimization on, images wider than Bunny’s desktop max width are downscaled at the edge, and any query params added after URL signing break signed tokens. When enabled, the printed config gets athumbnail: { queryParams: { width: '300' } }starter hint. - Client uploads — browser-direct uploads for large files or serverless body-size limits. For S3 zones this is a free
clientUploads: true. For HTTP zones it needs a Bunny Edge Script (additional Edge Scripting cost); the wizard can deploy it now or you can runbunny:deploy-edge-scriptlater. - Video library name — for the Stream library. Its main region is fixed at Frankfurt (DE) and can’t be changed; only optional replication regions are configurable (same permanence warning).
- Signed URLs — token-authenticated, time-limited links for storage and/or stream.
- CDN cache purge — enables auto-purge on upload/delete (writes your account key into app env).
What it creates
The wizard shows approximate Bunny storage prices at each prompt (Standard$0.01/GB, Edge/SSD $0.02/GB, replication regions add the same per-GB rate as the tier; Stream replication is $0.00/GB main, $0.02/GB first extra region, $0.005/GB each subsequent) purely for orientation — they aren’t a billing guarantee. Stream also bills encoding + CDN delivery and Storage bills CDN delivery on top; see the current full pricing in the bunny.net dashboard after setup.
Videos are served CDN-direct — no MP4 fallback is enabled, which keeps stream storage minimal. If you instead need Payload access control to gate video reads, that path proxies MP4 through Payload and requires
stream.mp4Fallback (see Stream); it isn’t what the wizard sets up.
Replication regions are optional and permanent once the zone is created. You can add more later in the Bunny dashboard.
What it never does
- It never edits
payload.config.ts— it prints the config for you to paste. - It writes secrets to disk only if you opt in to appending them to
./.env, and it never overwrites values that already exist there. - It never deletes any Bunny resource.
Reuse and partial failure
Every step is ensure-style. Before creating anything the wizard runs read-only checks and marks each resourcecreate or reuse:
- A storage zone, pull zone, or video library that already exists with the same name is reused, not duplicated.
- If a name is taken by an unrelated resource (or globally taken by another account), the wizard stops and asks you to pick a different name — it never mutates something it didn’t create.
init with the same names is safe: existing resources are reused.
After running
- Paste the printed
bunnyStorage({ … })block into yourpayload.config.tspluginsarray. - Set the printed environment variables (or let the wizard append them to
./.env). - Restart your app.
- Quick Start
- Client uploads
- Signed URLs
- Deploy the Edge Script — for HTTP-API zones that need browser-direct uploads