opcache & APCu

The opcache and APCu shares are derived from the box-wide RAM budget computed in FPM capacity sizing. This page covers how they are sized, what BOA writes into each phpNN.ini, the Valkey/Redis cache instance settings, and the read-only sampling tools (fpm_tune + fpmreport) that guide manual tuning.

opcache and APCu shares

After the RAM ladder sets _USE_PHP and _USE_FPM, the cache shares follow:

  • _USE_OPC tracks the budget tier from the RAM ladder (8192 / 4096 / 2048 / 1024 / _USE), overridden to 64 on _VMFAMILY=VZ.
  • _USE_APC = _USE_FPM — APCu is sized to the whole FPM-pool budget, not _USE_FPM / 2. APCu is a single shared memory segment for the entire pool, not a per-worker allocation; the old per-worker /2 formula caused >90% saturation on large servers with complex codebases.

APCu phpNN.ini settings

APCu is written to the FPM phpNN.ini only (_fix_php_ini_apcu via _apply_php_fpm_ini_fixes in lib/functions/php.sh.inc). Each version has two inis — the FPM one at /opt/phpNN/etc/phpNN.ini and the CLI one at /opt/phpNN/lib/php.ini — and _fix_php_ini_apcu runs against the FPM ini only; _apply_php_cli_ini_fixes never calls it, so APCu is absent from the CLI ini. It is also gated [ "${e}" -gt 56 ], so PHP 5.6 gets no APCu at all. (This FPM-only placement is the basis of the CLI-vs-FPM cache distinction covered in cache & cron tuning.) BOA writes:

extension=apcu.so
extension=igbinary.so
apc.serializer=igbinary
apc.shm_size=256M
apc.ttl=3600

apc.serializer=igbinary pairs with the explicitly-loaded igbinary.so for a more compact serialization than the PHP default. apc.ttl=3600 bounds entry lifetime so stale segments are not held indefinitely. apc.shm_size is the per-version APCu segment. The 256M written here is only the template seed: _tune_memory_limits rewrites every 256M in phpNN.ini to ${_USE_APC}M (sed "s/256M/${_USE_APC}M/g", system.sh.inc), so on any tuned box the effective apc.shm_size is the box-wide _USE_APC budget derived above, not a flat 256 MB.

APCu is per-worker-process state living inside the FPM workers. It cannot be flushed remotely — a stale entry only clears when the worker is recycled. The graceful all-versions reload mechanism for clearing it is covered in cache & cron tuning.

Valkey / Redis cache instance

Valkey/Redis cache instances are run as a cache, not a persistence store:

  • RDB snapshots disabled — save "".
  • Eviction policy maxmemory-policy volatile-lfu.

These are applied by _valkey_no_rdb in autoupboa and the matching redis.conf / valkey.conf paths in system.sh.inc. The memory ceiling (_MAX_MEM_VALKEY, default _RAM / 3 of usable RAM) and the password file (/root/.valkey.pass.txt, or /root/.redis.pass.txt for the Redis variant) are covered alongside the starvation diagnosis in cache & cron tuning. Component versions track _PHP_APCU (APCu 5.1.x) and _VALKEY_MAJOR_RELEASE (Valkey 9.x) — for pinned numbers see the Reference appendix. For Redis/Valkey host tunables see Host control files & INI reference.

Separately, autoupboa's _swap_purge_if_bloated purges swap (sync + drop_caches, then swapoff/swapon when free RAM can absorb it) on each run once swap usage reaches 50%. That 50% is a function-local default (_SWAP_THRESHOLD, the first positional argument), not a barracuda.cnf operator variable.

Adaptive sampling: fpm_tune + fpmreport

BOA samples live FPM behaviour to guide manual tuning. Both tools are strictly read-only — they write no phpNN.ini, trigger no FPM restart, and change no tuning.

fpm_tune.sh

fpm_tune.sh (aegir/tools/system/monitor/check/, launched from minute.sh) self-throttles to roughly one run per 5 minutes via /var/log/boa/fpm-tune/.last_run (--force overrides). Each run makes three passes:

  1. Version discovery + per-version worker USS sweep — taken before any status traffic, so the workers spawned to serve the sampler's own requests do not dilute the average.
  2. Per-pool pm.status over each pool socket.
  3. Per-version opcache/APCu probe via that version's wwwNN system-pool socket (the shared segments read the same data for any pool of that version).

It appends one JSONL record per pool and per version to /var/log/boa/fpm-tune/<date>.jsonl (_RETAIN_DAYS=30; older files pruned by a find … -mtime +30 -delete on each run).

fpmreport

fpmreport (aegir/tools/bin/fpmreport) aggregates that JSONL into per-version/per-pool findings: pool starvation, opcache/APCu growth or oversize, file-cap proximity, memory_limit-approach flags, and idle versions with reclaim estimates.

It also prints a per-pool configured / observed table for every non-idle pool — full header: pool, ver, plan, eng, class, cfg_kids, cfg_mem, peak, p95, mcr/day. cfg_kids and cfg_mem are the currently configured pm.max_children and memory_limit — read live from the pool conf, following the include so dedicated pools show their inherited box-wide memory_limit — next to the observed peak active children, p95 and max_children-hit rate. A pool configured at 128 but only ever driven to 34 reads as cfg_kids 128 … peak 34 rather than a bare inferred figure.

plan and eng are the instance's plan (_CLIENT_OPTION) and engines count (_CLIENT_CORES), read live by the cfg_plan() helper from /root/.<oN>.octopus.cnf — the instance name is the pool name minus its .NN version suffix (o8.84o8). /data/disk/<oN>/log/cores.txt overrides the cnf cores value when present (matching the sizing path), engines default to 1 when neither source yields a number, and plan renders - when the cnf is unreadable. They resolve the ambiguity of a bare cfg_kids: 16 could be the POWER shared tier (8 × engines × 2) or a dedicated plan's dynamic calc landing on 16 on a small box — with plan/eng beside it, the allocation is validated at a glance against the inputs the sizing logic keys on. The same values reach --json: each pool_summary record carries plan and engines alongside its cfg_max_children and cfg_memory_limit_mb fields.

fpmreport                       # this node, all retained data
fpmreport --days 7              # window relative to the newest record
fpmreport --json                # machine-readable, one finding per line
fpmreport --data DIR|FILE...    # incl. off-node corpora

bash fpm_tune.sh --force --stdout   # sample once, print + store records

These findings drive the manual capacity adjustments in FPM capacity sizing (the _PHP_FPM_* override knobs); the tools never apply changes themselves.

Related