FrankenPHP — это современный application-сервер для PHP, написанный на Go и построенный поверх Caddy. Он умеет запускать PHP-приложения в режиме long-running worker’ов, поддерживает HTTP/2, HTTP/3, автоматический TLS и режим встраивания приложения в один бинарь. Для Laravel-проектов FrankenPHP — это полноценная альтернатива Swoole и RoadRunner, причём с заметно более простой настройкой.
В этой статье разберём, чем FrankenPHP отличается от классической связки php-fpm + nginx, как поднять его за пару минут локально и в Docker, как подружить с Laravel Octane и какие подводные камни ждут в worker-режиме.
Почему FrankenPHP, а не php-fpm
Классическая схема «nginx → php-fpm» отлично работает много лет, но у неё есть известные узкие места:
- на каждый запрос инициализируется фреймворк, загружаются сервис-провайдеры, читается конфиг;
- nginx и php-fpm — это два независимых процесса, которые нужно настраивать и мониторить отдельно;
- веб-сокеты, SSE и long-polling реализовать поверх php-fpm неудобно.
FrankenPHP решает это так:
- веб-сервер и PHP живут в одном процессе, что снижает накладные расходы;
- worker mode позволяет инициализировать приложение один раз и далее обрабатывать запросы «горячими» воркерами — как Octane;
- встроенный Caddy даёт HTTPS из коробки, HTTP/2, HTTP/3 и удобный Caddyfile вместо громоздкой связки конфигов;
- есть режим standalone-бинаря: ваш Laravel-проект собирается в один исполняемый файл вместе с PHP — удобно для CLI-утилит и self-hosted раздач.
Быстрый старт локально
Проще всего попробовать FrankenPHP через Docker. Создадим минимальный Dockerfile для Laravel-проекта:
FROM dunglas/frankenphp:latest
# Системные зависимости Laravel
RUN install-php-extensions \
pdo_pgsql \
pdo_mysql \
redis \
intl \
opcache \
zip
WORKDIR /app
COPY . /app
# Composer уже есть в образе
RUN composer install --no-dev --optimize-autoloader \
&& php artisan config:cache \
&& php artisan route:cache \
&& php artisan view:cache
ENV SERVER_NAME=":80"
EXPOSE 80 443
CMD ["frankenphp", "run", "--config", "/etc/caddy/Caddyfile"]
Стандартный Caddyfile в образе уже умеет отдавать Laravel из public/. Если нужен свой, его минимальная версия выглядит так:
{
frankenphp
order php_server before file_server
}
:80 {
root * /app/public
encode zstd br gzip
php_server
}
Собираем и запускаем:
docker build -t laravel-frankenphp .
docker run --rm -p 8080:80 laravel-frankenphp
Открываем http://localhost:8080 — приложение поднимается без отдельного nginx и без php-fpm.
Worker mode: ускоряем Laravel в 3–5 раз
Главная фишка FrankenPHP — worker mode. В нём приложение бутстрапится один раз, и каждый воркер обрабатывает множество запросов, не пересоздавая контейнер сервисов. Это даёт результат, сравнимый с Swoole и RoadRunner, но без отдельных расширений и без своего event-loop’а в проекте.
Идеальный способ подружить Laravel с воркерами — Laravel Octane, у которого с 2024 года есть официальный драйвер для FrankenPHP:
composer require laravel/octane
php artisan octane:install --server=frankenphp
Octane сам сгенерирует worker-скрипт и подскажет, как запускать сервер. В Docker это выглядит так:
CMD ["php", "artisan", "octane:frankenphp", \
"--host=0.0.0.0", "--port=80", \
"--workers=4", "--max-requests=500"]
Что важно помнить про worker mode:
- состояние между запросами сохраняется — нужно следить за синглтонами и глобальными переменными;
- модель данных Eloquent безопасна, но кастомные сервисы лучше регистрировать как
scoped, а неsingleton; - параметр
--max-requestsзаставляет воркер перезапускаться раз в N запросов — страховка от утечек памяти; - любые изменения кода требуют перезапуска воркеров (в dev-режиме помогает
--watch).
HTTPS из коробки
FrankenPHP наследует от Caddy автоматическое получение сертификатов Let’s Encrypt. Если у вас есть домен и FrankenPHP смотрит наружу 80/443 портами, достаточно указать его в SERVER_NAME:
ENV SERVER_NAME="example.com"
При первом запуске Caddy сам выпустит сертификат, настроит HTTP/2 и HTTP/3, добавит редирект с http на https. Для разработки удобно использовать localhost — Caddy выпустит и установит локальный самоподписанный сертификат.
Healthcheck и интеграция в docker-compose
В отличие от php-fpm, FrankenPHP сам отвечает на HTTP-запросы, поэтому healthcheck пишется проще. Пример сервиса в docker-compose.yml:
services:
app:
build: .
ports:
- "8080:80"
environment:
SERVER_NAME: ":80"
APP_ENV: production
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost/up"]
interval: 10s
timeout: 3s
retries: 5
depends_on:
db:
condition: service_healthy
Маршрут /up Laravel отдаёт из коробки начиная с 11-й версии — он не лезет в базу и не делает ничего лишнего, поэтому идеально подходит для health-проверки.
FrankenPHP vs Swoole vs RoadRunner
Кратко сравним три популярных способа уйти от php-fpm.
- Swoole — самое старое и быстрое решение, но требует PECL-расширения, имеет много неочевидных нюансов с корутинами и любит конфликтовать с библиотеками, которые используют stream-функции.
- RoadRunner — Go-сервер, который держит пул PHP-воркеров и общается с ними по бинарному протоколу. Очень стабилен, но требует собственного бинаря и Procfile-стиля конфигов.
- FrankenPHP — Go-сервер, который встраивает PHP внутрь себя через SAPI. Не требует расширений, имеет встроенный HTTPS, поддерживает worker mode и режим standalone-бинаря.
Если вы стартуете новый проект на Laravel и хотите ускорения «без боли» — FrankenPHP сейчас оптимальный выбор. Если у вас уже работает RoadRunner, мигрировать не обязательно: разница в производительности на типичной нагрузке не настолько большая, чтобы оправдать переезд.
Подводные камни
Несколько вещей, на которые стоит обратить внимание перед продом:
- Логи. FrankenPHP пишет access-логи Caddy и stdout PHP. Если у вас уже настроена отправка логов в Loki/Telegram — проверьте, что они корректно собираются с обоих потоков.
- Сессии в файлах. Worker mode плохо дружит с файловыми сессиями: используйте Redis или базу.
- Глобальное состояние. Всё, что вы кладёте в
static-свойства классов, переживёт запрос. Это и плюс (кэш), и риск (утечка данных между пользователями) — пишите тесты. - OPCache. В worker mode эффект OPCache меньше, чем в обычном fpm, но включить его всё равно стоит — на CLI-командах и в фазе старта он экономит десятки миллисекунд.
Итого
FrankenPHP — это попытка собрать «всё для продакшена» в одном бинаре: веб-сервер, PHP, HTTPS, worker mode. Для Laravel-проектов он закрывает сразу несколько задач, которые раньше решались склейкой nginx + php-fpm + supervisor + certbot. В сочетании с Octane вы получаете быстрый рантайм без необходимости разбираться в тонкостях Swoole.
Если вы давно хотели попробовать что-то новое в инфраструктуре PHP — начните с локального Docker-эксперимента: пара строк в Dockerfile, и у вас уже самый современный application-сервер для PHP.