Skip to main content
If you’re moving an existing application to Laravel Cloud, you likely already have files sitting in another provider’s bucket. Copying everything before you cut over can be slow and force downtime. Laravel’s read-through filesystem driver lets you cut over immediately instead. Reads that miss on your new Laravel Cloud Object Storage bucket automatically fall back to your old provider and copy the file into the new bucket for next time, so your files migrate themselves as your application accesses them.

Before you start

The read-through driver requires Laravel 13.26.0 or later. Run composer update laravel/framework before continuing. If your old provider is S3-compatible, you’ll also need league/flysystem-aws-s3-v3; see the Object Storage prerequisites.
This works for any two Flysystem-compatible disks, not just S3-compatible ones, so it also covers moving off local disk storage or another provider’s driver.

Step 1: Create your Laravel Object Storage bucket

If you haven’t already, create a Laravel Object Storage bucket and attach it to your environment. Note its disk name (for example, r2); Laravel Cloud automatically injects the AWS_* credentials your application needs to reach it. Leave your old provider’s credentials in place; you’ll need them for the fallback disk in the next step.

Step 2: Configure your old provider as a fallback disk

In config/filesystems.php, add a disk entry for your old provider using whatever credentials it requires:
Laravel Cloud only injects credentials for the bucket attached to your environment, so add your old provider’s credentials as custom environment variables under your environment’s settings.

Step 3: Add a read-through disk

Combine your new Laravel Cloud bucket (primary) and your old provider (fallback) into a single read-through disk:
Reads check the new bucket first. If the file isn’t there yet, Laravel reads it from the old provider, copies it into the new bucket, and returns it, all within the same request. Writes and directory listings always go to the new bucket, so anything your application saves after cutover lands directly on Laravel Cloud. Existence and metadata checks, such as Storage::exists and Storage::size, consult either disk without triggering a copy.
By default, a failed promotion (for example, the new bucket is briefly unreachable) doesn’t fail the read; Laravel just returns the file from the fallback disk. Set 'throw_on_promotion_failure' => true on the disk if you’d rather the read fail loudly. Either way, keep an eye on your logs during the migration window: Laravel Nightwatch surfaces exceptions like these in real time, which helps catch credential or permission issues with either disk early.

Step 4: Point your application at the read-through disk

Update FILESYSTEM_DISK to your new read-through disk, and redeploy:
From this point on, every file read through Storage::disk('assets'), or the default Storage facade, transparently pulls from whichever bucket actually has the file, and quietly migrates it to Laravel Cloud along the way.

Step 5: Speed up the migration (optional)

Waiting for real traffic to touch every file can leave rarely accessed files on your old provider indefinitely. To migrate everything up front instead, loop over the fallback disk’s contents and read each file through the read-through disk:
For large buckets, dispatch this as a queued job per file (or per batch of files) instead of running it inline.

Step 6: Retire the old provider

Once every object in your old bucket exists in Laravel Cloud, remove the fallback. Use your storage provider’s inventory or compare the object keys in both buckets to confirm the migration is complete:
  1. Set FILESYSTEM_DISK back to your bucket’s disk name (r2).
  2. Remove the legacy and assets disk entries from config/filesystems.php.
  3. Redeploy.
  4. Cancel or delete the bucket with your old provider.