AWS Lambda PHP Integration
Patterns for deploying PHP and Symfony applications on AWS Lambda using the Bref framework.
Overview
Two approaches available:
-
Bref Framework - Standard PHP on Lambda with Symfony support, built-in routing, cold start < 2s
-
Raw PHP - Minimal overhead, maximum control, cold start < 500ms
Both support API Gateway integration with production-ready configurations.
When to Use
-
Creating new Lambda functions in PHP
-
Migrating existing Symfony applications to Lambda
-
Optimizing cold start performance
-
Configuring API Gateway or SQS/SNS event triggers
-
Setting up deployment pipelines for PHP Lambda
Instructions
- Choose Your Approach
Approach Cold Start Best For Complexity
Bref < 2s Symfony apps, full-featured APIs Medium
Raw PHP < 500ms Simple handlers, maximum control Low
- Project Structure
Symfony with Bref Structure
my-symfony-lambda/ ├── composer.json ├── serverless.yml ├── public/ │ └── index.php # Lambda entry point ├── src/ │ └── Kernel.php # Symfony Kernel ├── config/ │ ├── bundles.php │ ├── routes.yaml │ └── services.yaml └── templates/
Raw PHP Structure
my-lambda-function/ ├── public/ │ └── index.php # Handler entry point ├── composer.json ├── serverless.yml └── src/ └── Services/
- Implementation
Symfony with Bref:
// public/index.php use Bref\Symfony\Bref; use App\Kernel; use Symfony\Component\HttpFoundation\Request;
require DIR.'/../vendor/autoload.php';
$kernel = new Kernel($_SERVER['APP_ENV'] ?? 'dev', $_SERVER['APP_DEBUG'] ?? true); $kernel->boot();
$bref = new Bref($kernel); return $bref->run($event, $context);
Raw PHP Handler:
// public/index.php use function Bref\Lambda\main;
main(function ($event) { $path = $event['path'] ?? '/'; $method = $event['httpMethod'] ?? 'GET';
return [
'statusCode' => 200,
'body' => json_encode(['message' => 'Hello from PHP Lambda!'])
];
});
- Cold Start Optimization
-
Lazy loading - Defer heavy services until needed
-
Disable unused Symfony features - Turn off validation, annotations
-
Optimize composer autoload - Use classmap for production
-
Use Bref optimized runtime - Leverage PHP 8.x optimizations
- Connection Management
// Cache AWS clients at function level use Aws\DynamoDb\DynamoDbClient;
class DatabaseService { private static ?DynamoDbClient $client = null;
public static function getClient(): DynamoDbClient
{
if (self::$client === null) {
self::$client = new DynamoDbClient([
'region' => getenv('AWS_REGION'),
'version' => 'latest'
]);
}
return self::$client;
}
}
Best Practices
Memory and Timeout
-
Memory: Start with 512MB for Symfony, 256MB for raw PHP
-
Timeout: Symfony 10-30s for cold start buffer, Raw PHP 3-10s typically sufficient
Dependencies
{ "require": { "php": "^8.2", "bref/bref": "^2.0", "symfony/framework-bundle": "^6.0" }, "config": { "optimize-autoloader": true, "preferred-install": "dist" } }
Error Handling
try { $result = processRequest($event); return [ 'statusCode' => 200, 'body' => json_encode($result) ]; } catch (ValidationException $e) { return [ 'statusCode' => 400, 'body' => json_encode(['error' => $e->getMessage()]) ]; } catch (Exception $e) { error_log($e->getMessage()); return [ 'statusCode' => 500, 'body' => json_encode(['error' => 'Internal error']) ]; }
Logging
error_log(json_encode([ 'level' => 'info', 'message' => 'Request processed', 'request_id' => $context->getAwsRequestId(), 'path' => $event['path'] ?? '/' ]));
Deployment
Serverless Configuration
serverless.yml
service: symfony-lambda-api
provider: name: aws runtime: php-82 memorySize: 512 timeout: 20
package: individually: true exclude: - '/node_modules/' - '/.git/'
functions: api: handler: public/index.php events: - http: path: /{proxy+} method: ANY
Deploy and Validate
1. Install Bref
composer require bref/bref --dev
2. Test locally (validate before deploy)
sam local invoke -e event.json
3. Deploy
vendor/bin/bref deploy
4. Verify deployment
aws lambda invoke --function-name symfony-lambda-api-api
--payload '{"path": "/", "httpMethod": "GET"}' /dev/stdout
Symfony Full Configuration
serverless.yml for Symfony
service: symfony-lambda-api
provider: name: aws runtime: php-82 stage: ${self:custom.stage} region: ${self:custom.region} environment: APP_ENV: ${self:custom.stage} APP_DEBUG: ${self:custom.isLocal} iam: role: statements: - Effect: Allow Action: - dynamodb:GetItem - dynamodb:PutItem Resource: '*'
functions: web: handler: public/index.php timeout: 30 memorySize: 1024 events: - http: path: /{proxy+} method: ANY
console: handler: bin/console timeout: 300 events: - schedule: rate(1 day)
plugins:
- ./vendor/bref/bref
custom: stage: dev region: us-east-1 isLocal: false
Constraints and Warnings
Lambda Limits
-
Deployment package: 250MB unzipped maximum (50MB zipped)
-
Memory: 128MB to 10GB
-
Timeout: 29 seconds (API Gateway), 15 minutes for async
-
Concurrent executions: 1000 default
PHP-Specific Considerations
-
Cold start: PHP has moderate cold start; use Bref for optimized runtimes
-
Dependencies: Keep composer.json minimal; use Lambda Layers for shared deps
-
PHP version: Use PHP 8.2+ for best Lambda performance
-
No local storage: Lambda containers are ephemeral; use S3/DynamoDB for persistence
Common Pitfalls
-
Large vendor folder - Exclude dev dependencies; use --no-dev
-
Session storage - Don't use local file storage; use DynamoDB
-
Long-running processes - Not suitable for Lambda; use ECS instead
-
Websockets - Use API Gateway WebSockets or AppSync instead
Security Considerations
-
Never hardcode credentials; use IAM roles and SSM Parameter Store
-
Validate all input data
-
Use least privilege IAM policies
-
Enable CloudTrail for audit logging
-
Set proper CORS headers
Examples
Example 1: Create a Symfony Lambda API
Input: "Create a Symfony Lambda REST API using Bref for a todo application"
Process:
-
Initialize Symfony project with composer create-project
-
Install Bref: composer require bref/bref
-
Configure serverless.yml
-
Set up routes in config/routes.yaml
-
Test locally: sam local invoke
-
Deploy: vendor/bin/bref deploy
-
Verify: aws lambda invoke --function-name <name> --payload '{}'
Output: Complete Symfony project structure with REST API, DynamoDB integration, deployment configuration
Example 2: Optimize Cold Start for Symfony
Input: "My Symfony Lambda has 5 second cold start, how do I optimize it?"
Process:
-
Analyze services loaded at startup
-
Disable unused Symfony features (validation, annotations)
-
Use lazy loading for heavy services
-
Optimize composer autoload
-
Measure: Deploy and invoke to verify cold start < 2s
Output: Refactored Symfony configuration with cold start < 2s
Example 3: Deploy with GitHub Actions
Input: "Configure CI/CD for Symfony Lambda with Serverless Framework"
Process:
-
Create GitHub Actions workflow
-
Set up PHP environment with composer
-
Run PHPUnit tests
-
Deploy with Serverless Framework
-
Validate: Check Lambda function exists and responds
Output: Complete .github/workflows/deploy.yml with multi-stage pipeline and test automation
References
-
Bref Lambda - Complete Bref setup, Symfony integration, routing
-
Raw PHP Lambda - Minimal handler patterns, caching, packaging
-
Serverless Deployment - Serverless Framework, SAM, CI/CD pipelines
-
Testing Lambda - PHPUnit, SAM Local, integration testing