Установка
OxPHP распространяется в виде Docker-образа — это самый быстрый и рекомендуемый способ начать обслуживать PHP-приложения. Образ включает бинарный файл сервера, PHP 8.4 или 8.5 ZTS, расширение OxPHP и все зависимости времени выполнения на Alpine Linux. Теги по умолчанию :0.13.0 и :latest поставляются с PHP 8.5; для PHP 8.4 используйте теги :0.13.0-php8.4, :php8.4 или любой вариант *-php8.4*.
Docker (рекомендуется)
Скачайте официальный образ из GitHub Container Registry:
docker pull ghcr.io/oxphp/oxphp:0.13.0Образ включает:
- Бинарный файл сервера OxPHP — асинхронный HTTP-сервер
- Среда выполнения PHP ZTS — 8.4 или 8.5, в зависимости от скачанного тега; потокобезопасный PHP для выполнения с несколькими воркерами
- PHP-расширение OxPHP (
oxphp_sapi.so) — предоставляетoxphp_request_id(),oxphp_server_info(),oxphp_worker()и другие встроенные функции - Библиотека-мост (
liboxphp_bridge.so) — соединяет сервер на Rust со средой выполнения PHP - Базовый образ Alpine Linux — минимальный размер среды выполнения (сборка на Debian публикуется по ночам, см. Образ на Debian (glibc))
- Без директивы
USER— образ запускается от имени root (какnginx:alpine/php-fpm:alpine/frankenphp:alpine), чтобы иметь возможность привязываться к привилегированным портам, но затемoxphp serve/runпо умолчанию понижают привилегии доwww-dataперед началом обслуживания, поэтому трафик из коробки не обрабатывается от имени root. Пользовательwww-data(UID 82, GID 82; 33 в образе на Debian) создаётся заранее, а каталог/var/www/htmlпередаётся ему во владение на этапе сборки. Явно закрепите идентичность процесса на уровне оркестратора — для конкретного uid или дополнительной эшелонированной защиты:docker run --user www-data ghcr.io/oxphp/oxphp:0.13.0- Compose:
services.app.user: www-data - Kubernetes:
securityContext.runAsUser: 82
Образ на Debian (glibc)
Ночной конвейер также публикует тот же сервер, собранный на Debian trixie вместо Alpine: ghcr.io/oxphp/oxphp:nightly-trixie (PHP 8.4), :php8.4-nightly-trixie и :php8.5-nightly-trixie, для linux/amd64 и linux/arm64.
Релизы с тегами версий выходят только на Alpine. Ночные теги собираются из main в те ночи, когда он изменился с момента последнего полностью успешного прогона, и не дают никаких гарантий стабильности. Если в какую-то ночь сборка на Debian завершается неудачей, :nightly-trixie и :php<minor>-nightly-trixie остаются на последней успешной сборке, поэтому могут отставать от :nightly.
Образ на Debian тратит меньше процессорного времени на запрос, и тому есть две причины:
- Локальное хранилище потока. Потокобезопасный PHP обращается к состоянию движка, принадлежащему отдельному потоку, через thread-local-указатель. При сборке с musl PHP оставляет этот указатель на модели TLS global-dynamic, поэтому код в
libphp.soполучает его через вызов в среду выполнения TLS; с glibc используется initial-exec — загрузка относительно указателя потока. - Аллокатор. Аллокатор musl, которым расширения и библиотеки вроде libpq пользуются наряду с собственным менеджером памяти PHP, отображает свежую память под результаты запросов libpq и снимает это отображение, когда они освобождаются. На x86-64 это оборачивается TLB shootdown между потоками сервера.
Процессорное время контейнера OxPHP на один ответ, образ на Debian против образа на Alpine, при одной и той же ревизии OxPHP и одном и том же патч-релизе PHP 8.5: приложение Yii3 под wrk -c64, медиана шести чередующихся прогонов. Каждая строка измерена на одном хосте: x86-64 — на облачной ВМ с 16 vCPU (AMD EPYC 9575F), arm64 — под Docker Desktop на Apple silicon. Собственное процессорное время Postgres не учитывается.
| Страница Yii3 | Страница с чтением из Postgres | |
|---|---|---|
| x86-64, традиционный режим | −18.9% | −14.1% |
| x86-64, режим воркеров | −18.1% | −18.6% |
| arm64, традиционный режим | −8.5% | −9.0% |
Что меняется для Dockerfile, построенного на этом образе:
- Пакеты ставятся через
apt-get update && apt-get install -y --no-install-recommends, а не черезapk, и под именами Debian: например,libicu-devиlibpq-devдля сборкиintlиpdo_pgsql,libicu76иlibpq5во время выполнения. - Собирайте расширения в стадии
php:<minor>-zts-trixie. Файл.so, собранный с musl в стадии-zts-alpine, несовместим с этим образом, как и наоборот. - У
www-dataUID/GID 33, а не 82:securityContext.runAsUser: 33, и всё, что передаётся во владение по числовому идентификатору, должно использовать 33. wgetнет, но естьcurl. Проверка работоспособности, написанная для образа на Alpine какwget --spider http://localhost:9090/health, превращается вcurl -fsS -o /dev/null http://localhost:9090/health.- Образ больше: база PHP занимает около 170–180 MiB в сжатом виде против примерно 45–50 MiB у Alpine.
Структура образа
Расположение файлов в образе среды выполнения:
/usr/local/
├── bin/
│ └── oxphp # server binary
├── lib/
│ ├── libphp.so # PHP ZTS runtime (8.4 or 8.5, matches the image tag)
│ ├── liboxphp_bridge.so # C bridge library
│ └── php/extensions/no-debug-zts-<ABI>/
│ ├── oxphp_sapi.so # OxPHP PHP extension
│ └── sodium.so, ... # from the PHP base image; the set depends on the tag
└── etc/php/
└── conf.d/
├── docker-php-ext-*.ini # from the PHP base image; the set depends on the tag
└── extension.ini # extension=oxphp_sapi.soЗначение <ABI> зависит от минорной версии PHP. PHP 8.4 использует 20240924, PHP 8.5 использует 20250925. В примерах ниже жёстко указан 20250925, потому что их строка FROM нацелена на php:8.5-zts-alpine3.23 (это PHP тега по умолчанию :0.13.0) — при смене FROM нужно сменить и дату. Чтобы вывести значение переносимо прямо во время сборки:
php -r 'echo ini_get("extension_dir");'
# /usr/local/lib/php/extensions/no-debug-zts-20250925Используйте $(php -r 'echo ini_get("extension_dir");') в командах оболочки, чтобы не задавать значение вручную.
В образе нет собственного php.ini (помимо conf.d/, в /usr/local/etc/php/ лежат только исходные образцы php.ini-development и php.ini-production из upstream, и PHP не загружает ни один из них), а также нет никаких настроек приложения. Передайте свои в виде .ini-файла в conf.d — смонтированного или скопированного в образ; см. руководство по Docker.
Имя файла определяет, будет ли .ini прочитан и когда. PHP разбирает все файлы .ini в conf.d в алфавитном порядке и молча пропускает файлы с любым другим именем. Для обычной настройки побеждает последнее прочитанное значение, поэтому файл, который её переопределяет, должен идти в сортировке после файла, который её задаёт. Директивы extension= и zend_extension=, напротив, собираются из всех файлов и загружаются все, так что более поздняя extension= никогда не заменяет более раннюю.
Три компонента OxPHP и их назначение; размеры измерены в образе 0.11.0. Размеры различаются для linux/amd64 и linux/arm64 и меняются от релиза к релизу:
| Компонент | Размер | Назначение |
|---|---|---|
oxphp |
~12–15 МБ | HTTP-сервер, маршрутизация, плагины, метрики |
liboxphp_bridge.so |
~150–170 КБ | Разделяемая библиотека-мост, связывающая сервер со средой выполнения PHP |
oxphp_sapi.so |
~700 КБ | PHP-функции (oxphp_request_id(), OxPHP\Http\Request и т. д.) |
Цепочка зависимостей:
graph LR oxphp["oxphp"] --> libphp["libphp.so"] libphp --> deps["libxml2, libcurl, libsqlite3, libonig, ..."] oxphp --> bridge["liboxphp_bridge.so"] sapi["oxphp_sapi.so"] --> bridge
Бинарный файл oxphp линкуется с libphp.so и liboxphp_bridge.so. PHP-расширение oxphp_sapi.so также линкуется с библиотекой-мостом, чтобы состояние отдельного запроса было доступно вашему PHP-коду.
Минимальный Dockerfile
Базовый образ php:8.5-zts-alpine3.23 (или php:8.4-zts-alpine3.23) уже содержит libphp.so и все его зависимости. Минорную версию PHP в вашем FROM согласуйте с тегом OxPHP, из которого вы копируете, и библиотеку C тоже: артефакты из тега -trixie ставятся на php:<minor>-zts-trixie, а не на базу Alpine. Достаточно скопировать три артефакта OxPHP и добавить libgcc: с ней линкуется собранный на Rust бинарный файл oxphp, а в PHP-образе на Alpine её нет:
FROM php:8.5-zts-alpine3.23
RUN apk add --no-cache libgcc
COPY --from=ghcr.io/oxphp/oxphp:0.13.0 /usr/local/bin/oxphp /usr/local/bin/oxphp
COPY --from=ghcr.io/oxphp/oxphp:0.13.0 /usr/local/lib/liboxphp_bridge.so /usr/local/lib/
COPY --from=ghcr.io/oxphp/oxphp:0.13.0 /usr/local/lib/php/extensions/no-debug-zts-20250925/oxphp_sapi.so /usr/local/lib/php/extensions/no-debug-zts-20250925/
RUN echo "extension=oxphp_sapi.so" > /usr/local/etc/php/conf.d/oxphp.ini
COPY --chown=www-data:www-data . /var/www/html/public
EXPOSE 80 443
CMD ["oxphp"]Этот подход удобен для разработки: доступны PHP CLI, docker-php-ext-install и pecl. Composer и Xdebug в базовый образ PHP не входят. Добавьте их сами с помощью COPY --from=composer:2 /usr/bin/composer /usr/local/bin/composer и pecl install xdebug, как это делает таргет dev в руководстве по Docker.
Продакшен-Dockerfile
Официальный образ OxPHP сохраняет PHP CLI и инструменты для расширений (php, phpize, pecl, docker-php-ext-install) образа php:<minor>-zts-alpine3.23, на котором он построен, поэтому расширения можно устанавливать прямо в нём; см. Установка PHP-расширений в продакшене. Если же собирать их в отдельной стадии, пакеты, нужные только для сборки, такие как icu-dev, не попадут в итоговый образ. Выбор между двумя вариантами сборки ниже зависит от того, нужны ли вам дополнительные PHP-расширения.
Если вашему приложению нужны дополнительные расширения (pdo_mysql, intl и т. д.), соберите их в отдельной стадии и скопируйте в итоговый образ:
# Extension build stage
FROM php:8.5-zts-alpine3.23 AS extensions
RUN apk add --no-cache icu-dev postgresql-dev \
&& docker-php-ext-install pdo pdo_mysql pdo_pgsql intl
# Production
FROM ghcr.io/oxphp/oxphp:0.13.0
# Runtime dependencies for extensions
USER root
RUN apk add --no-cache icu-libs libpq
# Copy compiled extensions
COPY --from=extensions /usr/local/lib/php/extensions/no-debug-zts-20250925/*.so /usr/local/lib/php/extensions/no-debug-zts-20250925/
# Enable extensions
RUN { \
echo "extension=pdo.so"; \
echo "extension=pdo_mysql.so"; \
echo "extension=pdo_pgsql.so"; \
echo "extension=intl.so"; \
} > /usr/local/etc/php/conf.d/app-extensions.ini
USER www-data
COPY --chown=www-data:www-data . /var/www/html/publicЕсли вашему приложению не нужны дополнительные расширения, достаточно этого:
FROM ghcr.io/oxphp/oxphp:0.13.0
COPY --chown=www-data:www-data . /var/www/html/publicСоберите и запустите:
docker build -t my-app .
docker run -p 80:80 my-appПо умолчанию сервер слушает порт 80. Корневой каталог документов — /var/www/html/public, и приведённые выше сниппеты копируют проект прямо в него. Для Laravel, Symfony и других фреймворков, которые уже поставляются с подкаталогом public/, используйте вместо этого COPY --chown=www-data:www-data . /var/www/html, чтобы собственный каталог public/ фреймворка совпал со значением по умолчанию. Если ваша структура отличается ещё сильнее, переопределите корневой каталог документов переменной окружения DOCUMENT_ROOT.
Сборка из исходников (без PHP)
Соберите OxPHP из исходников с отключённой поддержкой PHP, чтобы обслуживать только статические файлы:
cargo build --release --no-default-featuresБинарный файл находится в target/release/oxphp. Он использует исполнитель-заглушку, который возвращает ответ-заглушку на PHP-запросы, при этом статические файлы обслуживаются как обычно. Этот режим полезен для тестирования сервера без установленной среды выполнения PHP.
Сборка из исходников (с PHP)
Для сборки OxPHP с полной поддержкой PHP сначала нужно скомпилировать и установить библиотеку-мост и PHP-расширение.
Предварительные требования
- Инструментарий Rust (1.91.1 или новее)
- PHP 8.4 или 8.5 с включённым ZTS (Zend Thread Safety)
- Компилятор C (gcc или clang)
phpizeи заголовочные файлы для разработки PHP
Шаги сборки
-
Соберите и установите библиотеку-мост.
cd ext/bridge make && sudo make install -
Соберите и установите PHP-расширение.
cd ../ phpize && ./configure --enable-oxphp-sapi && make && sudo make install -
Соберите OxPHP. Набор возможностей по умолчанию включает php.
cargo build --release
Во время выполнения бинарному файлу нужны разделяемые библиотеки в пути поиска библиотек:
export LD_LIBRARY_PATH=/usr/local/lib
./target/release/oxphpПри развёртывании на Alpine Linux выполняйте сборку внутри того же образа php:{8.4,8.5}-zts-alpine, который используется в качестве среды выполнения PHP, — согласуйте минорную версию с образом OxPHP, который вы поставляете. Смешивание сборок под glibc и musl приводит к ошибкам во время выполнения. Официальный Docker-образ обрабатывает это корректно.
Проверка установки
После запуска OxPHP структурированный вывод логов в формате JSON подтверждает, что сервер работает:
{"timestamp":"...","level":"INFO","message":"OxPHP HTTP server starting","listen_addr":"0.0.0.0:80",...}
{"timestamp":"...","level":"INFO","message":"Server listening","addr":"0.0.0.0:80"}Проверьте, что сервер отвечает:
curl http://localhost/Если вы включили внутренний сервер через INTERNAL_ADDR, проверьте эндпоинт проверки работоспособности:
curl http://localhost:9090/healthИсправный сервер возвращает 200 со статусом в формате JSON. Сервер с деградацией возвращает 503.
Что дальше
- Быстрый старт — создайте проект, запустите OxPHP с помощью Docker Compose и выполните первый запрос
- Руководство по Docker — Dockerfile для разработки и продакшена, настройка Compose и монтирование томов
- Конфигурация — полный справочник по переменным окружения