Brewless Brewless Docs

Getting started

Moving from Laravel Vapor

From vapor.yml to brewless.yml in one command, then your variables, the data in your database and your domain, with what each Vapor key becomes.

On this page

A Laravel application on Vapor has its environments described in vapor.yml. brewless init reads that file once, writes the brewless.yml that says the same wherever Brewless has a setting for it, and tells you what it could not carry over. Nothing at Vapor or AWS is read or changed: your application keeps running there until you move your domain.

You need what the quickstart asks for: a Scaleway account connected to your organisation at Brewless, and the command line client, signed in.

1. Write brewless.yml from vapor.yml

In the root of the project, next to vapor.yml:

brewless init --from-vapor

init asks which application at Brewless this is, or makes a new one, and writes brewless.yml. It then adds the environments of vapor.yml you pick, in the region you choose, and sets each one up the way it was at Vapor: a new database where it had one, a queue at Scaleway Queues unless it said queues: false, and a bucket where it had storage. An environment whose jobs wait in its database at Brewless can move them to a queue later with queue: sqs in brewless.yml. It says what that comes to and makes it after one yes. Without --from-vapor, init asks whether to start from the vapor.yml it finds.

Take this vapor.yml:

vapor.yml
id: 12345
name: shop
environments:
  production:
    runtime: 'php-8.4:al2023'
    memory: 1024
    cli-memory: 512
    warm: 10
    queue-timeout: 540
    database: shop-db
    storage: shop-files
    gateway-version: 2
    domain:
      - shop.example.eu
      - www.shop.example.eu
    build:
      - 'composer install --no-dev'
      - 'npm ci && npm run build && rm -rf node_modules'
    deploy:
      - 'php artisan migrate --force'
  staging:
    runtime: 'php-8.4:al2023'
    memory: 512
    queues: false
    scheduler: false
    database: shop-staging-db
    build:
      - 'composer install --no-dev'
      - 'npm ci && npm run build && rm -rf node_modules'
    deploy:
      - 'php artisan migrate --force'

With both environments added in Paris, init writes:

brewless.yml
# Which application this is at Brewless. No secrets belong in this file.
organisation: acme
application: shop
framework: laravel
php: '8.4'

release:
  - 'migrate --force'

environments:
  production:
    kind: production
    region: fr-par
    web:
      memory: 1024
      min: 1
    release:
      memory: 512
    worker:
      timeout: 540
    database: shop-db
    queue: sqs
    storage: shop-files
    domains:
      - shop.example.eu
      - www.shop.example.eu
  staging:
    kind: staging
    region: fr-par
    web:
      memory: 512
    scheduler: false
    database: shop-staging-db
    queue: false

and prints, once each, what it did not carry over as it was:

  Not carried over from vapor.yml as it was:
  - `warm` (production): Vapor kept 10 Lambda containers warm, each taking one request at a time; a replica at Brewless takes up to 50, so `web.min` is 1. …
  - `gateway-version` (production): There is no API Gateway at Brewless: visitors reach the web container through bunny.net, or its own address without it.
  - `build`: Left out, as Brewless does it itself: composer install --no-dev, npm ci, npm run build, rm -rf node_modules.

These lines are printed in the terminal only; the file stays clean. The table below lists every key. Commit brewless.yml: it holds no secrets.

2. Check it with brewless plan

brewless plan

shows per environment what the file asks for next to what stands now, and changes nothing. A key Brewless refuses comes with its line in the file. Every key and its bounds are on brewless.yml, which also says what a deploy does with each of them today.

brewless.yml names each database and bucket as vapor.yml does. A database or bucket that init made has a name of its own, after the application and the environment, so the plan shows the two next to each other: write the name that stands in brewless.yml.

3. Your variables and secrets

vapor.yml holds no variables, and Brewless never reads them from Vapor. The environments Brewless set up have variables of their own already: the database, the queue, the bucket and a new APP_KEY. Fetch those into a file of their own, so they do not mix with a file of Vapor's:

brewless env:pull production --file=.env.brewless-production

Add to it what your application needs besides, from your variables and secrets at Vapor: mail, payment and other services, for instance. Keep the DB_*, AWS_*, SQS_*, QUEUE_CONNECTION and FILESYSTEM_DISK lines Brewless wrote: they point at the new database, queue and bucket. Take your APP_KEY from Vapor instead of the new one when your application has encrypted data or sessions: Laravel reads those only with the key they were written with. Then push it and remove the file:

brewless env:push production --file=.env.brewless-production
rm .env.brewless-production

Plain values that are no secret, such as APP_LOCALE, can go under variables in brewless.yml instead. See Environment variables.

4. The data in your database

The database Brewless set up is empty, and the data is not moved for you. A Serverless SQL Database speaks PostgreSQL. From a machine that can reach the database at Vapor, dump it and load it into the new one, with the address and credentials from brewless env:pull:

pg_dump --no-owner --no-privileges "postgresql://user:password@old-host:5432/shop" > shop.sql
psql "postgresql://$DB_USERNAME:$DB_PASSWORD@$DB_HOST:$DB_PORT/$DB_DATABASE?sslmode=require" < shop.sql

Do this before your first deploy, so the release commands find your tables. A MySQL database at Vapor needs a MySQL database to move to, such as a Managed Database you name when you set the environment up; Brewless does not convert data between the two. Leave your application at Vapor in maintenance mode while you move, or move again just before you switch the domain, so no writes are lost.

The files in your S3 bucket are not moved either: copy them into the new bucket with a tool that speaks S3 to both.

5. Deploy

brewless deploy production

builds the current commit, runs the release commands and puts the release live on the address of the environment, such as acme-shop-production.b-cdn.net, and on its test address. Try it there while your domain still points at Vapor. The deploy names the domains of brewless.yml that the environment does not answer on yet and asks whether to add them: answer no until you move your domain, because adding one points its DNS at Brewless when the zone is in your accounts. See Deployments.

6. Your domain

When the release on Brewless does what it should, move your domain over: init printed a brewless domain:add line for every domain of the environments it added. Moving your domain to Brewless walks through the DNS zone, the records in the way and APP_URL. Once visitors arrive at Brewless, what you had at Vapor can go: Brewless never removes anything there.

vapor.yml next to brewless.yml

Same means the value is carried over as it is. Works differently means it is carried over in another form, or partly. Not available means Brewless has no setting for it; init says so when your file has the key.

vapor.yml brewless.yml Status What happens
id, name organisation, application Works differently init asks which application at Brewless the project is, or makes one.
runtime: php-8.4:al2023 php: "8.4" Same One version for all environments, that of production or else the first; 8.3 and later.
runtime: docker, -arm – Not available Brewless builds the image itself on its public base image and chooses the processor.
memory web.memory Same Megabytes, from 128 to 12288; the processor follows the memory.
timeout web.timeout Same Seconds, from 10 to 3600; a value outside is brought within.
warm web.min Works differently Replicas that keep running. One replica takes up to 50 requests at once and a Lambda container one, so warm: 10 becomes 1. A running replica is paid for all the time.
concurrency web.max Works differently The most replicas, worked out the same way: concurrency: 120 becomes 3.
tmp-storage web.tmp-storage Same From 1000 megabytes up to four times the memory. Less than 1000 is left out: every container gets 1000.
cli-memory, cli-timeout release.memory, release.timeout Works differently The container the release commands run in. The commands themselves get 90 seconds per release.
queue-memory, queue-timeout worker.memory, worker.timeout Same The container that works the queue and the schedule.
queues worker.queues, queue Works differently The names, the first being the default; a number of jobs per queue is not carried. Every environment gets queue: sqs, a queue at Scaleway Queues, or queue: false for queues: false. As at Vapor, a job on the queue starts the worker by itself; see how the worker is called.
queue-concurrency – Not available Every minute a trigger calls the worker for the schedule and then the queue.
scheduler scheduler Same sub-minute becomes true: the schedule runs every minute.
build build Works differently One list for all environments, that of production. Steps Brewless runs itself are left out: composer install, the frontend build when there is a package-lock.json, removing node_modules, and caching configuration, routes and views.
deploy release Works differently Artisan commands, without php artisan in front; one list for all environments. Other commands are left out. Without it, your application's own release commands run.
domain domains Same brewless deploy names those the environment does not answer on yet and adds them after a yes; or add each one with brewless domain:add. See Domains.
database database Same The database by name; its data is not moved. At Vapor a database is shared by name, within one team and region. At Brewless an environment can be linked to a database in its region, also one of another application, with a login of its own; see Databases.
storage storage Same The bucket by name; its files are not moved.
cache – Not available Brewless sets up no cache. Use a cache store of Laravel, such as the database, or a Redis of your own with its variables in Secret Manager.
network, balancer – Not available Visitors reach the web container through bunny.net, or its own address without it.
firewall – Not available Brewless has no firewall setting.
gateway-version – Not available There is no API Gateway in between.
octane – Not available Brewless serves the application with FrankenPHP in its classic mode.
database-user, database-proxy – Not available The database user is in your variables (DB_USERNAME).
layers, separate-vendor – Not available The image is built on Brewless's public base image and holds the vendor directory.
ignore – Works differently A deploy packs what is committed with git archive: leave paths out with export-ignore in .gitattributes.

An environment name Brewless does not take, such as one with capitals or underscores, is left out with a line that says so. Each environment gets the kind of its name (production, staging) or development, and the region it was added in.