Quick Start
The official OxPHP image bundles the server, PHP, the OxPHP extension, and every runtime dependency, so you can go from an empty directory to a running app in a handful of commands.
One command
If you already have a PHP project with a public/ directory:
docker run -p 80:80 -v .:/var/www/html ghcr.io/oxphp/oxphp:0.10.0Open http://localhost/. Your application is running.
To enable the internal server (health, metrics, config):
docker run -p 80:80 -p 9090:9090 -e INTERNAL_ADDR=0.0.0.0:9090 -v .:/var/www/html ghcr.io/oxphp/oxphp:0.10.0Step-by-step setup with Docker Compose
A more detailed walkthrough, from an empty directory to a working PHP application with health checks and structured logging.
-
Create the project directory.
mkdir my-oxphp-app && cd my-oxphp-app -
Create a Dockerfile.
FROM ghcr.io/oxphp/oxphp:0.10.0 COPY --chown=www-data:www-data . /var/www/htmlThe official image includes the server binary, PHP 8.4 or 8.5 ZTS (8.5 by default; pull 8.4 with
:0.10.0-php8.4or any*-php8.4*tag), the OxPHP PHP extension, and all runtime dependencies. -
Add a compose.yaml.
services: oxphp: build: . ports: - "80:80" - "9090:9090" environment: - LISTEN_ADDR=0.0.0.0:80 - DOCUMENT_ROOT=/var/www/html/public - INTERNAL_ADDR=0.0.0.0:9090 - LOG_LEVEL=info - ACCESS_LOG=allPort
80serves your application. Port9090exposes the internal server for health checks, Prometheus metrics, and the active configuration snapshot. -
Create a PHP application.
mkdir -p publicCreate
public/index.php:<?php $requestId = oxphp_request_id(); $info = oxphp_server_info(); echo "<h1>OxPHP</h1>\n"; echo "<p>Request ID: {$requestId}</p>\n"; echo "<p>Worker: {$info['worker_id']}</p>\n"; echo "<p>SAPI: " . php_sapi_name() . "</p>\n"; echo "<p>Version: {$info['version']}</p>\n"; echo "<p>Time: " . date('c') . "</p>\n";oxphp_request_id()returns the unique ID assigned to each request.oxphp_server_info()returns details about the running server includingversion,worker_id,request_time, andworker_mode. -
Build and start.
docker compose up -d --build -
Test your application.
curl http://localhost/Expected output:
<h1>OxPHP</h1> <p>Request ID: 67a4b3c11a2b00000001</p> <p>Worker: 0</p> <p>SAPI: cli-server</p> <p>Version: 0.10.0</p> <p>Time: 2026-03-23T12:00:00+00:00</p>Each request gets a unique ID. The worker ID shows which PHP worker thread handled it.
-
Check the internal endpoints.
# Health check — 200 when healthy, 503 when degraded curl http://localhost:9090/health # Prometheus-compatible metrics curl http://localhost:9090/metrics # Active configuration (TLS paths redacted) curl http://localhost:9090/config -
View the logs.
docker compose logs -f oxphpBecause
ACCESS_LOG=allis set, every request appears as a structured JSON log line with method, path, status, response time, and request ID.
If your application needs custom PHP extensions (pdo_pgsql, intl, xdebug, etc.), see
examples/dockerfile/Dockerfile
in the repository. It's a ready-to-use multi-stage Dockerfile with separate dev and prod targets.
OxPHP deliberately registers under one of the SAPI names OPcache recognises. OPcache disables itself
for unknown SAPIs; without this rename PHP execution would skip the OPcache layer entirely and run
several times slower. The trade-off is that php_sapi_name() cannot be used to detect OxPHP — use
function_exists('oxphp_request_id') or OxPHP\Http\Request::current() instead.
What's next
- Docker Guide — development and production Dockerfiles, Compose configuration, PHP ini mounts, and health check setup
- Configuration — full environment variable reference
- Routing — Traditional, Framework, SPA, and Worker routing modes
- Worker Mode — persistent PHP processes that bootstrap once and handle multiple requests
- PHP Functions — all OxPHP built-in PHP functions