Deploying Laravel on a serverless infrastructure like AWS Lambda (via Laravel Vapor or Bref) offers incredible benefits: automatic horizontal scaling, zero server maintenance, and paying only for the exact compute milliseconds you consume.
However, running a full-featured PHP framework in a serverless environment introduces one major engineering hurdle: Cold Starts. When traffic spikes or a new Lambda worker is spawned, the time it takes to boot the execution environment, load PHP extensions, and initialize the Laravel framework can introduce noticeable latency (500ms to 2+ seconds).
In this deep dive, we will explore practical, production-proven strategies to minimize cold start latencies and optimize serverless Laravel deployments on AWS.
1. What Causes Cold Starts in Laravel on Lambda?
When an AWS Lambda execution environment initializes, it performs three distinct steps:
- Environment Provisioning & Download: AWS allocates container resources and pulls your code archive or Docker image.
- Runtime Initialization: PHP-FPM / Bref runtime starts up, loading PHP extensions and OPcache.
- Application Bootstrap: Laravel boots service providers, compiles configuration/routes, and opens database connection pools.
Our optimization goal is simple: reduce the deployment payload size, pre-compile framework assets during build time, and optimize the underlying architecture.
2. Switch to ARM64 (AWS Graviton) Architecture
Running your Lambda functions on AWS Graviton processors (arm64) provides two immediate advantages: up to 20% lower execution cost and faster raw CPU initialization speeds compared to standard x86_64 architecture.
If you are using Bref (via serverless.yml), set the architecture flag:
provider:
name: aws
region: us-east-1
architecture: arm64
runtime: provided.al2023If using Laravel Vapor (in vapor.yml):
environments:
production:
memory: 1024
cli-memory: 512
runtime: 'php-8.4:arm'3. Pre-Compile Laravel Assets at Build Time (Not Runtime)
Never let Lambda run optimization commands during cold start invocations. All caching must happen during your CI/CD pipeline or inside your Docker build stage before the image reaches AWS.
Here is an optimized multi-stage Dockerfile using Bref that caches framework metadata ahead of time:
FROM bref/php-84-fpm:2-arm64 AS base
# Set working directory
WORKDIR /var/task
# Copy application files
COPY . .
# Install production dependencies only
RUN composer install --no-dev --prefer-dist --optimize-autoloader --no-interaction
# Pre-compile Laravel configs, routes, and views
RUN php artisan config:cache \
&& php artisan route:cache \
&& php artisan view:cache \
&& php artisan event:cache
# Set permissions
RUN chmod -R 755 /var/task/storage4. Right-Sizing Memory Allocations
AWS Lambda allocates CPU power proportionally to the amount of memory assigned to the function. While a 256MB function might seem cost-effective, it receives only a fraction of a dedicated vCPU, leading to slow cold boots.
Setting your function memory to 1024MB or 1792MB (1 full vCPU) often results in:
- 3x faster cold start initialization.
- Faster request execution times.
- Lower or equal total billing cost because execution duration is significantly shorter.
5. Managing Database Connections with AWS RDS Proxy
In standard server environments, PHP maintains a predictable number of database connections. On Lambda, if 500 concurrent requests arrive, AWS will spin up 500 Lambda workers, instantly opening 500 MySQL connections.
This triggers connection exhaustion (Too many connections) and adds significant TCP handshake latency to cold starts.
Solution: Use AWS RDS Proxy
- RDS Proxy holds open persistent connection pools to your MySQL/PostgreSQL instances.
- Incoming Lambda instances connect directly to RDS Proxy via low-latency local endpoints.
- Connections are shared and reused across concurrent Lambda invocations.
# In your .env file
DB_CONNECTION=mysql
DB_HOST=my-cluster-proxy.proxy-xxxxxx.us-east-1.rds.amazonaws.com
DB_PORT=3306
DB_DATABASE=production_db
DB_USERNAME=app_user
DB_PASSWORD=secret6. Eliminating Bloated Vendor Dependencies
Package size directly correlates to cold start download times. Audit your composer.json and prune unused packages. Specifically:
- Remove development packages using
--no-devduring build steps. - Exclude heavy local testing utilities, code sniffer rules, and documentation markdown from the deployed artifact.
- Use
.brefignoreor.vaporignoreto strip unnecessary directories liketests/,node_modules/, and unused storage folders.
Example .vaporignore:
tests
node_modules
.git
.github
*.md
phpunit.xml
docker-compose.yml7. When to Use Provisioned Concurrency
For mission-critical, latency-sensitive endpoints (like payment webhooks or checkout APIs), AWS offers Provisioned Concurrency. This keeps a predefined number of execution environments pre-warmed and ready to respond in sub-10ms.
In Laravel Vapor:
environments:
production:
concurrency: 10 # Keeps 10 instances pre-warmed 24/7Tip: Keep provisioned concurrency reserved only for core production APIs, as it incurs a flat hourly holding fee.
Conclusion
Serverless Laravel is completely viable for high-traffic production workloads when architected correctly. By switching to ARM64 Graviton instances, pre-compiling framework metadata at build time, using RDS Proxy for connection pooling, and optimizing container payloads, you can push cold start times down to acceptable sub-second levels while keeping warm requests lightning fast.