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](/content/docs/container-registry/index.html), 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

- A [Vercel account](/content/site-root.html)
- Docker Desktop or a running Docker daemon
- [Vercel CLI](/content/docs/cli/index.html) (`npm install -g vercel`)
- Composer 2, when creating the project outside Docker

## How it works

The project has these deployment-specific files:

- `routes/web.php` returns a JSON response from Laravel and exposes `/health` for deployment smoke tests.
- `Caddyfile` exposes only `public` and sends application routes to `public/index.php`.
- `Dockerfile.vercel` installs production dependencies and copies the complete Laravel application into FrankenPHP.
- `vercel.json` declares the container service and routes requests to it.

## Steps

### 1. Create the Laravel application

Create a Laravel project:

Terminal

```bash
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
<?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

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

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

});
```

### 2. Configure FrankenPHP

Create `Caddyfile` in the project root:

Caddyfile

```plaintext
:{$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

```dockerfile
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

```plaintext
.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

```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

```bash
php artisan key:generate --show
```

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

Terminal

```dotenv
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

```bash
vercel env add APP_KEY
```

### 8. Deploy to Vercel

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

Terminal

```bash
vercel deploy --prod
```

```bash
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.
