Laravel Vapor is an auto-scaling, serverless deployment platform for Laravel powered by AWS Lambda, API Gateway, S3, and CloudFront. Navigating the documentation requires understanding how traditional, stateful PHP-FPM architectures map to stateless, ephemeral micro-VM execution environments managed through code. This guide provides a deep operational companion to the Laravel Vapor documentation, unpacking its configuration runtime, database limits, background worker mechanics, and actual AWS bill drivers.
Most engineering teams migrate to serverless under the delusion that it removes infrastructure management entirely. In practice, running Laravel on AWS Lambda simply shifts your operational complexity from operating system maintenance to distributed systems engineering. Instead of tuning PHP-FPM pool sizes and worker daemons in Nginx, you must manage connection pool exhaustion against relational databases, cold start latency thresholds, execution time limits, and ephemeral filesystem storage.
When leveraged properly, Vapor eliminates infrastructure capacity planning, automatically absorbs tens of thousands of concurrent requests, and automates high-availability multi-zone redundancy. The key lies in understanding how vapor.yml translates to AWS CloudFormation templates, where AWS network latency creeps in, and how to structure background workloads to prevent cascading outages.
Core Architecture and How Vapor Runs PHP on Lambda
Understanding the Laravel Vapor documentation starts with understanding the execution lifecycle under AWS Lambda. Vapor does not use traditional Nginx or Apache web servers. Instead, it packages your Laravel application into a Docker container image or a zip artifact and boots it on an AWS Lambda micro-VM run by Firecracker.
When an HTTP request enters your application, it follows a deterministic serverless path:
- Edge Routing: The client hits an Amazon CloudFront distribution, which performs TLS termination and serves cached static assets directly from Amazon S3.
- API Gateway / Application Load Balancer: Non-static requests hit an HTTP API Gateway (default) or an Application Load Balancer (ALB). The gateway converts the raw HTTP payload into a standardized JSON event.
- Custom PHP Runtime: AWS Lambda boots an isolated micro-VM containing a specialized PHP binary compiled by the Vapor team. Vapor injects a custom runtime loop that processes the incoming JSON event, constructs a Symfony/Laravel request object, and passes it through the standard HTTP kernel.
- Response Marshalling: The kernel generates a Laravel response, which the Vapor runtime converts back into an HTTP-compatible JSON response payload sent back through API Gateway to the user.
This execution model changes fundamental assumptions about PHP performance. In a traditional PHP-FPM environment, worker processes persist across multiple HTTP requests, maintaining memory state and open persistent database sockets. Under AWS Lambda, instances scale from zero to thousands dynamically. When an instance is idle, AWS freezes its execution context. When traffic spikes, new isolated micro-VMs boot simultaneously, triggering cold starts.
id: 12345
name: analytics-api
environments:
production:
runtime: "docker"
memory: 1024
cli-memory: 2048
timeout: 28
concurrency: 500
database: production-aurora
cache: production-redis
domain: api.example.com
The manifest above illustrates the foundational configuration layer in vapor.yml. Each environment defines isolated memory, timeout, concurrency boundaries, and underlying AWS infrastructure dependencies.
Vapor Configuration Anatomy: The vapor.yml Specification
The vapor.yml file serves as the single source of truth for your infrastructure as code. Every deployment reads this file, determines environment variables, provisions necessary AWS IAM roles, updates Lambda function configurations, and synchronizes assets to S3.
Runtime Selection: Zip vs Docker
Vapor historically supported native runtime zip archives, but contemporary deployments rely heavily on Docker-based container runtimes. The container approach eliminates the strict 250MB uncompressed Lambda zip limit and allows you to install custom system dependencies, compiled PHP extensions, and specialized binaries like ImageMagick or FFmpeg directly through a Dockerfile.
Critical vapor.yml Directives
Configuring your environments requires understanding the exact behavioral knobs available:
- memory: Allocates RAM to your web Lambda function (from 128 MB up to 10,240 MB). AWS Lambda allocates CPU performance proportionally to memory. Allocating 1024 MB gives you roughly double the CPU shares of 512 MB, which directly reduces cold start times.
- timeout: Defines the maximum duration in seconds before Lambda terminates a web request. HTTP API Gateways have a hard AWS limit of 29 seconds. Configuring a timeout higher than 28 seconds for web requests will result in gateway 504 errors.
- concurrency: Sets the maximum number of concurrent Lambda instances allocated to this environment. Without concurrency limits, a rogue script or sudden traffic spike could consume your entire AWS account’s concurrency quota, starving other microservices.
- warm: Configures pre-warmed Lambda instances. Vapor invokes these functions periodically using EventBridge to keep micro-VMs initialized in memory, mitigating cold starts for latency-sensitive APIs.
For workloads requiring sophisticated asynchronous execution across distributed worker layers, modern Laravel architecture often leverages native primitives. You can review how Laravel concurrency mechanics and execution runtimes interact with ephemeral execution pools to optimize throughput without overloading worker nodes.
Database Strategies and the Connection Exhaustion Problem
The most common failure point when moving Laravel to Vapor is relational database saturation. In a traditional virtual machine setup, four 8-core application servers might maintain a pool of 200 connections to MySQL. Under serverless auto-scaling, if your application receives 3,000 simultaneous requests, AWS Lambda instantly spins up 3,000 isolated micro-VMs. Each container executes Laravel independently, immediately demanding its own database connection.
Standard MySQL or PostgreSQL instances will crash under this connection storm, exhausting max_connections in seconds and returning SQL connection errors (SQLSTATE[08004] [1040] Too many connections).
Amazon RDS Proxy Integration
To resolve this, Vapor architectures mandate the use of Amazon RDS Proxy. RDS Proxy sits between your Lambda functions and your relational database, maintaining a persistent pool of established connections to the database engine while allowing thousands of ephemeral Lambda containers to multiplex across those connections.
| Metric / Feature | Direct RDS Connection | RDS Proxy Architecture |
|---|---|---|
| Connection Scaling | Hard limit based on RAM (e.g. 500 max) | Multiplexes 10,000+ Lambda requests across shared pool |
| Failover Time | 30 to 60 seconds (DNS propagation) | Under 5 seconds (Proxy redirects traffic instantly) |
| CPU Overhead | High (TLS handshakes on every invocation) | Low (TLS terminated at proxy layer) |
| Connection Pinning | Not applicable | Occurs if using prepared statements incorrectly |
To prevent transaction pinning in RDS Proxy, ensure your Laravel database configuration disables emulation of prepared statements and avoids setting session-level SQL variables dynamically inside application requests.
Queue Workers and Ephemeral Background Processing
Background processing in Laravel Vapor fundamentally diverges from the traditional daemon-driven model where php artisan queue:work runs continuously under Supervisor. In Vapor, queues are powered natively by Amazon Simple Queue Service (SQS) and triggered via event-source mappings.
When an application dispatches a job to an SQS queue, AWS Lambda detects the message and invokes your application’s queue Lambda function automatically. The queue runtime boots, unpacks the job payload, executes the job class, and terminates or handles the next job in the batch.
Queue Concurrency and Visibility Timeout Rules
Two configuration variables dictate queue stability under high volume:
- visibility_timeout: The SQS visibility timeout must strictly exceed your Lambda queue timeout. If your queue timeout is 60 seconds, configure the SQS visibility timeout to at least 70 seconds. If visibility timeout is shorter, SQS will assume the job failed while it is still executing and deliver it to a second Lambda worker, causing duplicate processing.
- max_concurrency: Vapor allows you to restrict how many concurrent workers consume an SQS queue. This is critical when jobs interact with third-party rate-limited APIs or un-proxied legacy databases.
Because jobs run on ephemeral containers, long-running processes that require persistent disk buffers must explicitly write to /tmp or stream data directly to Amazon S3. Disk space in /tmp defaults to 512 MB unless configured up to 10,240 MB in vapor.yml.
Asset Handling, CDN Caching, and Ephemeral Storage
In standard server environments, compiled JavaScript, CSS, and uploaded images reside on local disks served directly by Nginx. On AWS Lambda, the local filesystem is ephemeral, read-only across standard directories, and wiped as soon as a micro-VM terminates.
The S3 and CloudFront Pipeline
During the deployment step (vapor deploy production), the Vapor CLI parses your build directory, compiles your Vite or Mix assets, and synchronizes them directly to an Amazon S3 bucket dedicated to static storage. CloudFront sits in front of this bucket with preconfigured edge cache headers.
To support this architecture, Laravel’s asset() helper must resolve assets through the CDN URL. Vapor automatically configures the ASSET_URL environment variable on your Lambda runtime to point to the CloudFront distribution domain.
Handling File Uploads
Web requests passing through API Gateway have a strict binary payload size limit of 10 MB. Direct multipart file uploads through standard Laravel HTTP controllers will immediately fail if the file exceeds this threshold. The documented and required pattern in Vapor is direct client-to-S3 uploads:
- The client requests a signed S3 upload URL from a lightweight Laravel controller using
Storage:disk('s3')->temporaryUploadUrl(). - The front-end client (using Vapor’s companion JavaScript SDK) sends the file binary directly to S3 via an HTTP PUT request.
- Upon successful upload, the client sends the generated S3 file key to the Laravel backend to complete domain processing.
For applications heavily reliant on real-time reactive components, understanding your frontend network transport is critical. If you are planning front-end migrations, reviewing how Laravel Livewire state synchronization and updates handle network latency on serverless APIs will help prevent client-side desynchronization issues.
Managing Cold Starts and Application Boot Performance
A cold start occurs when AWS Lambda allocates a brand new micro-VM, downloads the container image, initializes the runtime environment, and boots the Laravel framework framework kernel. For latency-sensitive production workloads, cold starts can introduce response delays ranging from 250 milliseconds to over 1.5 seconds.
Deconstructing the Boot Bottleneck
The cold start overhead consists of two phases: AWS platform initialization and Laravel kernel boot. You cannot control platform initialization, but you have direct control over framework boot time. When a Lambda instance boots, it must load Composer autoloader files, read configuration files, and register service providers.
Cold Start Mitigation Checklist
- Optimize Config and Route Caching: Always ensure
php artisan config:cacheandphp artisan route:cacherun during the build hook. Without cached manifests, Laravel scans dozens of files from disk on cold start. - Prune Deferred Service Providers: Audit your
config/app.phpor bootstrap providers. Remove packages that perform expensive operations inside theirregister()orboot()methods. - Tune Pre-Warmed Instances: Use the
warmparameter invapor.yml. Settingwarm: 5keeps five Lambda instances continuously initialized. When traffic surges past five concurrent requests, cold starts will occur for subsequent instances, but baseline traffic remains unaffected. - Increase Memory Allocations: Lambda allocates CPU performance proportionally to RAM. Bumping a function from 512 MB to 1024 MB or 1792 MB provides a dedicated vCPU slice, significantly cutting framework initialization time in half.
Network Topologies: VPC Routing, NAT Gateways, and Static IPs
By default, Lambda functions execute within an isolated AWS-managed service network. If your application needs to talk to private resources, such as an Amazon RDS MySQL database, an ElastiCache Redis cluster, or a private Elasticsearch node, your Lambda functions must be attached to a Virtual Private Cloud (VPC).
Attaching your application to a VPC alters its network topology and outbound internet egress.
The NAT Gateway Prerequisite
When a Lambda function is attached to a private VPC subnet, it loses outbound public internet access entirely. It can talk to your private database inside the subnet, but it can no longer communicate with Stripe, send emails via Postmark, or reach external third-party APIs. To restore outbound connectivity, your VPC must route outbound traffic through an AWS NAT Gateway situated in a public subnet with an attached Elastic IP.
| Configuration Topology | Database Access | Outbound Internet Access | AWS Infrastructure Cost Impact |
|---|---|---|---|
| No VPC (Default) | Requires public database (insecure) | Automatic via AWS edge | $0.00 base infrastructure cost |
| VPC with Public Subnets Only | Direct private routing | Fails (Lambda cannot route out) | $0.00 base infrastructure cost |
| VPC with Private Subnets + NAT Gateway | Direct private routing | Successful via Elastic IP | ~$32.40/month per NAT Gateway + data transfer |
This network configuration also provides the architectural answer for applications requiring a static outbound IP address. The Elastic IP attached to your NAT Gateway serves as the single egress IP for all outgoing API requests originating from your serverless Laravel application.
CI/CD Automation and Deployment Lifecycle Hooks
A robust continuous deployment pipeline for Laravel Vapor does not run vapor deploy directly from developer laptops. Deployments should execute through deterministic pipelines such as GitHub Actions, GitLab CI, or Bitbucket Pipelines using dedicated IAM service credentials.
Deployment Pipeline Phases
A standard Vapor deployment executes across three distinct phases defined in vapor.yml:
- Build Phase (Local CI runner): Compiles frontend assets, runs
composer install --no-dev, generates framework cache files, and builds the container image. - Pre-Deployment Hooks (Temporary Container): Executes database migrations or operational health checks before shifting production traffic to the new build. If a pre-deployment step fails, Vapor aborts the deployment without routing traffic to the broken artifact.
- Post-Deployment Hooks: Clears cache drivers, warms caches, or executes cache priming commands after the new Lambda function versions are active.
environments:
production:
runtime: "docker"
build:
- "composer install --no-dev --classmap-authoritative --optimize-autoloader"
- "npm ci && npm run build"
- "php artisan event:cache"
deploy:
- "php artisan migrate --force"
- "php artisan config:cache"
- "php artisan route:cache"
In the pipeline above, the build array executes entirely within your CI environment, producing an immutable artifact. The deploy array executes inside an ephemeral AWS Lambda container spun up exclusively for deployment maintenance before production traffic shifts.
Observability: CloudWatch Logs, Metrics, and Error Handling
In an environment where servers boot and shut down hundreds of times an hour, traditional file-based logging (such as inspecting storage/logs/laravel.log over SSH) is impossible. All standard standard PHP stdout and stderr streams are captured by AWS Lambda and routed into Amazon CloudWatch Logs.
Centralized Logging and the Structured JSON Standard
Laravel on Vapor automatically configures the logging pipeline to use the stderr log channel. For high-scale applications, plain text log strings become difficult to search across millions of CloudWatch records. Configuring structured JSON logging enables precise querying via CloudWatch Logs Insights.
// config/logging.php
'channels' => [
'vapor' => [
'driver' => 'monolog',
'handler' => Monolog\Handler\StreamHandler:class,
'formatter' => Monolog\Formatter\JsonFormatter:class,
'with' => [
'stream' => 'php://stderr',
],
'level' => env('LOG_LEVEL', 'info'),
],
],
Operational Metrics to Monitor
When operating Vapor in production, focus your alerting around four primary telemetry signals:
- Lambda ConcurrentExecutions: Approaching your AWS account concurrency quota (default 1,000 per region) will result in function throttling (HTTP 429).
- Lambda Duration: Steady increases in execution duration often signal unindexed database queries or external API degradation.
- API Gateway 5xx Errors: Differentiate between application-level exceptions (HTTP 500, logged in CloudWatch) and platform-level gateway timeouts (HTTP 504, occurring when execution exceeds 29 seconds).
- RDS Proxy DatabaseConnections: Tracks how effectively RDS Proxy is multiplexing incoming Lambda traffic into the physical database pool.
Comprehensive Cost Breakdown and Pricing Realities
Evaluating Laravel Vapor requires analyzing two separate billing layers: the fixed platform management fee charged by Laravel, and the variable infrastructure consumption bill billed directly by Amazon Web Services.
Vapor Platform Licensing
Laravel Vapor charges an annual fixed subscription fee for platform orchestration:
- Standard Vapor Plan: $399 per year (or $39 per month). This grants unlimited team members, unlimited application deployments, and automated provisioning across connected AWS accounts.
Real-World AWS Cost Models
Because AWS pricing is usage-based, costs scale strictly with database provisioning, network egress, and compute execution. Below is a realistic monthly cost model across three common application tiers running on AWS via Vapor.
| Infrastructure Component | Low Traffic (1M req/mo) | Medium Traffic (20M req/mo) | High Traffic (100M req/mo) |
|---|---|---|---|
| AWS Lambda (Compute) | $2.50 | $42.00 | $215.00 |
| API Gateway (HTTP API) | $1.00 | $20.00 | $100.00 |
| CloudFront + S3 (Assets) | $1.50 | $15.00 | $75.00 |
| Amazon RDS (Database) | $15.00 (db.t4g.micro) | $78.00 (db.t4g.medium) | $340.00 (Multi-AZ db.r6g.large) |
| Amazon RDS Proxy | $0.00 (Not used) | $18.00 | $36.00 |
| ElastiCache (Redis) | $0.00 (File cache) | $15.00 (cache.t4g.micro) | $68.00 (cache.m6g.large) |
| NAT Gateway (VPC Outbound) | $0.00 (No VPC) | $32.40 (Single AZ) | $64.80 (Multi-AZ HA) |
| CloudWatch Logs | $0.50 | $8.00 | $45.00 |
| Total Estimated AWS Cost | $20.50 / month | $228.40 / month | $943.80 / month |
Notice that for medium and high traffic workloads, AWS Lambda compute itself represents a minor fraction of the total bill. Fixed infrastructure components, specifically database instances, RDS Proxy endpoints, and NAT Gateways, dominate the monthly operational expense.
Explore the Broader Ecosystem
Serverless architecture represents just one deployment paradigm within modern backend engineering. Understanding when to select serverless execution over containerized orchestrations like Amazon ECS or bare-metal virtual machines depends heavily on team skill sets, traffic volatility, and database coupling.
Explore our complete Laravel, Basics directory for more guides.
Factors That Affect Development Cost
- Vapor Platform Subscription
- AWS Lambda Invocations and Duration
- Amazon RDS and RDS Proxy Sizing
- NAT Gateway Deployment for VPC Egress
- CloudWatch Log Ingestion and Retention
Total hosting expenses vary from roughly twenty dollars per month for hobby workloads to over nine hundred dollars per month for high-traffic enterprise architectures.
Migrating to Laravel Vapor requires an architectural mindset shift. It trades the predictable, deterministic operational patterns of single-server Linux administration for the elastic, decoupled resilience of cloud-native microservices. For applications with highly erratic traffic patterns, periodic batch workloads, or global user bases, Vapor eliminates capacity planning and server maintenance.
However, serverless is not an absolute architectural upgrade for every project. If your application relies on stateful, long-running WebSocket connections, complex background processes requiring hours of uninterrupted execution, or monolithic database setups that cannot leverage connection pooling, traditional provisioned virtual machines remain a sound engineering choice. Success with Laravel Vapor lies in respecting AWS operational constraints: proxy your database connections, externalize ephemeral assets, decouple background jobs, and monitor your network topologies closely.