Overview

cfStreamId metatag, so running the import again skips videos that are already imported.
Scope
The import copies the video files, names, and descriptions. Settings that control how and where videos play stay on Cloudflare, so plan to set up their Bunny equivalents.Prerequisites
- A bunny.net account
- A Cloudflare account with Cloudflare Stream
Migrate your videos
1
Install and authenticate the Bunny CLI
Install the Bunny CLI with the shell installer or npm:The installer downloads a prebuilt binary for your platform and places it on your See Installation for more options. Then authenticate the CLI with your bunny.net account:
- curl
- npm
PATH.bunny login opens your browser to complete authentication and stores the credentials locally. The first time you log in from an interactive terminal, it also offers to install the agent skill for AI coding tools. On a remote machine or in CI without a browser, run bunny login --api-key "$BUNNYNET_API_KEY" instead, with a key from Account Settings > API key.2
Create a Bunny Stream library
Create a library for the imported videos, or skip this step to use an existing one:DE is always the main storage region. Replicate to the regions closest to your audience, from When the CLI asks how to add videos, choose Later. You start the import in the next steps.Copy the library ID from the output. You pass it to
UK, SE, NY, LA, SG, SYD, BR, and JH. You can’t remove a replication region later.Set encoding options, such as
--resolutions or --encoding-tier, when you create the library. Changes you make later apply only to new uploads, not to videos that are already imported. Run bunny stream libraries create --help to see every option, or read Encoding.--library in the import commands. The flag also accepts the library’s name, and --lib works as a short alias.3
Create a Cloudflare Stream API token
The Bunny CLI needs your Cloudflare Account ID and an API token with the Stream:Edit permission. Edit lets the CLI list your videos and generate an MP4 download for any video that does not already have one.To find your Account ID, open the Cloudflare dashboard and go to Build > Images & Stream > Hosted videos. The Account ID appears in the sidebar under Account details.
To create the token, open User API Tokens and click + Create Token.
Click Create Custom Token, enter a token name, such as migrate-to-bunny, and add the permission Account > Stream > Edit. Edit includes read access, so this one permission covers both listing videos and generating their MP4 downloads. Click Continue to summary, then Create Token.
Copy the token. Cloudflare shows it only once.




4
Preview the migration with a dry run
Run the import with Once Cloudflare accepts them, the CLI offers to save them so later runs skip these prompts. Press Enter to accept:
The dry run then prints a summary and the import plan:Already imported counts videos that are already in the library, and To import counts the ones a real run would import. Every video is listed under (no collection), since Cloudflare Stream has no folders.Bunny Stream accepts sources up to 2160p (3840x2160, or 2160x3840 in portrait) and up to 72 hours long. The dry run warns about any video beyond those limits, as it will fail on Bunny:The dry run does not check
--dry-run, using the library ID from step 2:--dry-run prints the import plan without making any changes to either platform.The CLI then prompts for your Cloudflare credentials. Paste the API token at the API Token prompt and the Account ID at the Account ID prompt:Where the CLI stores credentials
Where the CLI stores credentials
The CLI stores saved credentials in
~/.config/bunnynet/stream-import-credentials.json (or under $XDG_CONFIG_HOME/bunnynet/ when it is set). Only your user account can read the file, and it is separate from the main Bunny CLI config. Saved credentials belong to the active Bunny CLI profile. If Cloudflare later rejects them, for example after you revoke the token, the CLI deletes them and prompts again on the next run. If you answer no, the CLI prompts on every run.The CLOUDFLARE_API_TOKEN and CLOUDFLARE_ACCOUNT_ID environment variables always take priority over saved credentials.requireSignedURLs. A video with Require Signed URLs turned on still appears in the plan and fails during the import, so turn the setting off on those videos first, as described in Scope.5
Run the import
Run the same command without The CLI prints the same summary, then asks for confirmation. Type The CLI skips videos that are already imported, has Cloudflare generate an MP4 download for each new one, hands those to Bunny three at a time, and tags each video with The import command also accepts these flags:
--dry-run:y and press Enter to start:cfStreamId. It returns once every video is queued. Bunny then fetches each file directly from Cloudflare and encodes it.--waitkeeps the command running, with a live encoding progress indicator, until Bunny has encoded every video.--forceskips the confirmation prompt and the offer to retry failed videos, for unattended or CI runs.--concurrency Nsets how many videos the CLI passes to Bunny in parallel, from 1 to 20.--clean-faileddeletes the failed Bunny copies of videos before importing them again, including videos that Bunny rejected.--retry-stalleddeletes and imports again any video stuck at zero bytes on Bunny.--request-timeoutsets how many seconds one request to Cloudflare or Bunny can take before it gives up (default 30).--video-timeoutsets how many seconds the hand-off of one video to Bunny, or with--waitits encoding, can take before it gives up (default 5400).
bunny stream import --help for the full list.6
Watch encoding progress
The import returns before Bunny finishes encoding. Check progress with the status command:It prints one row per queued video, with its status on Bunny, encode percentage, size, and when it was queued:Run it again until the last line reports 0 processing. The command exits with a non-zero code when any video has failed, so you can also use it as a check in scripts.
7
Verify in the bunny.net dashboard
Open the bunny.net dashboard and go to Stream > Your library > Manage library to confirm every video has encoded.
Set up the library’s security settings if you have not already, then replace the Cloudflare Stream embed URLs and player references in your site with their Bunny Stream equivalents. See Embedding videos for the embed and player options.

8
Clean up the MP4 downloads on Cloudflare
The import generates an MP4 download for every video that did not already have one. According to Cloudflare’s documentation, MP4 downloads add no storage cost, but Cloudflare bills for the video’s duration each time one is downloaded. Bunny’s fetch counts as one download per video.If you plan to delete your videos from Cloudflare Stream, deleting a video also deletes its downloads, so there is nothing else to do.If you are keeping the videos on Cloudflare for now, set The command lists at most 10,000 videos. On a larger account, read the remaining IDs from the
CLOUDFLARE_ACCOUNT_ID and CLOUDFLARE_API_TOKEN in your shell, then delete the downloads you do not need through the Cloudflare API:$VIDEO_UID is the Cloudflare video ID, stored in the cfStreamId metatag on each Bunny video. To list the video IDs on the account, run:cfStreamId metatags or the Cloudflare API.The CLI reuses MP4 downloads that existed before the migration. If your site already serves some of those downloads, leave them in place.Troubleshooting
Some videos failed during the import
Some videos failed during the import
When videos fail during an interactive import, the CLI lists each failure and offers to retry them:Answer
y to retry them in the same run. If the import was interrupted, or you declined the retry, run it again with --resume:--resume retries failed videos, processes any that were still outstanding, and re-applies the cfStreamId tag to videos Bunny fetched before the run stopped. It stops retrying a video after three failed attempts and skips videos that Bunny rejected. To retry those too, add --clean-failed, which deletes their Bunny copies first.Running the import again is always safe. Videos that already carry the cfStreamId tag are skipped, and the CLI reports Everything is already imported once nothing is left.No download available
No download available
Cloudflare did not finish generating the MP4 download within five minutes, which can happen with long or high-resolution videos. It can also mean the video was deleted from Cloudflare after the import started.Cloudflare keeps generating the download after the CLI stops waiting, so wait a few minutes, then retry. The retry picks up the finished download instead of requesting a new one.
Cloudflare video requires signed URLs
Cloudflare video requires signed URLs
The video has
requireSignedURLs turned on. Turn off Require Signed URLs on it in Cloudflare Stream, retry, then turn it back on. See Scope.Cloudflare could not generate an MP4 download
Cloudflare could not generate an MP4 download
Cloudflare reported an error while generating the download, and the CLI prints its reason. Check the video in the Cloudflare dashboard. A retry only helps once Cloudflare can generate the download.
Videos are stuck with no data
Videos are stuck with no data
If
bunny stream import status reports videos stuck at zero bytes, run the import again with --retry-stalled to delete and fetch them again.