🇵🇸 Solidarity with Palestine.We seek justice and peace, and strongly oppose all forms of injustice and genocide.
Back

Laravel on the Edge with Vercel

So you had another idea for your next backend side project and, of course, as a PHP developer, you decided to go with your beloved Laravel. But! Instead of spending another $6 on a new droplet on DigitalOcean, you decided to go serverless for free with Vercel; or at least this was your short story.

In this article, I'll share my experience deploying a Laravel 11 API with Vercel. This journey has many checkpoints, as mentioned below:

serverless with php, really?

Yes, really. The initial motive was simple: finding an alternative platform where I can deploy a Laravel app fast & free. Since I'm familiar with Vercel, it was the first option that popped into my mind. Also, I somehow remembered that it supports PHP runtime now, so why not give it a try?

Serverless trade-offs

If you're not familiar with the term, serverless is a cloud computing model where the cloud provider manages the infrastructure and automatically allocates resources as needed. This is great for scalability for spike traffic, and you don't overload your gray matter with managing and maintaining the infrastructure. But this comes with some trade-offs:

  • Read-only filesystem: Not great if you want to use an SQLite database, for example. This will force you to use an external database service like Supabase.
  • No SSH: Because there's no server.
  • No long-running processes: In Vercel's case, there's a maximum 10-second timeout for requests, for the free tier plan at least. You can always upgrade to a pro plan to increase the duration

And honestly, these trade-offs aren't that bad, especially if you're building a small app or a POC or, as in my case, a backend api.

update: Vercel has increased the maximum time from 10 to 60 seconds, as mentioned by their VP @leeerob on a thread I've posted on Twitter (X) (he's so reactive, by the way!). This came after doing a quick performance benchmark (more on that later).

Bootstrapping a Laravel project

notice: The whole process is defined in detail in a repo-template I've made while tinkering with Vercel's edge functions. You can check it out here.

I've also tried to make relevant atomic commits for easier history navigation. I'm just going to give you a brief overview of the process.

1. Vercel's php runtimes

Vercel doesn't support PHP out of the box, but it does offer a powerful runtime system that allows you to run PHP applications. The runtime system is based on the vercel-php package, which provides support for different PHP versions from 7.4 to 8.3. Here are the available versions:

  • vercel-php@0.7.1 for PHP 8.3.x
  • vercel-php@0.6.2 for PHP 8.2.x
  • vercel-php@0.5.5 for PHP 8.1.x
  • vercel-php@0.4.4 for PHP 8.0.x
  • vercel-php@0.3.6 for PHP 7.4.x

To choose a specific version, you can add a vercel.json file to your project's root directory with the following content:

vercel.json
{
  "version": 2,
  "functions": {
    "api/index.php": {
      "runtime": "vercel-php@0.6.1" // using php 8.2 here
    }
  }
}

2. vercel's basic config

there's a lot of tutorials out there that can help you with the basic setup, but here's a quick overview of the steps:

  • Create a /api/index.php file in your project's root directory and require public/index.php to forward requests to it.
  • Set up your environment variables in vercel.json.
  • Set up environment variables in the Vercel dashboard (APP_KEY and secrets).
  • Set up your Vercel secrets in your GitHub secrets (for CI/CD, more on that later).

Encountered hassles

I'm just going to speedlist the problems I faced and how I solved them:

  • Read-only logs (permissions): Fixed by:
    • Adding Sentry (added a Sentry DSN to my environment variables)
    • in vercel.json, setting the variables:
      • APP_ENV to production
      • APP_DEBUG to false
  • /api endpoint returning 404:
  • 500 error with Vercel's CICD on git push:
    • Solved by switching deployment from Vercel's CICD to GitHub Actions.
    • Disabled Vercel's auto deployment in vercel.json
    • Added a GitHub Action to deploy on push to the master branch

Speaking of the 500 error, I didn't dig deep into the issue, and the error didn't occur when deployed manually with the Vercel CLI, so I just switched to GitHub Actions for the deployment.

This was also an opportunity to add extra steps to the deployment process, like running migrations, seeding the database, and forcing deployment without cache.

The said benchmark

The second thing I thought about while doing my deployment on Vercel was comparing how the instance would perform against a bare metal server you actually control, in our case, the cheapest droplet on DigitalOcean.

So, using a SQLite database seeded with random users, I've run a small benchmark with operations such as queries with Eloquent and some Collections operations, as listed below:

  • Count users
  • Count users after a select
  • Select all users
  • Map & groupBy operation on a user's collection

The tests were run with the help of Laravel's Benchmark helper, which was useful to define the number of runs of any code block to simulate a spike test as accurately as possible.

For comparison purposes, I've run the test on my local machine, un-dockerized, with the following configuration:

Below is the self-explanatory code for the tests:

Finally, the tests were run 2500 times in our case. The time each operation took is in milliseconds and is the average of each operation for the same 2500 iterations.

Surprisingly, Vercel was a bit faster than DigitalOcean. Here are the results

  • On my local machine: The total time needed for the logic is 3.17 seconds
  • On the droplet: 8.55 seconds needed to run logic, 9.79 seconds for full lifecycle framework.
  • With edge functions: 7.4 seconds needed, 8.76 seconds for lifecycle.

You can try it out by yourself using the endpoint /backend/benchmark/users/{iterations}, Don't forget to specify the desired value of the iteration query parameter.

I've shared some more insights and details (earlier) respectively on a reddit post and a twitter thread if you want to check them out.

conclusion

So, what did I learn from this experience? Well, Vercel is a great platform for deploying small apps or POCs, especially for a cost-effective solution.

The trade-offs are manageable, and the ease of use and built-in CI/CD are definite advantages compared to DigitalOcean's app platform, which offers a similar experience but is only free for your first three static site projects.

However, for more control and potentially longer-running operations, queue and cron jobs for example, a traditional VPS like a DigitalOcean droplet might be a better fit.

There are other things I didn't try them out yet with the platform:

  • cron jobs
  • redis queues
  • laravel horizon
  • livewire and pulse
  • inertiajs

And that's a wrap! I hope this post gave you a good overview of deploying a laravel app on vercel, and if you have any questions or feedback, feel free to reach out to me on Twitter or open an issue on github for a possible tpyo :)


references