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
- A Vercel account
- Docker Desktop or a running Docker daemon
- Vercel CLI (
npm install -g vercel) - Composer 2, when creating the project outside Docker
How it works
The project has these deployment-specific files:
routes/web.phpreturns a JSON response from Laravel and exposes/healthfor deployment smoke tests.Caddyfileexposes onlypublicand sends application routes topublic/index.php.Dockerfile.vercelinstalls production dependencies and copies the complete Laravel application into FrankenPHP.vercel.jsondeclares the container service and routes requests to it.
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.