Back to blog
FILE 0xF8·BREF 2 TO 3 AND PROVIDED.AL2 TO AL2023, THE GOTCHAS

Bref 2 to 3 and provided.al2 to al2023, the gotchas

April 30, 2026 · aws, lambda, php, bref

AWS sent the usual EOL notice for the provided.al2 Lambda runtime. I had 13 functions on Bref 2 / al2 spread across two serverless projects. The migration was almost boring — except for two stomps that wasted real time and would waste anyone else's time too.

What was happening

The mechanical steps are what you'd expect:

  1. bref/bref to ^3.0 in composer.json
  2. composer update --with-all-dependencies
  3. In serverless.yml, provider.runtime: provided.al2provided.al2023
  4. Reinstall node_modules from scratch
  5. serverless deploy --stage prod

Bref 3 collapses the layers: only php-83, php-82, etc. exist — no separate -fpm / -console flavors. The Bref plugin auto-sets BREF_RUNTIME=fpm based on runtime: php-83-fpm in your function config. Layer ARN owner moves from one account to another, so any ARN you hard-coded somewhere will break.

What I found

Two gotchas that don't show up in the changelog.

One: composer update regenerates the autoloader with dev deps.

After composer update --with-all-dependencies, the autoloader references phpunit, myclabs/deep-copy, etc. My serverless.yml package.patterns excludes those vendor subdirectories from the deploy artifact. Result: cold-start fatals like

Fatal error: Failed opening required '/var/task/vendor/myclabs/deep-copy/deep_copy.php'

and the function 500s.

Fix: always finish with a --no-dev install before deploy.

composer update --with-all-dependencies
# do all the version bumps and serverless.yml edits
composer install --no-dev --optimize-autoloader
serverless deploy --stage prod

Two: newer Node breaks Serverless 3.40.x.

My laptop's default Homebrew node had moved to v25. Serverless 3.40 still pulls in an old graphlib that references the browser global window. On Node 25 that throws on load. Fix:

brew install node@20
export PATH="/opt/homebrew/opt/node@20/bin:$PATH"
serverless deploy --stage prod

This is the kind of thing that only bites when you don't deploy for a few months and your toolchain has drifted out from under the framework.

The fix

Net result across 13 functions:

aws lambda list-functions \
  --query "Functions[?Runtime=='provided.al2'].FunctionName"
# []

Layer size dropped about 24% (1.8 MB → 1.7 MB). Cold starts are roughly the same. No code changes required, which is exactly what you want from a runtime migration.

What I'd do differently

The --no-dev reinstall really should be the last line of any "how to upgrade Bref" doc, not buried as a footnote. I lost a deploy cycle on it. I'll add a composer install --no-dev step to my deploy script so this isn't a thing I have to remember at the keyboard.