Recipes

April 30, 2026 · View on GitHub

Copy-paste shortcuts for the most common reli workflows. Each recipe links out to the full reference doc when you want to understand the options.

If a recipe assumes the Docker wrapper (reli ...), the equivalent on a native checkout is ./reli .... See getting-started.md for installation.

Profile a php-fpm pool for 60 seconds

Capture all worker processes at once, write a single .rbt per worker, and stop after a minute:

mkdir -p ./traces

# Attach to every worker matching the regex for ~60s.
# The daemon writes one .rbt per *reli worker* (-T, default 8) into
# ./traces/, named worker_<reli-worker-pid>.rbt — not one per target PID.
timeout 60 reli inspector:daemon \
    -P "^php-fpm" \
    -f rbt -o ./traces/

# List what was written, then browse / report on one of them
ls ./traces/
# worker_12345.rbt  worker_12346.rbt  ...
reli rbt:explore ./traces/worker_12345.rbt
reli rbt:analyze < ./traces/worker_12345.rbt

References: tracing/capturing-traces.md, tracing/rbt-analyze-and-explore.md.

Investigate memory growth in a worker

Dump now (short stop), analyse offline, look at the prioritised report:

# 1. Take a fast binary dump (target stopped only briefly)
reli inspector:memory:dump --pid=<pid> --output=worker.rdump

# 2. Analyse the dump into a .rmem snapshot
reli inspector:memory:analyze worker.rdump -f rmem -o worker.rmem

# 3. Read the prioritised findings (dominant classes, cycles, choke points, …)
reli inspector:memory:report worker.rmem

# 4. (optional) Drop into the TUI to chase paths interactively
reli rmem:explore worker.rmem

To check whether a suspected leak is actually growing, capture a second snapshot after letting the workload run, then diff:

reli inspector:memory:compare worker.rmem worker-later.rmem

References: memory/memory-dump.md, memory/memory-report.md, memory/rmem-explore-and-serve.md.

Capture memory_limit crashes with the sidecar

Run the sidecar as a separate process / container, register one line in your application bootstrap, and reli will dump from outside at the exact moment the application reports it's dying:

# Start the sidecar (foreground; wrap in systemd / supervisor / a
# Kubernetes sidecar container in real deployments).
# The sidecar refuses to bind unless the socket's parent directory
# is mode 0700 and owned by the current uid. On systemd hosts the
# default $XDG_RUNTIME_DIR/reli/sidecar.sock already satisfies this,
# so --socket can be omitted entirely.
reli inspector:sidecar --output-dir=/tmp/reli-dumps
# → [sidecar] Listening on /run/user/<uid>/reli/sidecar.sock

# If $XDG_RUNTIME_DIR is unavailable (root shells outside a user
# session, minimal containers), prepare a 0700 dir you own and pass
# --socket explicitly — see docs/monitoring/sidecar.md.

Install the FFI-free client package in your application (the standalone client mirror; downgraded to PHP 7.0+ so it runs anywhere your application does):

composer require "reliforp/reli-prof-sidecar-client:^0.12"

Other install paths (full reliforp/reli-prof or vendoring the three client files) are documented in monitoring/sidecar.md § Client Library; the standalone package is the recommended one.

// In your application bootstrap (index.php / bin/console / …)
\Reli\Sidecar\Client\MemoryLimitHandler::register();

When the application hits memory_limit, you get a .dump file plus a .meta.json with the call trace and memory stats in --output-dir.

Analyse the dump like any other reli memory snapshot:

reli inspector:memory:analyze /tmp/reli-dumps/sidecar-<pid>-<datetime>.dump \
    -f rmem -o crash.rmem
reli inspector:memory:report crash.rmem

References: monitoring/sidecar.md (including client install, Docker / Kubernetes setup, and CI cross-release comparison).

Profile FrankenPHP workers

FrankenPHP loads PHP as libphp.so and runs many threads inside one process — only the PHP worker threads carry executor globals. Three flags do the work:

# Daemon mode: discover workers automatically, skip Caddy / Go threads
reli inspector:daemon \
    -P frankenphp \
    --target-thread-regex='^php-[0-9a-f]+$' \
    --php-regex='.*/libphp\.so$' \
    --libpthread-regex='.*/libc\.so.*' \
    -f rbt -o ./traces/

For attaching to a single worker (inspector:trace), pass the PHP worker TID rather than the FrankenPHP parent PID. The pattern below intentionally excludes php-main — that's the bootstrap thread, not a worker, and attaching to it fails (the exact message varies by FrankenPHP / PHP build; current builds typically print _tsrm_ls_cache slot is uninitialized, older ones failed to find ZendMM main chunk). See tracing/frankenphp.md for the full story.

ps -L -p "$(pgrep -o frankenphp)" -o tid,comm | awk '\$2 ~ /^php-[0-9a-f]+$/'
#   12345 php-1abc34de
#   12346 php-2bcd45ef
# (FrankenPHP names worker threads with a hex suffix; the regex above also
#  excludes `php-main`, the bootstrap thread.)

reli inspector:trace -p 12345 \
    --php-regex='.*/libphp\.so$' \
    --libpthread-regex='.*/libc\.so.*' \
    -o trace.rbt

Reference: tracing/frankenphp.md.