DEV Community

Cover image for Navigating Laravel's Journey with Vercel and PlanetScale
Ankit Sinha
Ankit Sinha

Posted on

Navigating Laravel's Journey with Vercel and PlanetScale

In the ever-expanding cosmos of development tools, Laravel stands out as my trusted comet, swiftly cutting through the vastness of code. However, for my smaller projects, I sought a deployment solution that was not just cost-effective but also a breeze to set up

Here is my step-by-step guide on how to deploy Laravel applications on Vercel with a PlanetScale database (MySql):

  1. Create a new Laravel application.
  2. Configure the application for Vercel.
  3. Set up a PlanetScale database.
  4. Configure vervel with Environment Variables
  5. Automate the deployment process using Github Actions.

Now, let's sprinkle some creativity into your step-by-step guide:

🌌 Step 1: Creating a Laravel Constellation

We require an application for deployment. Let's swiftly conjure a new Laravel + Jetstream project—a constellation of code and creativity.

# Create a new Laravel project 
composer create-project laravel/laravel laravel-vercel-example

# Install Jetstream
composer require laravel/jetstream
php artisan jetstream:install livewire
Enter fullscreen mode Exit fullscreen mode

Next, we'll set up a local database using Docker Compose, update our environment variables, and run our migrations.

# docker-compose.yml
version: '3.8'

services:
  db:
    image: mysql:8.0
    command: --default-authentication-plugin=mysql_native_password
    restart: always
    environment:
      MYSQL_DATABASE: laravel
      MYSQL_ROOT_PASSWORD: password
    ports:
      - "3306:3306"
Enter fullscreen mode Exit fullscreen mode

Next, We need to update our .env file in our project

# .env
DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=db
DB_USERNAME=root
DB_PASSWORD=password
Enter fullscreen mode Exit fullscreen mode

Now, Once we had completed this, lets boot up db and migrate our base laravel tables,

# Start our database
docker-compose up -d

# Run migrations
php artisan migrate
Enter fullscreen mode Exit fullscreen mode

Now Create a project in Github and Push this project excluding your env files to Github, as we will be using github action to automatic deploy this application in vercel.

🚀 Step 2: Configuring the Vercel

Before deploying our application to Vercel, we need to make some changes,

  1. Set up a new Vercel project
  2. Create a vercel.json file
  3. Add our api/index.php entry point
  4. Set up our trusted proxies
  5. Ensure the vendor directory is not uploaded

Step 1. Setup a new Vecel project

We'll use the Vercel CLI to create a new project, which we plan to deploy later.

# Install the Vercel CLI
npm i -g vercel

# Login to Vercel
vercel login

# Create and link a new project
vercel link

Enter fullscreen mode Exit fullscreen mode

Follow the step-by-step instructions provided by vercel login and vercel link. This will generate a .vercel directory and automatically include it in your .gitignore.
Open the .vercel/project.json file and take note of the orgId and projectId as we'll need them later.

Step 2. Create a vercel.json file

// vercel.json
{
    "version": 2,
    "framework": null,
    "functions": {
        "api/index.php": { "runtime": "vercel-php@0.6.0" }
    },
    "outputDirectory": "public",
    "routes": [
        {
            "src": "/build/(.*)",
            "dest": "/build/$1"
        },
        {
            "src": "/(.*)",
            "dest": "/api/index.php"
        }
    ],
    "env": {
        "APP_ENV": "production",
        "APP_DEBUG": "true",
        "APP_URL": "YOUR APP URL",

        "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",

        "CACHE_DRIVER": "array",
        "LOG_CHANNEL": "stderr",
        "SESSION_DRIVER": "cookie",

        "DB_CONNECTION": "mysql",
        "DB_PORT": "3306",
        "MYSQL_ATTR_SSL_CA": "/etc/pki/tls/certs/ca-bundle.crt"
    }
}
Enter fullscreen mode Exit fullscreen mode

Do change runtime if using another version of PhP:

Let’s quickly explain some of the key concepts here.

functions

"functions": {
      "api/index.php": { "runtime": "vercel-php@0.6.0" }
  },
Enter fullscreen mode Exit fullscreen mode

Our application is essentially a single serverless function, which we'll create next. We use the community vercel-php runtime to set up the environment and automatically install dependencies with Composer.

outputDirectory

"outputDirectory": "public",
Enter fullscreen mode Exit fullscreen mode

Vercel builds our application by running npm run build, but by default, it looks for a dist directory. However, our assets are actually saved in public/build, so we need to instruct Vercel where to find them.

If you're using a tool other than Livewire, you may need to adjust this setting accordingly.

routes

"routes": [
    {
        "src": "/build/(.*)",
        "dest": "/build/$1"
    },
    {
        "src": "/(.*)",
        "dest": "/api/index.php"
    }
],
Enter fullscreen mode Exit fullscreen mode

The first route intercepts requests for CSS and JS assets and directs them to the appropriate files. All other requests are managed by our Laravel serverless function, as specified by the second route.

env

"env": {
"APP_ENV": "production",
"APP_DEBUG": "true",
"APP_URL": "YOUR APP URL",

"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",

"CACHE_DRIVER": "array",
"LOG_CHANNEL": "stderr",
"SESSION_DRIVER": "cookie",

"DB_CONNECTION": "mysql",
"DB_PORT": "3306",
"MYSQL_ATTR_SSL_CA": "/etc/pki/tls/certs/ca-bundle.crt"
Enter fullscreen mode Exit fullscreen mode

}
Here, we establish our non-secret environment variables. Take note of the MYSQL_ATTR_SSL_CA variable. This is crucial for a secure connection to PlanetScale in the following steps. We will set up our secret environment variables in the Vercel dashboard later on.

🎭 Step 3. Add our api/index.php entry point

// api/index.php
<?php

require __DIR__ . '/../public/index.php';
Enter fullscreen mode Exit fullscreen mode

This simple file redirects to the default index.php file provided by Laravel. This is necessary because Vercel only allows functions to be located in the api directory.

🛡️ Step 4. Set up our trusted proxies

Vercel hosts our code behind a load balancer, which forwards requests to port 80. This can confuse Laravel when it generates secure links. To solve this issue, we need to make a simple modification to TrustProxies.php.

// app/Http/Middleware/TrustProxies.php
<?php

namespace App\Http\Middleware;

use Illuminate\Http\Middleware\TrustProxies as Middleware;
use Illuminate\Http\Request;

class TrustProxies extends Middleware
{
    /**
     * The trusted proxies for this application.
     *
     * @var array<int, string>|string|null
     */
    protected $proxies = '*';

    /**
     * The headers that should be used to detect proxies.
     *
     * @var int
     */
    protected $headers =
        Request::HEADER_X_FORWARDED_FOR |
        Request::HEADER_X_FORWARDED_HOST |
        Request::HEADER_X_FORWARDED_PORT |
        Request::HEADER_X_FORWARDED_PROTO |
        Request::HEADER_X_FORWARDED_AWS_ELB;
}
Enter fullscreen mode Exit fullscreen mode

🚫 Step 5. Ensure the vendor directory is not uploaded

Lastly, we need to add a .vercelignore file. This will prevent our vendor (created during composer installation) and any pre-built files from being included, as Vercel will build these for us.

# .vercelignore
/vendor
/public/build
Enter fullscreen mode Exit fullscreen mode

🪐 Step 3. Set up a PlanetScale database.

Visit PlanetScale and create an account if you haven't already. From the dashboard, create a new database.

Once your database is operational, click on "Connect". Generate a new password and record the configuration details.

🌐 Step 4. Configure vervel with Environment Variables

Now we'll add those configuration details to Vercel. This will allow our deployed application to access the database. Go to the dashboard and add the following environment variables:

  • APP_KEY which can be generated by running php artisan key:generate
  • DB_HOST From PlanetScale Database Dashboard
  • DB_DATABASE From PlanetScale Database Dashboard
  • DB_USERNAME From PlanetScale Database Dashboard
  • DB_PASSWORD From PlanetScale Database Dashboard

🚀 Step 5. Automated deployment with Github Actions

Now, we can integrate everything with Github Actions. We will set up a workflow that automatically applies migrations to PlanetScale and deploys our application to Vercel.

To start, add the following .github/workflows/main.yaml file:

on:
    push:
        branches: main
jobs:
    deploy:
        name: Deploy
        runs-on: ubuntu-latest
        steps:
            - uses: actions/checkout@v3

            - name: Setup PHP
              uses: shivammathur/setup-php@v2
              with:
                php-version: '8.2'

            - name: Install Vercel CLI
              run: npm install --global vercel@latest

            - name: Install Dependencies
              run: composer install

            - name: Migrate DB
              run: |
                php artisan migrate --force
              env:
                APP_ENV: production
                DB_CONNECTION: mysql
                DB_HOST: ${{ secrets.DB_HOST }}
                DB_PORT: 3306
                DB_DATABASE: ${{ secrets.DB_DATABASE }}
                DB_USERNAME: ${{ secrets.DB_USERNAME }}
                DB_PASSWORD: ${{ secrets.DB_PASSWORD }}
                MYSQL_ATTR_SSL_CA: /etc/ssl/certs/ca-certificates.crt

            - name: Deploy to Vercel
              run: vercel deploy --prod --token=${{ secrets.VERCEL_TOKEN }}
              env:
                VERCEL_ORG_ID: ${{ secrets.VERCEL_ORG_ID }}
                VERCEL_PROJECT_ID: ${{ secrets.VERCEL_PROJECT_ID }
Enter fullscreen mode Exit fullscreen mode

Next, add the following secrets to your Github dashboard:

From PlanetScale:
DB_HOST
DB_DATABASE
DB_USERNAME
DB_PASSWORD

From .vercel/project.json:
VERCEL_ORG_ID
VERCEL_PROJECT_ID

From Vercel settings page:

VERCEL_TOKEN

Now, with secrets in place, every push to the main branch triggers a symphony of actions—installing dependencies, running migrations, and deploying our Laravel creation to Vercel.

Finally, push everything to Github.

Let’s take a closer look at what this workflow is doing:

Your code will be checked out.

  • A PHP environment will be set up, giving you access to php and composer.
  • The Vercel CLI will be installed.
  • Your application dependencies will be installed.
  • Migrations will be run with php artisan migrate --force.
  • Your application will be deployed to Vercel.

You should now have a deployed and functioning application!

Top comments (1)

Collapse
 
matmitcheil profile image
Mat Mitcheil Ando

Thanks sensei