FrankenPHP для Laravel: современный application-сервер вместо php-fpm — PROG-TIME

FrankenPHP для Laravel: современный application-сервер вместо php-fpm

30.05.2026

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.