PHP
Detected from composer.json. Served with FrankenPHP.
See Buildpacksfor how detection and version policy work. A tree that also has package.json gets Node.jsin the same build (Laravel + Vite).
Detects
composer.json in the base directory.
Versions
Default is PHP 8.3. Published lines as of September 2026: 8.2-8.5. You get the latest patch of the requested minor.
Pin, in this order:
.php-versionrequire.phpincomposer.json(a range picks the highest matching published version)
Composer itself is a build tool. You do not pin it.
Install and build
composer install --no-dev --optimize-autoloader. Laravel (artisan) then caches config, routes, views, and events when those Artisan commands exist. Symfony (bin/console plus symfony.lock) runs cache:warmup --env=prod.
Launch defaults: Laravel APP_ENV=production, LOG_CHANNEL=stderr, APP_DEBUG=false. Symfony APP_ENV=prod, APP_DEBUG=0.
Start command
Document root is public/, or web/, or the app root.
- Laravel Octane (lockfile lists
laravel/octane):php artisan octane:frankenphp --port=$PORT --host=0.0.0.0 - Otherwise:
frankenphp php-server --listen :$PORT --root <docroot>
Override with a Procfile. Listen on PORT.