Overview

s3Source metatag, so running the import again skips videos that are already imported.
S3 has no video catalog, so the CLI works out titles and collections from the object keys. For example, a bucket with one video at the top level and two folders maps like this:
Webinars/2024/Q3 product update.mp4 also lands in the Webinars collection.
Scope
The import copies the video files, their titles, any description and tags in the object metadata, and your top-level folder structure. Access controls stay on AWS, so plan to set up their Bunny equivalents.Prerequisites
- A bunny.net account
- An AWS account with an S3 bucket that holds your videos, and permission to create an IAM user
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 an IAM user with read access to your bucket
The Bunny CLI needs an AWS access key that can list your bucket and read its objects. It never changes anything in S3.In the IAM console, go to Users and click Create user. Enter a user name, such as migrate-to-bunny, leave console access off, and click Next.
On Set permissions, choose Attach policies directly, then click Create policy. In the policy editor, switch to JSON and paste the following policy, replacing Name the policy, such as migrate-to-bunny-demo, and create it. Back on Set permissions, refresh the policy list, select the new policy, then click Next and Create user.Open the new user, go to Security credentials, and click Create access key. Choose Command Line Interface (CLI) as the use case, confirm the recommendation, and create the key. Copy the Access key ID and the Secret access key. AWS shows the secret only once.Set the access key in the shell where you run the import:The CLI does not prompt for or save access keys. If you already use the AWS CLI, it also picks up keys from

migrate-to-bunny-demo with your bucket name in both places:If your bucket encrypts objects with SSE-KMS, also allow
kms:Decrypt on the bucket’s KMS key. Buckets with the default SSE-S3 encryption need nothing more.~/.aws/credentials and the profile AWS_PROFILE names. Pre-signed URLs made from temporary credentials, such as an AWS SSO session, stop working when the session ends, so use an access key for a large import.4
Preview the migration with a dry run
Run the import with Once S3 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. S3 does not know how long a video is, so the summary has no Duration row. It does not report resolution either, so unlike other sources, the dry run cannot warn about videos above Bunny’s 2160p or 72-hour limits. Those videos fail when Bunny encodes them.The plan groups videos by the collection they will land in, and lists them in alphabetical order of their keys, the order S3 returns them in. Videos at the top level of the bucket are listed under (no collection).To import only part of a bucket, add
--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 bucket’s region and name. The region prompt is filled in with us-east-1, so replace it with your bucket’s region, which the S3 console shows in the bucket list:Where the CLI stores credentials
Where the CLI stores credentials
The CLI saves the region and bucket for the active Bunny CLI profile in
~/.config/bunnynet/stream-import-credentials.json (or under $XDG_CONFIG_HOME/bunnynet/ when it is set). Your access keys are not saved. The AWS_REGION and S3_BUCKET environment variables, and the --bucket flag, always take priority over saved values.--prefix with the path to start from, such as --prefix videos/2024, or set S3_PREFIX. The top-level folders below that prefix then become the collections.5
Run the import
Run the same command without The CLI prints the same summary, then asks for confirmation. Type The CLI creates a collection for each top-level folder, skips videos that are already imported, hands the rest to Bunny three at a time, and tags each video with Collection names match without regard to case, so the S3 folders The ID of a folder is its path in the bucket. Preview a folder’s videos with The import works the same way, limited to that folder’s videos and its collection.
--dry-run:y and press Enter to start:s3Source, along with any description and tags from its metadata. It returns once every video is queued. Bunny then fetches each file directly from S3 and encodes it.Webinars/ and webinars/ land in one collection, even though S3 treats them as separate folders. Move the objects in one of them to a differently named folder before the import to keep them separate.Each pre-signed URL stays valid for six hours by default, which is long enough for Bunny to fetch the file. Set S3_PRESIGNED_URL_TTL to a number of seconds between 3600 and 604800 to change it.The import command also accepts these flags:--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 S3 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.To migrate a large bucket one folder at a time, list the folders:--folder, then pass the same ID to the import. Quote the ID when it contains spaces:--folder also accepts a deeper path, such as "Webinars/2024". Its videos still land in the top-level collection, Webinars.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. Each top-level S3 folder appears as a collection, and every migrated video is listed under Latest videos.
Set up the library’s security settings if you have not already, then replace the S3 or CloudFront video URLs in your site with their Bunny Stream equivalents. See Embedding videos for the embed and player options.Once the migration is complete, you can delete the IAM user’s access key, or the user itself.

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 s3Source tag to videos Bunny fetched before the run stopped. When the interrupted run was limited to one folder, the resume continues that folder. 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 s3Source tag are skipped, and the CLI reports Everything is already imported once nothing is left.Bucket is in a different region
Bucket is in a different region
us-east-1, is not the bucket’s region. Find the region in the S3 console’s bucket list, then run the command again with it set:AWS_REGION takes priority over a saved region. To replace the saved one, answer yes when the CLI offers to save again.Access denied to bucket
Access denied to bucket
Resource lines.Invalid AWS credentials
Invalid AWS credentials
AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY are set in the shell where you run the import, and that the key is still active in the IAM console.S3 object is in GLACIER
S3 object is in GLACIER
The object is in an archive storage class, which S3 cannot serve until it is restored. Restore it from the S3 console, wait for the restore to finish, then run the import again. Until then, the error says the restore is still in progress.
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.Migrate from an S3-compatible provider
Thes3 source also works with S3-compatible storage, such as Cloudflare R2, Wasabi, Backblaze B2, or MinIO. Set S3_ENDPOINT to the provider’s https:// endpoint, and use the provider’s region name, such as auto for R2: