Skip to main content
Bunny Stream handles video and audio: adaptive HLS/MP4 playback, thumbnails, and resumable uploads for large files. It is the stream block passed to bunnyStorage(...).
payload.config.ts
With Payload access control on (the default), a Stream collection needs mp4Fallback: true or signed URLs with redirect — otherwise the plugin throws at startup. See Access control and mp4Fallback. Set disablePayloadAccessControl: true on the collection to serve straight from the CDN instead, where this requirement doesn’t apply. Files whose MIME type matches mimeTypes are routed to Bunny Stream. Everything else goes to Bunny Storage, if configured. Video metadata for each document lives in the bunnyData field.

Options

Default mimeTypes cover the common video and audio formats: MP4, MKV, WebM, FLV, AVI, MOV, WMV, MPEG, TS, MXF, and MP3/OGG/WAV audio.
The top-level stream block is optional. If every collection points at its own stream library, you can omit the global stream entirely.

Access control and MP4 fallback

By default, files are served through Payload’s access control (disablePayloadAccessControl isn’t set on the collection). For Stream videos in this mode, Bunny can’t serve HLS directly through Payload’s proxy, so you need one of:
  • mp4Fallback: true — Payload proxies the MP4 rendition instead of HLS.
  • Signed URLs with redirect — set signedUrls.staticHandler.redirect: true (globally or on the collection) so Payload redirects straight to Bunny’s HLS stream instead of proxying it. This is the faster option — see Signed URLs.
The plugin’s options validator rejects a collection at startup if neither is set and access control is on. If you set disablePayloadAccessControl: true on a collection instead, files are served directly from the Bunny CDN and this restriction doesn’t apply.
When Payload proxies the MP4 fallback, it fetches the file from Bunny server-side. If your Stream library uses a BlockNoneReferrer rule, set referer to a value the library allows so those requests aren’t rejected. Default libraries need nothing here.

Cleanup

Some uploads leave videos in your library that no document uses. This happens when an upload stops halfway, for example after a closed tab or a failed TUS session. It also happens when the upload finishes but the document is never saved, for example when a bulk upload save fails. Enable cleanup to remove these videos on a schedule. Cleanup only checks uploads that this plugin started. Before it deletes a fully uploaded video, it makes sure no document references it. It never touches videos that other services or the Bunny dashboard add to the same library. Keep maxAge longer than an editor might leave an upload unsaved, because a document saved after cleanup has run points to a deleted video.
Or customize:
Cleanup registers a Payload job task, so your project needs Jobs Queue configured and the queue processed (e.g. via a cron endpoint or payload jobs:run).
There is one cleanup task for the whole plugin, and its schedule is global-only — set it on the top-level stream.cleanup. When a collection points at its own library, its cleanup controls only maxAge (each library’s stale sessions are swept with that library’s own cutoff); it can’t set a separate schedule.

Collection overrides

A collection’s stream can be one of three things:
  • a partial override (mimeTypes, mp4Fallback, thumbnailTime, tus, uploadTimeout) — merged onto the global library;
  • full options (with their own apiKey/hostname/libraryId) — this collection uses its OWN stream library, ignoring the global library entirely;
  • false — disable Bunny Stream for this collection.
The plugin distinguishes full options from a partial override by whether it includes apiKey. Full options inherit nothing from the global library — mimeTypes, mp4Fallback, tus, and the rest fall back to plugin defaults, not the global library’s values. Provide that library’s own tokenSecurityKey for signed URLs, and its own webhook.secret if you use webhooks. Per-collection cleanup carries only maxAge — the cleanup schedule is global-only. A partial tus: true or tus: { … } turns TUS on for that collection, even when the global stream.tus is off. tus: false turns it off. See Collection overrides for the full-options vs partial-override rules and the complete list of options.