Building a PHP 8.4 Lambda Runtime: Performance Analysis


Why Build Your Own Runtime?

Bref is the most popular way to run PHP on AWS Lambda, and for good reason—it’s well-maintained and gets the job done. However, there are situations where building your own runtime makes sense: you need a specific PHP version before Bref supports it, you want particular extensions, or you simply prefer working with standard PHP patterns without additional abstractions.

This post documents a fully functional PHP 8.4 runtime running on ARM64, complete with performance benchmarks. Consider it a clearly documented alternative to Bref for teams who want complete control over their PHP Lambda environment.

Runtime Architecture

Single Lambda layer (~33MB) containing PHP 8.4 for ARM64, minimal bundled libraries (only libonig), and a bootstrap script implementing the Lambda Runtime API. Supports both PSR-15 HTTP handlers and callable functions.

Performance Benchmarks

Benchmarks across multiple memory configurations on ARM64. Handler processes 10 HAL-formatted entity responses.

Warm Invocation Performance (100 samples each)

Pure CPU performance without I/O:

MemoryAvgMedianP95P99Cost/1M
128MB4.2ms2ms17ms20ms$0.0069
256MB2.2ms2ms3ms14ms$0.0075
384MB2.1ms2ms3ms4ms$0.0105
512MB2.2ms2ms3ms6ms$0.0145
1792MB2.1ms2ms3ms4ms$0.0495

Key findings:

  • 256MB+ configurations all average ~2ms
  • 128MB is CPU-bound at 4.2ms (2x slower)
  • P99 latency matters: 384MB shows 4ms vs 256MB’s 14ms spikes

Cold Start Performance (30 samples each)

MemoryAvg TotalAvg InitMedianMinMax
256MB288ms132ms282ms265ms333ms
384MB279ms144ms272ms244ms359ms
512MB270ms147ms266ms240ms320ms
1792MB252ms138ms252ms224ms278ms

Key findings:

  • Init time: 132-147ms (relatively consistent)
  • Higher memory = slightly slower init (more resources to allocate)
  • Minimal benefit beyond 256MB (only 3% faster at 512MB)

Cost Analysis

At 384MB with AWS Lambda ARM64 pricing ($0.0000133334 per GB-second):

ScenarioCost per 1M Requests
100% Warm (2ms)$0.01
1% Cold (279ms avg)$0.024
5% Cold$0.079
10% Cold$0.148

Memory Recommendations

256MB - Best cost/performance ($0.0075/1M, 2.2ms avg) 384MB - Lowest P99 latency (4ms vs 256MB’s 14ms spikes)

Avoid 128MB (2x slower) and >512MB (no performance gain). Function uses ~42MB actual memory.

Comparison with Bref

Comparison to Bref’s published benchmarks:

AspectThis RuntimeBref (Layer/ARM)
PHP VersionAny (8.4 here)Limited to Bref releases
ExtensionsFull controlPre-selected set
Cold Start Init~132-147ms~170ms+
Warm~2ms~2ms
Layer Size~33MB~40-60MB
AbstractionStandard PSR-7/PSR-15Bref-specific handlers

vs. Swoole/RoadRunner

Swoole and RoadRunner keep PHP processes alive between requests. Lambda’s warm containers achieve similar performance (~2ms/request). The difference: you manage 24/7 servers vs. AWS manages infrastructure with pay-per-request and zero idle cost. Lambda wins for variable traffic; Swoole/RoadRunner for consistent high-volume workloads where you’re already managing servers.

Repository: github.com/marcguyer/lambda-php


This post is part of my serverless PHP series.

This post was written collaboratively with AI assistance, practicing the iterative refinement approach it advocates.


    Series: serverless-php

    1. Building a PHP 8.4 Lambda Runtime: Performance Analysis

    See also

    comments powered by Disqus