Ed Johnson-Williams

Deploy Laravel 11 for free on Vercel in 2024

Published:

Tags: Laravel

You can deploy Laravel on Vercel. For free!

This is nice if you’re coming to Laravel from Javascript land like me. In Javascript, Netlify and Vercel make it really easy to deploy something you’re just trying out for free without needing to worry about running a server.

Deploying Laravel on Vercel is more work compared to hosting Astro, SvelteKit etc on Vercel or Netlify. But Laravel does more stuff so it’s a tradeoff like everything else.

Tools like Ploi (referral) and Laravel Forge are probably the way forward for more substantial sites.

I’m going to assume you know a bit about Vercel and how to use its CLI and web interface.

Provide feedback to this post on Github. I will make changes to this post if you can point out the ways it can be improved.


1. Make a Laravel app

I’m going to assume you can do this or can find out how to do it.

2. Add these files to your Laravel project

  • api/index.php
  • vercel.json
  • .vercelignore

api/index.php

<?php

require __DIR__ . '/../public/index.php';

vercel.json

This config uses Frankfurt for the region but choose one near your likely users.

The /build/(.*) route rule has to be before the /(.*) route rule. The /build/(.*) route is for your Vite-generated assets like CSS and images. The /(.*) route is for your Laravel app.

The runtime is a PHP Runtime for Vercel Serverless Functions.

{
    "version": 2,
    "regions": [
        "fra1"
    ],
    "functions": {
        "api/index.php": {
            "runtime": "vercel-php@0.7.1"
        }
    },
    "routes": [
        {
            "src": "/build/(.*)",
            "dest": "/build/$1"
        },
        {
            "src": "/(.*)",
            "dest": "/api/index.php"
        }
    ],
    "outputDirectory": "public"
}

.vercelignore

/vendor

3. Edit .package.json

As of 4th July 2024, you need to add this to your package.json. Otherwise you’ll get a buildtime error of php: error while loading shared libraries: libssl.so.10: cannot open shared object file: No such file or directory.

"engines": { "node": "18.x" },

4. Edit .gitignore

Add .vercel to your .gitignore

5. Trust proxies

Go to bootstrap/app.php. Add $middleware->trustProxies(at: '*'); to the withMiddleware method so it looks like this:

->withMiddleware(function (Middleware $middleware) {
    $middleware->trustProxies(at: '*');
})

This step is new in Laravel 11 and needed if you want to host on Vercel which uses AWS. See more here.

6. Run vercel to deploy

It’ll error because of missing environment variables.

7. Set the APP_KEY environment variable

php artisan key:generate --show # in a Laravel project
# or
php -r "echo 'base64:' . base64_encode(random_bytes(32)) . PHP_EOL;" # elsewhere
vercel env add APP_KEY

8. Set the other environment variables

APP_URL=https://your-app-url.vercel.app
APP_ENV=production
APP_DEBUG=false
APP_CONFIG_CACHE=/tmp/config.php
APP_EVENTS_CACHE=/tmp/events.php
APP_PACKAGES_CACHE=/tmp/packages.php
APP_ROUTES_CACHE=/tmp/routes.php
APP_SERVICES_CACHE=/tmp/services.php
VIEW_COMPILED_PATH=/tmp
LOG_CHANNEL=stderr
SESSION_DRIVER=cookie

9. Set up a database (optional)

I’ve had a look around for database options that will work with a Laravel deployed on Vercel.

Turso

Turso is basically SQLite. To use Turso with Laravel, the best approach I can find is to add Turso as a database driver and follow Turso’s LibSQL Driver for Laravel installation instructions here.

This post used to mention this package and advised to se https not libsql in the DB_URL variable and to add these environment variables

DB_CONNECTION=turso
DB_URL=https://***.turso.io
DB_ACCESS_TOKEN=your_turso_access_token

I think it’s probably better to use the Turso-approved driver.

You have to go a long way to absolutely need to start paying for Turso.

Neon

Neon is Postgres. Neon lets you have a 500MB project for free. You can make multiple databases in Neon and use them as you like.

DB_CONNECTION="psql"
DB_URL="postgres://default:*****db_user*****@***db_password**.neon.tech:5432/****database_name****?sslmode=require"

Don’t use a pooled connection from Neon.

Cloudflare D1

Cloudflare D1 is also basically SQLite. To add it as a database driver for Laravel, follow these instructions. It looks like they would like to offer Cloudflare KV as a cache driver and Cloudflare Queues as a queue driver too but that’s not available as of 4th June 2024.

Aaron Francis did a good video on using Cloudflare D1 with Laravel.

Cache

You don’t have to set up Redis for a cache and session driver. You could use your database. But you can use Upstash for this if you want.

Add these environment variables.

CACHE_STORE="redis"
QUEUE_CONNECTION="redis"
REDIS_HOST=***********.upstash.io
REDIS_PASSWORD="your-upstash-password"
REDIS_PORT="your-upstash-port-eg-40682"
SESSION_DRIVER="redis"
REDIS_CACHE_DB="0"

Also add 'scheme' => 'tls' to both default and cache in the redis section of config/database.php. Otherwise, you’ll get an error: “Error while reading line from the server”.

Other things

When you install a new module using composer, you’ll probably get a build failure in Vercel such as Required package "predis/predis" is not present in the lock file. You need to redeploy and clear the Build Cache.

I had a look at the Vercel database and cache options. They look far more expensive than the other options I’ve looked at and are actually whitelabelled versions of Neon and Upstash anyway!

Disclaimer

I haven’t pushed this approach very hard. There might be limits that I haven’t reached yet. To help you deploy Laravel on a conventional server with a database, Redis, web server etc on Hetzner, Digital Ocean, AWS EC2 etc, you might want to use something like Ploi (referral).

Prior art

There’s some prior art from others which helped get to this point. I found some things in Laravel and Vercel have changed since these posts/project were made which is why I pieced together the instructions above. I couldn’t have done any of this without this work by Caleb Porzio and Luca Pacitto (Nembie).

An example of a new thing in my instructions would be that Vercel doesn’t support environment variables in vercel.json any more. The /build/(.*) route is another new thing here.


Provide feedback to this post. I will make changes to this post if you can point out the ways it can be improved.