I gave a talk on serverless PHP at LonghornPHP 2022. This is the companion article I wrote for it. If you’re just looking for the slides, click here.
For PHP developers, servers occupy a weird space in our lives. We can’t work without them. But, for most of us, managing them is something that we’d rather avoid. Instead, we’re happy to pay for hosting companies to take the pain away.
But hosting companies don’t remove all your hosting headaches. If you’re managing a server yourself (or with the help of a tool like Laravel Forge or SpinupWP), you’re still on the hook if something happens and you still have to maintain them. (Ubuntu LTS still has a end of life! 😅)
And even with Platform-as-a-Service (PaaS), you’re not completely off the hook. You have to worry about scaling. This often leaves you overpaying for hosting because you have to be on a plan that can handle the worst-case scenario.
These are some problems that serverless PHP addresses. With serverless PHP, you pay for what you use. If your PHP application isn’t receiving any traffic, you’re not paying to keep a server online. But, at the same time, you get an infrastructure that can scale to handle thousands of requests within a minute.
What is serverless?
First things first, you’re probably asking yourself, “What is serverless? How can there be no servers?” These are legitimate questions and the main reason I really don’t like the term. It feels like someone is trying to sell you snake oil.
This brings us back to our question, “Are there really no servers with serverless!?” No, that’s false. There are still servers with serverless. This is why there are countless memes like the one below about it.
So what’s the deal with serverless then? What does it mean? Serverless was a marketing term coined to describe Functions as a Service.
A good way to think about Functions as a Service is to think about of it as “on-demand” computing. You upload code to a cloud provider. (We’ll only talk about Amazon Web Services here.) The code that you upload then gets executed by the cloud provider in response to an event.
You’ll notice that there’s no mention of servers or anything server related. That’s because Functions as a Service abstract away almost all server considerations. You can choose how much memory to give to the function, but that’s essentially it. You don’t have to think about anything else.
And that’s where the term serverless came from. With Functions as a Service, it’s not that there isn’t a server somewhere, it’s that you’re not responsible for it. It’s outside your sphere of concern. (Thanks Mike Healy for the great description!)
How does serverless PHP work?
Ok, so you hopefully have a better idea of what serverless is. That said, it doesn’t explain exactly how serverless PHP works. To understand that, let’s start by looking at how a PHP works at a high level.
PHP with a web server
As an interpreted language, you can simply upload your code to a web server to make a change. There’s no code to compile. It’s all handled automatically by the interpreter. Here, it’s the PHP interpreter.
When you make a request to an HTTP request to a PHP application, a web server (usually apache or nginx) will receive the request and figures out which PHP file you’re trying to access. Most of the time, it’s just the
index.php file in the root directory.
Once the web server knows which PHP file you’re requesting, it’ll call the PHP interpreter. The interpreter is the one that reads, parses and executes the PHP file you requested.
Executing this PHP file will generate some output. With a PHP web application, that output is an HTTP response more often than not containing HTML. The web server then takes this generated HTTP response and sends it back to your browser.
Ok, so you hopefully have a grasp of how PHP works with a server. Let’s move over the serverless PHP. How does that work compared to running it on a server?
Well, it’s in fact quite similar to how it works with a server. As we mentioned earlier, serverless computing is event-driven. So our serverless PHP interpreter awaits for an event and then that triggers your code to run.
With a web server, the web server receives an HTTP request. This event makes the web server call the PHP interpreter to process your code. This similarity with running PHP on a server is the primary reason serverless computing works so well with PHP.
So what’s the difference between the two then?
Well, the difference is that, with serverless, it’s not just PHP that runs without a server. It’s everything. So, instead of a web server, you use a specific cloud provider service. Below is a diagram of how it would look like if we used an API gateway instead of a web server:
As you can see, the API gateway receives the request from the browser. It converts the HTTP request to an event which it forwards to the serverless PHP application. The serverless PHP application runs and does its thing as usual and returns a result. The API gateway then transforms that result into an HTTP response and sends it back to the browser.
About the PHP Runtime
You probably noticed that there’s two parts to the “Serverless PHP” box. One is the PHP Runtime, and the other is your PHP code. The PHP runtime is definitely the unique element of serverless PHP.
What does it do? Well, the PHP runtime does two critical things.
First, it processes the event that Lambda receives, figures out what the event is requesting and then sends back a result that the API gateway can use as an HTTP response. This is very akin to what a web server does. If a web server gets a request for a file, it returns the file. If you make a request that it determines it should pass on to the PHP interpreter, it does that instead. When passing the request on to the PHP interpreter, the PHP runtime creates a FastCGI request, sends it to PHP-FPM and waits for the response.
That the PHP runtime manages this entire lifecycle brings up the second task that it’s responsible for. It’s that it manages the PHP-FPM process. For example, it ensures that we terminate it and stop the Lambda function after a certain amount of events get processed. (Much like the
pm.max_requests option does.)
It might seem weird to be using PHP-FPM for this, especially considering PHP-FPM only manages a single worker. The reason there’s a single worker is that there’s no need for more. Lambda can only handle a single event at a time, so any worker beyond the one handling the event will just stay idle.
Meanwhile, the reason to use PHP-FPM is that it’s faster to have a constantly running PHP process inside Lambda than spawning one for each event. It’s a similar reason why Nginx + PHP-FPM ended up being faster than the original Apache + PHP setup from the olden times. As you can see, there are a lot of parallels between serverless PHP works and how it works on a web server!
What are the advantages of serverless PHP?
We’ve now spent a good amount of time going over how serverless PHP works. There’s a good chance you’re wondering, “Why would I want to use this?” You could just use a VPS or bare-metal server Linux box.
No servers to manage
PHP developers have a difficult relationship with servers. If you’re like me and you’ve been a sysadmin in some capacity for a long time, you’re comfortable with servers. Or maybe you just use a server management tool like Laravel Forge (for Laravel developers) or ServerPilot.
That said, managing a server comes with responsibility. You’re the one on call if there’s a problem. You’re in charge of keeping everything updated and secure. It’s just a lot of additional stress that we’d rather not deal with.
That’s why people opt to use a Platform as a Service (PaaS) like Heroku or Platform.sh. These services remove a lot of these headaches. You choose some rough specs for the environment and you deploy your application. They then take care of the rest for you.
Scaling to infinity
One problem that managing a server or a PaaS won’t solve is scaling. Scaling PHP applications with neither of these solutions can automate it and, if they can, it’s via the platform itself or using Kubernetes. In both cases, this scaling takes minutes and there’s often a limit to how much scaling you can do.
Serverless PHP doesn’t suffer from these scaling issues. As we saw earlier, the PHP runtime contains a PHP-FPM process with a single PHP worker. This means that, in reality, each Lambda function is a PHP worker. So AWS Lambda is essentially a hosting platform that lets us scale to thousands of PHP workers.
And it doesn’t take minutes to get that level of scaling. Depending on the AWS region you’re in, you can go from 0 to 3,000 Lambda functions instantly. (You can read more about it in the documentation.) This isn’t theoretical either. I’ve documented how I’ve run this exact load test.
The only way to match that kind of instant scaling would be to run 3,000 PHP-FPM workers out of a single server or a cluster of servers. And what kind of server capacity do you need to have 3,000 PHP-FPM workers?
There’s no easy way to figure that out. It depends on the PHP application that you’re running. You’ll need to tune your PHP-FPM configuration based on the resources it uses. (You can read more about how to do it in this guide.)
With serverless PHP, there’s none of that.
Usage based pricing
How much would it cost you to keep a server that can scale to 3,000 PHP-FPM workers? As mentioned in the last section, tuning PHP-FPM varies a lot based on the PHP application. But, at least with WordPress, there doesn’t exist a server large enough to support 3,000 workers.
With WordPress, the number of PHP-FPM workers should be between 2 and 4 times the total amount of threads. And right now, the largest servers you can get are 128 cores with 256 threads. This means that this server could manage 1,024 workers in the best-case scenario.
A bare metal server of that size costs $5,500/month on Vultr. You’d also need three of them to handle that traffic spike. This puts your total cost at $16,500/month to provision that worker capacity preemptively.
Now, you can mitigate that cost by scaling the infrastructure up and down. But that adds complexity to your hosting architecture.
Meanwhile, AWS Lambda offers all that natively. You’ll only pay for your PHP-FPM workers when they actually run. And AWS Lambda even charges you down to the millisecond.
This can end up saving you a lot of money. Mathieu Napoli (we’ll talk more about him later) did a small case study showing this. The case study showed how externals.io went from paying $50/month on Platform.sh to ~$17/month using serverless PHP on AWS Lambda.
Drawbacks of serverless PHP
As much as I like serverless PHP, I think it’d be disingenuous to not talk about its drawbacks. So here are a few things that you should keep in mind if you’re interested in using serverless PHP.
Less predictable cost
Since we were just talking about usage based pricing, it’s worth discussing the general drawback to it. It’s not something that’s specific to serverless PHP per se. But usage based pricing can cause a lot of anxiety even if you end up paying less.
That’s because, with usage based pricing, it’s harder to predict how much you’ll pay for hosting each month.
With a server, you know you have to pay $X/month. You budget the money for it and that’s it. There’s no surprise. When you need more performance, you get a larger server and pay $Y more each month.
Meanwhile, with serverless PHP, there’s a lot less predictability and, to get it, requires doing complex calculations. You have to estimate how many requests you’ll get in a month, how long they last on average. And that’s just for AWS Lambda! You have to do it for every usage based service.
So, while usage based pricing has great cost saving potential, you might find it more stressful and complicated to budget for. Regular servers have this predictability that just makes you feel safer. Even if you might pay 2-3 times more for it.
Not as useful for constant load
Another aspect of usage based pricing and serverless PHP is that it really shines with unpredictable workloads. By unpredictable, we mean your server idles most of the time, but sporadically gets a huge spike that requires a lot of computing power. A common example of that would be an e-commerce site that drives sales traffic through newsletters.
Normally, you’d have to get a server that’s powerful enough to handle those spikes. There’s no need to do that with serverless PHP. AWS Lambda will scale to handle the spike and then scale back down afterwards.
But let’s say you have a PHP application that receives a constant amount of traffic at all times. Serverless PHP isn’t as attractive of an option price wise in that scenario since you can maximize the use of your server hardware. It’s likely you can get something more affordable using servers instead.
Serverless computing is a service that all three major cloud providers (Microsoft Azure, AWS and Google Cloud Platform) offer. But, at this time, they’re not interchangeable. As we saw, serverless PHP doesn’t just need AWS Lambda, but some other AWS services such as the API gateway or CloudFront. Even if each vendor has an equivalent service, they don’t work or behave the same from one vendor to another.
Because of this, you can maybe use serverless PHP with all three of them. But I don’t know. And the reason I don’t know is that all the developers working on serverless PHP are focusing their energy on a single provider: AWS.
All the resources, all the projects and services that we will see next use AWS. You’re going to have a much harder time getting serverless PHP to work on Azure and GCP because of that. So it’s something to keep in mind if you can’t or don’t want to use AWS as your infrastructure provider.
Final note on evaluating serverless PHP compared to servers
Before we move on, I’d like to talk about evaluating serverless PHP and servers. It’s very tempting to compare serverless PHP and servers on a pure cost basis. This makes it easy to say, “Serverless is expensive. I could just get a $5/month droplet from DigitalOcean.”
This isn’t an accurate or honest way to compare the cost between the two. There are intangible costs around the two.
Maybe your company already has an ops team or you don’t mind being on call 24/7. Or maybe you want to have full control of your server environment. Or maybe you just enjoy working with servers! Those are all valid reasons for using servers beyond cost.
I’ve managed servers since I’m 16 and I actually enjoy it. But because I value my mental health, I still prefer serverless. I don’t want the stress of being on call.
But maybe you’re just a small company. You can’t afford to hire a DevOps person or don’t want to be on call 24/7. But you’re ok spending a bit more on infrastructure to have near perfect uptime.
These are just some examples. The important point is to be honest when you evaluate both options. Cost is just one part of the equation.
Serverless PHP tools and projects
So let’s say that I did my job well and you’re interested in looking into serverless PHP. Where do you start? Well, here are some tools and open source projects you can use to get started.
Serverless PHP wouldn’t be where it is today without Bref. Mathieu Napoli, who I mentioned earlier, is the original pioneer that got PHP working on AWS Lambda. Every other tool or project that we’ll see leverages parts of the work that Mathieu and the other Bref contributors have done.
So what is Bref? Bref is an open source project that helps you run a PHP application on AWS Lambda. It has several components.
The most important one is the PHP runtime that we discussed earlier. Bref has runtimes for all PHP versions going back to PHP 7.3. Combined with an integration layer for Laravel and Symfony, this should allow you to run most modern PHP applications on AWS Lambda.
Next, Bref provides the tooling to deploy your serverless PHP application on AWS. It uses the Serverless Framework to build, configure and deploy your serverless PHP application. Serverless Framework is also open source and has a huge community and a lot of online resources to help you use it.
Finally, Bref also provides extensive documentation. It has everything you need to help you deploy your first serverless PHP application. If you want even more information, Mathieu also has a paid course to help you. (It also helps support his work!)
At Laracon 2019, Taylor Otwell announced Laravel Vapor to the audience.
Vapor is a platform that lets you manage your entire serverless Laravel infrastructure on AWS. It builds off the initial components of Bref. It has a runtime and also lets you deploy serverless Laravel projects on AWS.
That said, Vapor also does a lot more than Bref. It’s really a platform for managing your entire serverless Laravel infrastructure on AWS. It can create and manage:
- RDS database servers
- SQS queues
- ElastiCache cache clusters
- SES emails
- Route53 DNS zones
This is just to name a few things. The point is that it manages all your AWS infrastructure for you. This goes way beyond what Bref offers with the Serverless framework.
Laravel Vapor also has good documentation. There’s also a paid course by Jack Ellis that’s worth it just for the Slack community. Lots of people working with serverless PHP hang out there, so it’s a great place to ask questions.
Finally, it’s worth mentioning that Vapor is a paid product. It’s $39/month and you’re in charge of your AWS bill. Definitely worth it if AWS intimidates you or you don’t want to manage AWS infrastructure.
I was at Laracon 2019 when Taylor announced Laravel Vapor. I’ve been a sysadmin since I’m a teenager and, even then, I still thought, “This is amazing!” Right there, I decided I needed to build this for WordPress.
So that’s what Ymir is. It’s a platform for running WordPress on AWS as a serverless PHP application. Like Vapor, Ymir lets you deploy your serverless WordPress site on AWS while it manages your infrastructure for you.
While inspired by Laravel Vapor, Ymir does some WordPress specific things that Laravel Vapor doesn’t do. For example, it does a lot more with CloudFront. You can have Ymir configure CloudFront to do HTML caching at edge. Ymir can also configure your CloudFront CDN to be an image optimizing CDN similar to the Bunny CDN dynamic image processing.
Back to simplicity
When I started using PHP over a decade ago, one of the coolest things about it was just how simple it was to use. You could just drop a PHP script on a web server and hit refresh and you’d see the result right away. Or you could cowboy code and edit the files directly on the server! 🤣
Over the years, PHP kept that simplicity that made it so endearing. But PHP applications became more complex to host. You needed more than just a web server and database server.
Server management tools and platforms as a service helped bridge that gap. But they never got it quite to where you could just deploy code and forget about it.
With serverless PHP, you now can.