Deploy Laravel on Vercel with Docker | Vercel Knowledge Base

Laravel can run in a container-backed Vercel Function. FrankenPHP combines the PHP runtime with a production web server built on Caddy. Vercel builds the application into an image, stores it in Vercel Container Registry, and scales Function instances with traffic.

This guide deploys a stateless Laravel application, routes requests through public/index.php, and explains how to supply configuration and store durable state.

Prerequisites

How it works

The project has these deployment-specific files:

Steps

1. Create the Laravel application

Create a Laravel project:

Terminal

composer create-project laravel/laravel vercel-docker-laravel

cd vercel-docker-laravel

Replace the default route in routes/web.php with a response that identifies the running framework:

routes/web.php

<?php

use Illuminate.Http.JsonResponse;

use Illuminate.support.Facades.Route;

Route::get('/', static function (): JsonResponse {

return response()->json([

'framework' => 'Laravel',

'message' => 'Hello from Laravel and FrankenPHP on Vercel',

]);

});

Add a JSON health endpoint after the / route:

routes/web.php

Route::get('/health', static function (): JsonResponse {

return response()->json(['status' => 'ok']);

});

2. Configure FrankenPHP

Create Caddyfile in the project root:

Caddyfile

:{$PORT:80} {

root * /app/public

encode zstd gzip

php_server {

try_files {path} index.php

}

}

3. Add Dockerfile.vercel

Create the Dockerfile.vercel file in the project root:

Dockerfile.vercel

FROM dunglas/frankenphp:1-php8.4-bookworm AS base

RUN cp "$PHP_INI_DIR/php.ini-production" "$PHP_INI_DIR/php.ini" \

&& install-php-extensions intl zip

WORKDIR /app

FROM base AS dependencies

COPY --from=composer:2 /usr/bin/composer /usr/bin/composer

COPY composer.json composer.lock ./

RUN composer install \

--no-dev \

--no-interaction \

--no-progress \

--no-scripts \

--optimize-autoloader \

--prefer-dist

COPY . .

RUN mkdir -p \

storage/framework/cache \

storage/framework/sessions \

storage/framework/views \

bootstrap/cache \

&& composer dump-autoload --no-dev --classmap-authoritative

FROM base AS runtime

COPY --from=dependencies --chown=www-data:www-data /app /app

COPY --chown=www-data:www-data Caddyfile /etc/frankenphp/Caddyfile

RUN setcap CAP_NET_BIND_SERVICE=+eip /usr/local/bin/frankenphp \

&& chown -R www-data:www-data /config/caddy /data/caddy

ENV PORT=80 \

APP_ENV=production \

APP_DEBUG=false \

LOG_CHANNEL=stderr \

CACHE_STORE=array \

SESSION_DRIVER=array \

QUEUE_CONNECTION=sync

USER www-data

CMD ["frankenphp", "run", "--config", "/etc/frankenphp/Caddyfile"]

4. Exclude local files from the image

Create a .dockerignore file to specify excluded files:

.dockerignore

.git

.env

.env.*

!.env.example

database/database.sqlite

node_modules

tests

vendor

5. Add vercel.json

Create a vercel.json file to configure your project:

vercel.json

{

"$schema": "https://openapi.vercel.sh/vercel.json",

"services": {

"app": {

"root": ".",

"entrypoint": "Dockerfile.vercel",

"runtime": "container"

}

},

"rewrites": [

{ "source": "/(.*)", "destination": { "service": "app" } }

]
}

6. Run locally

Laravel uses the APP_KEY environment variable to encrypt and authenticate cookies and session data.

Generate a local application key:

Terminal

php artisan key:generate --show

Store the displayed value in an uncommitted .env.local file:

Terminal

APP_KEY=base64:your_generated_app_key

7. Configure the application key

Add the generated key as an environment variable in your Vercel project:

Terminal

vercel env add APP_KEY

8. Deploy to Vercel

Once ready, deploy your project to Vercel with a single command:

Terminal

vercel deploy --prod
vercel deploy --prod

Database, sessions, and cache

Add database credentials as Vercel environment variables and install the matching PHP extension in Dockerfile.vercel, such as pdo_pgsql or pdo_mysql. Use an external database or Redis-compatible service for the state that must be shared across instances.