Bref 2 to 3 and provided.al2 to al2023, the gotchas
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:
bref/brefto^3.0incomposer.jsoncomposer update --with-all-dependencies- In
serverless.yml,provider.runtime: provided.al2→provided.al2023 - Reinstall
node_modulesfrom scratch 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.