Move your S3 files from AWS to Brewless

Copy a Laravel app's uploads from an S3 bucket at AWS, or any S3 service, into its Scaleway bucket with brewless storage:import. Nothing is removed.

In this post
  1. How do you move an S3 bucket to Brewless?
  2. Which value goes where
  3. Three ways to give the source
  4. A key at AWS that may only read
  5. In your project or from your laptop
  6. Follow it, or come back later
  7. After the move

A Laravel app that comes from Vapor, or from anywhere else on AWS, keeps its uploads in an S3 bucket. When it moves to Scaleway with Brewless, the environment gets a new, private bucket on Scaleway Object Storage, and the files have to get there. This post shows how brewless storage:import copies them, which values go where, and what happens to your keys.

How do you move an S3 bucket to Brewless?

Run brewless storage:import with the environment and the bucket the files are in now. It copies every file into the environment's bucket with rclone, in a serverless job of your own Scaleway project, and removes nothing. Run it again just before you move your domain and it only copies what changed.

export SOURCE_AWS_ACCESS_KEY_ID=AKIA…
export SOURCE_AWS_SECRET_ACCESS_KEY=…
brewless storage:import production s3://my-vapor-bucket --source-region=eu-west-1

The command says where it copies from and to, asks once, and follows the copy until it is done.

Which value goes where

The names are the ones Laravel's s3 disk reads from config/filesystems.php. For the source you give them; for the destination Brewless already set them in the environment's variables when it made the bucket.

Laravel variable Source (you give it) Destination (Brewless set it)
AWS_BUCKET s3://<bucket>[/<folder>], SOURCE_AWS_BUCKET or --from-env <application>-<environment>-<project>
AWS_DEFAULT_REGION --source-region, SOURCE_AWS_DEFAULT_REGION; at AWS it is looked up the environment's region, such as fr-par
AWS_ENDPOINT --source-endpoint; leave it out for AWS https://s3.fr-par.scw.cloud
AWS_USE_PATH_STYLE_ENDPOINT --source-path-style false
AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY SOURCE_AWS_*, --source-profile, --from-env, or typed in the environment's own key for its bucket
AWS_URL not needed to copy change or remove it after the move

The AWS_* variables of your own shell are not read on purpose: they often belong to another AWS account than the one your files are in.

Three ways to give the source

From the .env of the old application, which reads only its AWS_* lines:

brewless storage:import production --from-env=../old-app/.env

With variables, the same names as Laravel's with SOURCE_ in front:

export SOURCE_AWS_ACCESS_KEY_ID=AKIA…
export SOURCE_AWS_SECRET_ACCESS_KEY=…
brewless storage:import production s3://my-vapor-bucket/uploads --source-region=eu-west-1

With a profile of the AWS CLI, from ~/.aws/credentials and ~/.aws/config:

brewless storage:import production s3://my-vapor-bucket --source-profile=migration

A key is never an option on the command line, where it would end up in your shell's history. Without a key in any of these places the command asks for it in the terminal, and run from a script it stops.

Note

An app on Vapor runs with an IAM role, so its .env usually has no AWS_ACCESS_KEY_ID. Vapor sets AWS_BUCKET itself. Make a key at AWS that may only read the bucket, as below.

A key at AWS that may only read

Do not hand out the key of your AWS account. Create an IAM user for the move, with only this inline policy, and an access key for an application outside AWS:

{
    "Version": "2012-10-17",
    "Statement": [
        { "Effect": "Allow", "Action": "s3:ListBucket", "Resource": "arn:aws:s3:::my-vapor-bucket" },
        { "Effect": "Allow", "Action": "s3:GetObject", "Resource": "arn:aws:s3:::my-vapor-bucket/*" }
    ]
}

It can list and read the bucket and nothing else. Remove the user once you have moved.

In your project or from your laptop

In your Scaleway project (default) --local
Where the files pass from AWS straight to Object Storage through your machine
Where your AWS key goes into your own Secret Manager, deleted afterwards nowhere: it stays on your machine
Terminal open no, you can close your laptop yes, until it is done
Files larger than 5 GB copied skipped and named

In the default mode Brewless runs rclone in a serverless job in your project. Both keys, the one for AWS and the environment's own key for its bucket, reach the job in one secret of your Secret Manager. When the copy ends, however it ends, that version of the secret is deleted. The operation Brewless records only knows which version it was, never a key. That is the same rule as for your other secrets: they stay in your account.

Follow it, or come back later

While the copy runs, the command shows how many files of how many and how much of how much. Ctrl+C stops following, not the copy:

brewless storage:status production

shows the last copy into the environment's bucket, while it runs and after it ended. It is also on the environment's page in the console, where you can cancel it; what was copied stays.

--dry-run copies nothing and counts what the source holds first, which is handy to know how much AWS will bill for the data that leaves it.

After the move

Warning

brewless env:push replaces the environment's variables as a whole. Do not push the .env of your old app with its own AWS_BUCKET and keys: that points the app back at the old bucket. Start from brewless env:pull instead.

Then change or remove AWS_URL, which pointed at the old bucket or CloudFront, run the import once more just before you move your domain, and remove the read key at AWS. Your app reads its files through the same s3 disk as before, now from Scaleway; see Laravel uploads on Scaleway Object Storage for how that bucket is set up.

The full reference, with other S3 services and every option, is in the documentation: https://docs.brewless.eu/applications/moving-files

Frequently asked questions

Does storage:import delete anything?

No. It copies with rclone copy, never sync. Nothing at the source is changed or removed, and a file that already is in the environment's bucket with the same size and time is left as it is.

Where does my AWS key go?

By default into a secret of your own Scaleway Secret Manager, next to the environment's own key for its bucket, as one rclone configuration for the job. When the copy ends, however it ends, that version of the secret is deleted. Brewless keeps no key. With --local the key never leaves your machine.

What does it cost?

AWS bills the data that leaves it, per GB. On the Scaleway side the copy is a serverless job in your project, billed by Scaleway for the time it runs. A dry run counts how many files and how much data there is first.

Can I run it again just before I switch my domain?

Yes. Files that are in the bucket already with the same size and time are skipped, so a second run only copies what was added or changed since the first.

Does it work with something other than AWS?

Yes. Any S3 service works as the source. Give its endpoint and region, such as DigitalOcean Spaces, Cloudflare R2, Hetzner Object Storage, MinIO or another Scaleway project.

All posts Read this post as markdown

Try it on your own app

Brewless deploys your Laravel app to Scaleway and bunny.net, in your own accounts. We open access to Laravel teams in small batches.