Skip to main content
One interactive command that provisions the Bunny resources you need and prints a ready-to-paste plugin config plus the matching .env lines. It never touches your payload.config.ts.
No install required — 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 → AccountAPI). This is the same key used for accountApiKey / 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 with thumbnail.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 a thumbnail: { 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 run bunny:deploy-edge-script later.
  • 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

These are billable resourcesThe wizard shows exactly what will be created (or reused) and asks you to confirm — the default answer is No. Nothing is created until you confirm. It never deletes anything.
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 resource create 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.
If a call fails partway through, the wizard prints what was already created, then exits. Re-running init with the same names is safe: existing resources are reused.

After running

  1. Paste the printed bunnyStorage({ … }) block into your payload.config.ts plugins array.
  2. Set the printed environment variables (or let the wizard append them to ./.env).
  3. Restart your app.
See also: