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:

bash
docker run -p 80:80 -v .:/var/www/html ghcr.io/oxphp/oxphp:0.13.0

Open http://localhost/. Your application is running.

To enable the internal server (health, metrics, config):

bash
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.13.0

Step-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.

  1. Create the project directory.

    bash
    mkdir my-oxphp-app && cd my-oxphp-app
  2. Create a Dockerfile.

    Dockerfile
    FROM ghcr.io/oxphp/oxphp:0.13.0 COPY --chown=www-data:www-data . /var/www/html

    The official image includes the server binary, PHP 8.4 or 8.5 ZTS (8.5 by default; pull 8.4 with :0.13.0-php8.4 or any *-php8.4* tag), the OxPHP PHP extension, and all runtime dependencies.

  3. Add a compose.yaml.

    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=all

    Port 80 serves your application. Port 9090 exposes the internal server for health checks, Prometheus metrics, and the active configuration snapshot.

  4. Create a PHP application.

    bash
    mkdir -p public

    Create public/index.php:

    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 including version, worker_id, request_time, worker_mode, and runtime_hooks.

  5. Build and start.

    bash
    docker compose up -d --build
  6. Test your application.

    bash
    curl http://localhost/

    Expected output:

    html
    <h1>OxPHP</h1> <p>Request ID: 67a4b3c11a2b00000001</p> <p>Worker: 0</p> <p>SAPI: cli-server</p> <p>Version: 0.13.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.

  7. Check the internal endpoints.

    bash
    # 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
  8. View the logs.

    bash
    docker compose logs -f oxphp

    Because ACCESS_LOG=all is set, every request appears as a structured JSON log line with method, path, status, response time, and request ID.

Tip

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.

Why does `php_sapi_name()` report `cli-server` instead of `oxphp`?

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.

Prompts for your agent

These prompts assume an agent with the OxPHP documentation MCP server connected. The agent reads the pages it needs itself.

prompt
Get my PHP application running on OxPHP. Read the `getting-started/quick-start` page through the OxPHP documentation MCP server. Work out from this repository which routing mode it needs and what its document root is, then give me the smallest configuration that serves it.

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 workers that bootstrap once and handle multiple requests
  • PHP Functions: all OxPHP built-in PHP functions
Found a mistake? Report it →