Skip to main content
Every collection listed under collections can use the global config as-is, or override individual options. This lets you keep one plugin config while giving each collection different behavior.

Collection options

prefix and disablePayloadAccessControl come from @payloadcms/plugin-cloud-storage and apply to every collection using this plugin. disableLocalStorage can’t be passed here — the plugin always sets it to true on managed collections.

Override surface: partial vs full replacement

Not every option can be tweaked in isolation. Some are partially overridable (merged onto the global zone/library); others exist only inside a full replacement config and can never be set on their own. The plugin flips storage/stream into full-replacement mode the moment the object contains apiKey — see Own zone or library per collection.
A partial stream.tus override needs global TUS enabledA stream: { tus: { … } } partial 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.

How overrides merge

A partial override object (one without apiKey) is merged on top of the matching global option — it doesn’t replace the whole block. An object that includes apiKey isn’t a merge at all: it is a complete replacement of that zone/library, with every credential field required and nothing inherited — see Own zone or library per collection. For a partial override, for example:

Own zone or library per collection

A collection’s storage or stream can go beyond a partial override and supply a full config — its own apiKey, hostname, zoneName (storage) or libraryId (stream). That collection then uses its OWN Bunny zone / library, and the global one is ignored for it. This is how you point different collections at different tenants’ zones and libraries.
The rules:
  • How it’s detected. The plugin treats the override as a full config when it contains apiKey ('apiKey' in override); otherwise it’s a partial override merged onto the global config. { uploadTimeout: 5 } is a partial override; { apiKey, hostname, zoneName } is a full config.
  • No inheritance. A full config inherits nothing from the global zone/library. Omitted optional keys (storage: region, s3, tokenSecurityKey, clientUploads, uploadTimeout; stream: mimeTypes, mp4Fallback, tus, referer, …) fall back to plugin defaults — never to the global config’s values.
  • Signed URLs. Provide that zone/library’s own tokenSecurityKey when signed URLs are enabled for the collection.
  • Webhooks. A per-collection stream library can carry its own webhook.secret; the single webhook endpoint accepts every configured secret.
  • Cleanup. Per-collection stream cleanup controls only maxAge. The cleanup schedule is global-only (one task for the whole plugin).
  • Client uploads travel with the zone. A full storage config’s clientUploads (including its edge script and secret) belongs to that zone and doesn’t merge with the global storage.clientUploads.
  • Top-level optional. Because a collection can own its whole zone/library, the top-level storage/stream are optional — a config made entirely of per-collection zones is valid.
Adding apiKey to a small tweak turns it into a full replacementThe trigger is purely the presence of apiKey, so an object you meant as a minor tweak becomes a complete replacement the moment you add credentials to it. For example, stream: { apiKey, hostname, libraryId, uploadTimeout: 5 } does not inherit mimeTypes, mp4Fallback, tus, webhook, or anything else from the global library — all of those reset to plugin defaults. If you only want to change uploadTimeout on the global library, leave apiKey out.
The completeness of a full config is validated at startup: a full storage config needs apiKey/hostname/zoneName; a full stream config needs apiKey/hostname/libraryId. A single stream library must not be configured with conflicting apiKey values across collections.

Disabling a service or feature: use false

Some options accept false to turn a feature off for one collection, even if it is enabled globally.
This only works with an explicit === false check, so always pass the literal false — an empty object or undefined means “inherit the global setting,” not “disable.”
payload.config.ts
purge, signedUrls, thumbnail, urlTransform, storage.clientUploads, and stream.tus all support false this way. A collection must keep at least one of storage or stream active — disabling both is a config error.

Enabling a collection with defaults

Pass true instead of an object to use every global setting unmodified:

Accessing the resolved config

Sometimes you need the settings the plugin resolved for a collection — the library id and API key for a Stream recipe, the zone name for a direct Storage call — from inside a hook, endpoint, or script. Rather than re-derive them from process.env or hand-maintain a lookup, ask the plugin. These accessors read the config it stashed on payload.config.custom, with global config and per-collection overrides already applied.
Semantics:
  • payload is a running Payload instance — req.payload in a hook or endpoint, or the instance from getPayload(...) in a script.
  • Never throws. Each accessor returns undefined when the plugin is absent or disabled, the slug isn’t one of the plugin’s collections, or (for a per-backend accessor) that backend is off for the collection. getBunnyCollectionConfig returns an object for any managed slug — a backend the collection doesn’t use is simply absent from it.
  • getBunnyConfig is an escape hatch. It returns the plugin’s internal normalized shape, which may change between minor releases. Treat it as read-only and prefer the curated accessors above.
  • The curated results are fresh copies; mutating them doesn’t affect the plugin.
Server-side onlyThe returned objects contain your Bunny API keys. Use them in server code — hooks, endpoints, scripts — and never send them to the client.