Skip to content

Applications ​

An <application> is a runnable unit of your project — typically a PHP web app served by FrankenPHP. Every project has at least one application inside the required <applications> element. Each application becomes its own service (and generated Dockerfile.{app}) in the Docker Compose file.

<application> ​

AttributeRequiredDefaultDescription
nameYes—Unique application name. See the identifier rules below.
typeYes—Runtime image. One of php:8.2, php:8.3, php:8.4, php:8.5.
rootNo/Path within the repository that holds this application's source. Defaults to the repo root.

Name pattern

name must match [a-zA-Z][a-zA-Z0-9 _-]* (1–64 chars) and be unique within the project. The name is slugified to derive container names, so My App becomes my-app.

Supported PHP versions ​

typePHP version
php:8.2PHP 8.2
php:8.3PHP 8.3
php:8.4PHP 8.4
php:8.5PHP 8.5

Child elements ​

ElementRequiredDescription
<web>YesHTTP locations served by this app. See Web.
<extensions>NoPHP extensions to install. See below.
<php>NoPHP ini settings, e.g. memory_limit. See PHP Settings.
<workerMode>NoEnable FrankenPHP worker mode. See below.
<workers>NoBackground worker containers. See Workers.
<crons>NoScheduled commands. See Crons.
<hooks>NoLifecycle scripts. See Hooks.
<mounts>NoPersistent volumes. See Mounts.
<relationships>NoConnections to backing services. See Relationships.

A minimal application:

xml
<application name="app" type="php:8.5" root="/">
    <web>
        <location path="/" root="public" index="index.php" passthru="/index.php"/>
    </web>
</application>

<extensions> ​

PHP extensions to compile into the application image. Each <extension> is installed with docker-php-ext-install; any required system packages (for example libicu-dev for intl) are resolved automatically during the build.

<extension> ​

AttributeRequiredDefaultDescription
nameYes—Extension name as accepted by docker-php-ext-install, e.g. intl, pdo_pgsql.
xml
<extensions>
    <extension name="gettext"/>
    <extension name="intl"/>
    <extension name="pcntl"/>
    <extension name="pdo_pgsql"/>
    <extension name="sockets"/>
    <extension name="curl"/>
</extensions>

TIP

If you connect to a PostgreSQL service, add pdo_pgsql. For MySQL/MariaDB add pdo_mysql. The extension list is reflected in the generated Dockerfile.{app}.

Classic vs. worker mode ​

FrankenPHP can serve an application in two modes, and Tragwerk supports both:

  • Classic mode — the default. When an application has no <workerMode> element, FrankenPHP handles each request in the traditional per-request lifecycle. It is a drop-in replacement for PHP-FPM: existing PHP apps run unchanged, and no worker-safe code is required. The generated Caddyfile emits a plain frankenphp directive.
  • Worker mode — opt-in. Adding <workerMode> boots the front controller once and keeps it warm between requests for higher throughput. It requires worker-safe application code. The generated Caddyfile emits frankenphp { worker … }.

Which mode should I use?

Start in classic mode — omit <workerMode> and your app runs exactly like it would under PHP-FPM, with no code changes. Reach for worker mode later, as an optional performance upgrade, once your application is worker-safe.

<workerMode> ​

<workerMode> is optional. Omit it and the application runs in classic mode (the PHP-FPM drop-in described above). Add it to run the application in FrankenPHP worker mode: the front controller is bootstrapped once and kept warm between requests, instead of the default per-request bootstrap. This dramatically reduces per-request overhead for framework-based apps, but requires worker-safe application code.

AttributeRequiredDefaultDescription
countNoauto (~2× cores)Number of worker threads (positive integer). Set explicitly on shared hosts to avoid oversubscription.
max-requestsNo0Restart each worker after this many requests (non-negative integer). 0 means unlimited.
xml
<workerMode count="4" max-requests="1000"/>

When max-requests is greater than 0, Tragwerk injects a MAX_REQUESTS environment variable into the container, and the worker script restarts each worker after that many requests — a guard against memory leaks in long-running processes.

Worker mode requires a passthru

Worker mode derives its worker script from the <web> location that declares a passthru. You must have exactly one <location> with a non-null passthru (for example passthru="/index.php"). See Web.

<health> ​

<health> is optional. It controls how long a freshly started container may take before the deployment declares it dead.

AttributeRequiredDefaultDescription
start-periodNo30sGrace period after start during which failing probes do not count. A number plus ms, s, m or h.
retriesNo12Consecutive failed probes after the grace period before the container counts as unhealthy (1–1000).

Probes run every 5 seconds, so the total budget is start-period plus retries × 5s — 90 seconds by default.

xml
<health start-period="300s" retries="60"/>

When you need this

deploy hooks run in the entrypoint before the web server starts listening, so the container stays unhealthy for the whole duration of the hook. A hook that imports a large dataset or runs a long migration will blow the default 90-second budget, and the deployment tears the container down mid-hook and rolls back. Either widen the budget here, or move the work into a post_deploy hook so it runs in the background once traffic is flowing.

  • Web — the required <web> block and the passthru rule
  • Workers — long-lived background processes
  • Crons — scheduled commands
  • Hooks — build/deploy/post-deploy scripts
  • Mounts — persistent storage
  • Relationships — connecting to services
  • Examples

Tragwerk — self-hosted PHP application hosting.