Skip to main content
This guide covers upgrading between versions of the Bunny Storage plugin.

v3.1.1 to v3.1.2

v3.1.2 contains security fixes for Stream TUS uploads. It needs no config changes.
  • tus-auth checks the file like a storage client upload. It rejects a type outside the collection’s upload.mimeTypes or the Stream mimeTypes, a restricted type and a file over upload.limits.fileSize, even when checkAccess allows the request. The saved document gets the signed file size and MIME type.
  • A videoToken is bound to the signed-in user and expires after 24 hours. Tokens from v3.1.1 stop working, so an upload that was paused before the update starts again. The same happens to an upload that stays paused for more than 24 hours.
  • Payload rejects a videoId that another document in a Stream collection of the same library already uses.
If you call tus-auth from your own client, request a new videoToken for each upload and save the document within 24 hours.

v3.1.0 to v3.1.1

v3.1.1 needs no config changes. Most apps are not affected. It only changes documents that have an empty or missing prefix in a collection that sets a prefix. v3.1.0 stored the files of such documents at the root of the storage zone. v3.1.1 reads, deletes and stores them under the collection prefix. Documents with a non-empty prefix keep the same file paths. To check a collection, query it for documents without a prefix:
If the query returns documents, move each file and its image sizes from <file> to <collection prefix>/<file> in the storage zone. Otherwise their URLs return 404. You can move files with any S3 client on an S3 zone, or download and re-upload them in the Bunny dashboard.

v3.0.x to v3.1.0

v3.1.0 needs no config changes, but it has new install and upload requirements. Most apps only need steps 1 and 2. v3.1.0 requires Payload 3.90.2 or later. If you are on v3.0.1 with an older Payload version, upgrade Payload first. Staying on v3.0.1 is not recommended, because it lacks the security fixes in v3.1.0.

1. Install the cloud storage plugin

@payloadcms/plugin-cloud-storage is now a peer dependency. Add it next to the plugin, in the version that matches your Payload version:

2. Redeploy the Edge Script

If you use client uploads with an Edge Script (HTTP API zones), redeploy it:
The new script checks the signed file size and MIME type and does not overwrite existing files. An older script still accepts upload URLs minted by v3.1.0, but it skips these checks. S3 zones do not use the script.

3. Stricter client uploads

The /api/storage-bunny/storage/upload endpoint now has these requirements:
  • Without a custom clientUploads.access function, the user needs create or update access to the collection.
  • filesize is required, next to collectionSlug, filename and mimeType.
  • HTML, JavaScript, SVG and XML files are rejected, unless the collection sets upload.allowRestrictedFileTypes: true.
  • The upload URL only accepts the declared size and MIME type.
  • It returns 409 when a file already exists at the path.
The admin UI already follows these rules. Payload’s own rename logic keeps file names unique, so 409 is rare in practice.

4. Custom upload clients

This step only applies if you wrote your own upload client. The admin UI needs no changes.
  • Client uploads. Send the headers from the upload endpoint response with the PUT. Send the clientUploadContext from the same response unchanged with the document. Payload now requires its signed receipt, and the plugin takes the prefix, file size and MIME type from it.
  • TUS and Stream. tus-auth returns a videoToken with every videoId. Send it as bunnyData.stream.videoToken when you save a document with a videoId, and send videoId with videoToken to resume an upload. Without a valid token, Payload ignores the videoId on save, and tus-auth creates a new video.
  • Stream client uploads. Send the file head as head to tus-auth, and send the returned clientUploadContext unchanged with the document.
  • Read-only fields. The injected prefix field and bunnyData.stream.resolutions no longer accept values from the REST or GraphQL API. Server code that uses the Local API is not affected.
See the API reference for the request and response shapes.

5. Check static plus mint-time prefixes

This step only applies to collections that set both a static prefix and a clientUploads.prefix callback that returns a path outside the static prefix. For example, the static prefix uploads and the callback result tenants/acme. v3.0.x uploaded such files to tenants/acme/<file>, but stored uploads/tenants/acme on the document. So URLs and deletes pointed to a path with no file. v3.1.0 uploads new files to uploads/tenants/acme/<file>, which matches the stored prefix. To fix files uploaded with v3.0.x, move each file (and its image sizes) from <callback result>/<file> to <static prefix>/<callback result>/<file>. You can move files with any S3 client on an S3 zone, or download and re-upload them in the Bunny dashboard. The stored documents need no change. Collections that use only one of the two prefixes are not affected.

v2.x to v3.0

v3 has two independent parts: a set of config changes (which throw at startup until you rename the keys) and a data migration for the stored Stream metadata. Do both.
Requires Payload 3.90.2 or later and Node.js 22 or later. Upgrade Payload (and Node, if needed) first if you are below either.
Back up, then run the data migration. Take a database backup first. v3 reads Stream metadata only from the new bunnyData field — until you run the data migration, every existing video looks empty (broken thumbnails, no resolutions, videoId queries return nothing). On Postgres/SQLite the legacy columns are dropped the moment the v3 schema is pushed, so migrate before that. Config renames alone are not enough.

Config changes (breaking)

Several config keys were renamed or removed in v3. Most throw at boot with a targeted message, so a v2 config crashes on startup until you update it. The last three rows are the exception — they produce no boot error (they are a naming convention, silently ignored, or always overridden), but you should still clean them up: Per-service keys are unchanged: storage.apiKey and stream.apiKey stay as they are. New in v3 (optional, nothing to migrate — adopt when you need them):
  • Client uploads — browser-direct uploads, configured under storage.clientUploads (storage.clientUploads: true shorthand, or the full config). There is no mode option — the transport is chosen automatically (presigned S3 when the zone sets storage.s3, otherwise a deployed Edge Script).
  • Per-collection zones and libraries — point a collection at its own storage zone / stream library with a full config; the top-level storage/stream become optional.
  • storage.s3 — S3-compatible zone access.
  • stream.referer — Referer header for MP4-fallback requests behind BlockNoneReferrer libraries.
  • Per-collection stream.tus: false — disable TUS for one collection.
  • Stream webhook (stream.webhook) — v2 had no webhook; MP4-fallback resolutions were fetched lazily on the first serve (v3 still does this as a fallback). The optional webhook pre-populates bunnyData.stream.resolutions the moment encoding finishes instead. Set webhook.secret to the library’s Read-Only API key (Bunny signs each callback with the X-BunnyStream-Signature HMAC header) and point your Bunny library’s webhook at /api/storage-bunny/stream/webhook.

What changed (data)

v3 stores Bunny Stream metadata in a single bunnyData group field instead of the flat bunnyVideoId and bunnyVideoMeta fields. type and libraryId are computed on read (not stored). videoId is stored and indexed, so queries such as where: { 'bunnyData.stream.videoId': { equals } } keep working.

Migrate your data

The plugin ships a migration helper at @seshuk/payload-storage-bunny/migrations. It supports MongoDB, Postgres and SQLite and is idempotent — re-running it only touches documents that have not been migrated yet.
1

Upgrade the plugin to v3

2

Create a migration file

3

Call the helper from it

4

Run the migration

5

Remove the old fields

The legacy data lives in the bunnyVideoId and bunnyVideoMeta fields (MongoDB) — the bunny_video_id and bunny_video_meta columns on SQL. You do not declare these in your own config: the v2 plugin added them for you, and v3 no longer declares them, so after migrating they are leftover data with no field definition behind them.Once you have verified your app reads video metadata from bunnyData, delete the leftovers by passing drop: true. This unsets the fields (MongoDB) or drops the columns (Postgres/SQLite) for every managed collection:
You can run this as a one-off from any script with a Payload instance, or fold it into the migration once you’re confident:
drop is destructive and one-way — the legacy fields are gone afterward, so direction: 'rollback' can no longer copy data back. Only pass drop: true after you’ve verified the migrated data, or with a database backup in hand.

Database notes

MongoDB is schemaless, so there is no ordering constraint.If your project doesn’t use payload migrate, call migrateBunnyData({ payload }) once from any script that has a Payload instance.

Options

v2.1.x to v2.2.0

What changed

The main changes in v2.2.0: Migration actions:
  1. Move purge.apiKey to plugin-level apiKey (old way still works but deprecated)
  2. Replace adminThumbnail with thumbnail (required, no backward compatibility)
  3. Optionally use new collection overrides for storage.uploadTimeout, stream.uploadTimeout, stream.mp4Fallback, stream.tus.uploadTimeout
Backward compatibility
  • purge.apiKey still works in v2.2.0 but will be removed in v2.3.0 (removed in v3 — use accountApiKey, see Config changes)
  • adminThumbnail is removed - you must use thumbnail instead

v2.0.x to v2.1.0

What changed

The main changes in v2.1.0:

v1.x to v2.x

Version requirements

v2.x needs Payload v3.53.0 or higher.If you’re below v3.53.0:
  • Upgrade Payload first, then migrate to v2.x
  • Or stay on v1.x: npm install @seshuk/payload-storage-bunny@^1.0.0

What changed

The main changes in v2.x:

How to migrate

1

Update the package

2

Remove the options wrapper

3

Fix stream config

4

Fix purge config

5

Remove experimental stuff

Complete example

New features in v2.x

TUS resumable uploads — perfect for large video files:
Signed URLs — secure file access with geo-restrictions:
URL transform — customize file URLs:
Thumbnail control — better admin panel thumbnails:
Stream cleanup — auto-remove failed uploads:

Need help?

Check the Examples page for working configurations, or open an issue if you run into problems. For Payload upgrade help, see their official docs.