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.useRedirect: 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 config 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

Uploads that start but never finish (abandoned tabs, failed TUS sessions) leave orphaned videos in your library. Enable cleanup to remove them on a schedule.
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;
  • a full config (with its 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 a full config from a partial override by whether it includes apiKey. A full config inherits 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: { … } override only applies when the global stream.tus is enabled — otherwise it is silently dropped. To turn TUS on for a single collection, either give that collection a full stream config, or enable tus globally and disable it per collection with tus: false.
See Collection overrides for the full-config vs partial-override rules and the complete list of options.