This was an experience I wanted to have because I’ve been aware of Laravel Vapor and the possibility of running Laravel on AWS serverless infrastructure. As part of my process of learning more about AWS, this felt like a great challenge.

I have several sites running on my shared hosting setup, which you can find in my previous article. I decided to move only one of them: a site I use for personal stuff, not something critical. It made sense as an experiment for having this serverless experience.

When I started to investigate what I needed, something surprised me. The cold start time for Lambda functions was around 8 seconds. That was a very important factor in my decision to use a site with very low traffic and only for internal use. I don’t have a problem waiting because it’s just me accessing it occasionally. But this delay would not be acceptable for any of my real sites. The user experience would suffer too much.

I investigated and there are options to keep Lambda functions warm, either through AWS configuration or by calling a scheduled function that keeps everything ready. But that would have implied spending much more. This site was previously part of my shared hosting, and now I’m running it as a completely separate serverless stack, which is already an extra cost. If I had to keep it running all the time, the cost would be much higher than it is now. Since I access this particular site very infrequently, I just accepted the delay.

Then I started to understand how to divide the execution and what pieces I needed: API Gateway, CloudFront, all the IAM roles, and how to put everything in Terraform.

Understanding Bref

The first thing I needed to investigate was Bref and what role it plays. Lambda doesn’t support PHP natively, so Bref provides the runtime as a Lambda Layer. It’s just a pre-built PHP binary that Lambda loads before running your code. There’s also a specific package for Laravel integration. I had to understand that Lambda Layers are basically “code that runs before your code.” Once that clicked, the rest made more sense.

The Problems I Hit

After deploying, my site returned 403 errors. After some debugging, I discovered that CloudFront was forwarding the original Host header to API Gateway, which rejected it. The fix was using AWS’s managed policy AllViewerExceptHostHeader instead of AllViewer. A small detail, but frustrating to find.

I also hit a “419 Page Expired” error on form submissions. I knew sessions needed to be stored in the database for Lambda, and the Laravel app was configured correctly. But when I checked the Terraform code, I found SESSION_DRIVER=array in the environment variables. The array driver stores sessions only in memory for the current request, so the CSRF token was lost between page load and form submit. A quick fix once I traced it.

Another issue: when running terraform apply, it just waited. For about 30 minutes I was staring at the terminal, not understanding why. It turns out Terraform was waiting for ACM certificate validation, which requires adding DNS records in Cloudflare. I could have avoided this by using terraform apply -target to create the certificate first, add the DNS records, then apply the rest. It wasn’t a big mistake, I just added the records and continued, but it was a lesson learned.

The Code

I organized everything to be reusable. I implemented it for only one site, but the configuration is generic. If I want to add more sites, it’s ready to handle an array of them, as you can see in the code.

You can find the repository here: github.com/raulprdev/aws-serverless-laravel

Should You Do This?

Serverless Laravel makes sense when:

  • Traffic is low or unpredictable
  • You can tolerate cold starts, or you’re willing to pay for warm-up
  • You want zero server maintenance

Stick with EC2 or containers when:

  • You need consistent response times
  • Traffic is steady enough that always-on compute makes sense
  • You’re running multiple sites that share resources efficiently

For me, this was primarily a learning experience. I hope this is useful for you. It was an interesting experience and I will continue monitoring the application to see how it goes.