I've been deploying PHP applications to AWS Lambda with Bref for a while now. Since I'm a Terraform fan, I very quickly parted with @mnapoli's serverless.yml approach, in favour a Terraform + container-based Lambdas.
Here, the Dockerfile approach works, but it's verbose: a typical multi-stage Dockerfile for a Symfony app on Lambda is 100+ lines of repetitive infrastructure code.
Recently, Cloud Native Buildpacks (CNB) caught my attention. Their promise is to turn source code into container images without a Dockerfile, with built-in layer caching and a standardized build/run separation. The question was: could this work for Lambda?
The challenge
Lambda container images have specific requirements that don't align perfectly with the CNB model:
- Lambda expects the app at
/var/task(a reserved, non-overridable path) - Lambda needs a specific entrypoint (
/lambda-entrypoint.shfor Bref) - The CNB lifecycle adds its own launcher binary that Lambda doesn't understand
- Bref base images use Amazon Linux with stripped-down package managers
None of these are showstoppers, but they required creative solutions.
What my buildpack does
Given a PHP project with bref/bref in its composer.json, the buildpack handles everything:
- Detects the app (composer.json + bref dependency)
- Installs PHP extensions (via crane download or from-source compilation)
- Runs composer install with production optimizations
- Configures OPcache for Lambda cold starts
- Detects Symfony and warms the cache
- Produces a Lambda-ready container image
The entire configuration is driven by environment variables:
make lambda \
TEST_APP_PATH=./my-symfony-app \
LAMBDA_IMAGE=my-app \
BP_BREF_RUNTIME=fpm \
BP_BREF_EXTENSIONS=redis,gd,soap \
BP_OPCACHE_JIT=true
Compare that to 120 lines of multi-stage Dockerfile with extension compilation, vendor optimization, cache warmup, and OPcache tuning.
Testing it for real
I deployed the Symfony Demo application to Lambda using the buildpack. Same app, same database, same environment variables as the existing layer-based deployment. Only change: it's just built with pack build instead of docker build and a long multi-stage Docker file.
It works. The function URL serves the app correctly, Symfony cache is pre-warmed, and the second build reuses all cached layers (vendor, opcache config) making rebuilds near-instant when only application code changes.
I also tested extension compilation: ftp and soap compile successfully from PHP source on ARM64 during the build step, with proper shared library dependency resolution via Bref's copy-dependencies.php.
What's not handled by the buildpack
The buildpack builds your app into a Lambda container image. It does not:
- Set up your Symfony app for Bref (you still need BrefKernel, trusted_proxies, etc.)
- Serve static assets (CSS/JS need CloudFront + S3, same as any Bref deployment)
- Replace your IaC (you still need Terraform/CDK/Serverless to create the Lambda function)
It replaces the Dockerfile and the build process, not the infrastructure or app configuration.
Current state
The buildpack is published and usable today:
- GitHub: https://github.com/psantus/bref-buildpack
- Docker Hub:
psantus/bref-buildpack:0.2.0 - CNB Registry:
bref/php-lambda(registered at registry.buildpacks.io) - CI: passing on GitHub Actions
- Tested: Symfony Demo running on Lambda ARM64 (Graviton) via function URL
It supports PHP 8.2/8.3/8.4, ARM and x86 architectures, extension compilation from source, all 40+ bref-extra extensions (x86) or compile-from-source (ARM), Symfony auto-detection, composer caching, OPcache/JIT configuration, and vendor shrinking.
Try it
brew install buildpacks/tap/pack
git clone https://github.com/psantus/bref-buildpack
cd bref-buildpack
make lambda TEST_APP_PATH=/path/to/your/php/app LAMBDA_IMAGE=my-lambda BP_BREF_EXTENSIONS=gd,redis
The resulting image is ready to push to ECR and deploy to Lambda.
Top comments (0)