Skip to content

Reference

Reference

For looking up a flag, a key or a path. Each entry is one line; the pages under How it decides and Operating it say why. Flags are shown as --flag; -flag is accepted too, and fpm-tune <command> -h prints the same list.

Commands

command what it does root
plan show what would change, and why; changes no configuration, records what it observed no
apply write the pool settings and reload php-fpm, once yes
enable-status turn on pm.status_path for pools that have none, and reload yes
serve keep measuring, with /metrics; --apply to act advisory no, --apply yes
install-service run serve under systemd, advisory by default yes
mode show or switch the service between advisory and apply switching yes
top watch the running service in the terminal no
apply-now ask the running service to apply its plan once yes
version print the version (--version, -v too) no
help print the command list (--help, -h too) no

plan, advisory serve and top run as the php-fpm user or root, because they read the pool configuration and the status pages. Positional arguments are refused on every command: Go's flag parsing stops at the first one and drops the flags after it. See Requirements.

Flags shared by plan, apply, enable-status and serve

flag default meaning
--state PATH /var/lib/fpm-tune/state.json the learned baselines
--memory SIZE detected override the memory limit: 8G, 8GB, 8Gi, 8GiB; units K, M, G, T, all binary
--reserve AMOUNT 15% memory held back from workers: a size (1G) or a percentage of the budget (20%)
--workload CLASS web default class for pools without a marker: web, bursty, subprocess-heavy; see the markers below
--sizing BASIS p95 per-worker cost basis: p95, p99, a bare percentile from 50 to 100, or peak
--cpu off hold a CPU-limited pool at its CPU ceiling; without it the CPU shape is measured and reported but sizes nothing
--cpu-headroom N 2 with --cpu, the factor on the workers that fill the CPU; 1 to 100, never below one per core plus one
--timeout DURATION 15s budget for scraping all pools
--verbose off log what is being read
--no-learn off do not record this scrape
--confidence-samples N 20 busy samples before a baseline is trusted enough to cut a pool
--confidence-span DURATION 30m time span of busy evidence before a baseline is trusted enough to cut a pool

--sizing p95 sizes on the 95th percentile with a 10% margin, floored by the most recent peak; peak sizes on the typical peak itself, which rises in one scrape and decays on a half-life, with the same floor. See Measuring workers.

plan

flag default meaning
--drop-in-dir DIR all masters plan for the master that includes this pool directory

Without --drop-in-dir, a host running several masters is planned as one, against one of their memory limits. When another fpm-tune holds the state lock, plan reports without recording; when the state directory is not writable, likewise, and says so.

apply

flag default meaning
--drop-in-dir DIR the directory the master includes where the pool settings are written; also selects the master on a host running several
--backup-dir DIR /var/lib/fpm-tune/backup the previous configuration while a change is in flight, the record of the change, and where php-fpm lives
--min-change FRACTION 0.15 smallest relative growth worth a reload; a shrink needs twice that
--min-interval DURATION 5m shortest time between reloads for growth; a shrink waits four times that
--dry-run off render and validate, write nothing, reload nothing

--no-learn is refused unless --dry-run is also given: applying has to record what it wrote, or the next run reloads the pool this one just reloaded. apply refuses to write from a budget it could not confirm; pass --memory. On a host running several masters, --drop-in-dir is required. See How it fails safe.

enable-status

flag default meaning
--drop-in-dir DIR the directory the master includes which master to act on
--status-path PATH /status the pm.status_path to set on pools that have none
--backup-dir DIR /var/lib/fpm-tune/backup where the previous drop-in is kept while the change is in flight
--dry-run off render and validate, write nothing, reload nothing

It writes zz-fpm-tune-status.conf, validates, reloads, and rolls back if the master does not come back. With every pool already exposing a page it prints Every pool on this master already exposes a status page and does nothing.

serve

flag default meaning
--apply off act on the plan; without it the loop observes, learns and publishes
--config PATH none load settings from this file; explicit flags override it
--interval DURATION 30s how often to sample the pools
--metrics ADDR :9110 address for /metrics, /healthz and /history.json; empty disables them
--control PATH control.sock beside the state file the unix socket apply-now asks on
--recommend PATH none write the plan as configuration to this path; nothing reads it
--heartbeat DURATION 30m re-log the current recommendation this often even when nothing changed; 0 disables
--history DURATION 24h how far back /history.json reaches, in memory
--save-every DURATION 5m how often the baselines reach disk
--drop-in-dir DIR the directory the master includes as for apply
--backup-dir DIR /var/lib/fpm-tune/backup as for apply
--min-change FRACTION 0.15 as for apply
--min-interval DURATION 5m as for apply

--apply --no-learn is refused, for the same reason as on apply. --recommend inside a directory a master includes is refused. A second daemon whose --metrics address is taken does not start.

install-service

flag default meaning
--apply off install in apply mode
--cpu off set cpu = true in the config; --cpu=false turns it off on a re-run
--metrics ADDR 127.0.0.1:9110 address for /metrics
--print off print the config and unit instead of installing them
--log-file PATH the journal append the log to this file through the unit, with a logrotate snippet; journal goes back; a re-run keeps what the unit has

A first run writes the whole config; a re-run keeps it and changes only the keys named. Both rewrite the unit and restart the service. See Running as a daemon.

mode

fpm-tune mode                  # print the current mode
sudo fpm-tune mode apply       # rewrite the mode line and restart the service
sudo fpm-tune mode advisory

No flags. Refused, with the fix, when /etc/fpm-tune/config does not exist.

top

flag default meaning
--addr HOST:PORT the installed service's metrics address, else 127.0.0.1:9110 where /history.json is served
--refresh DURATION 5s how often to fetch

The keys are on Applying once.

apply-now

flag default meaning
--control PATH /var/lib/fpm-tune/control.sock the daemon's control socket

The config file

/etc/fpm-tune/config is what the installed service reads (serve --config). One key = value per line; blank lines and lines starting with # or ; are ignored. Every serve flag name is a key (interval = 15s, reserve = 20%, cpu = true), and an unknown key is refused, so a typo stops the service rather than being ignored. A flag given on the command line overrides the file.

mode is the one key that is not a flag: advisory (the default) or apply. In advisory mode, when no recommend key is set, the recommendation is written to /var/lib/fpm-tune/recommended.conf. The file install-service writes carries mode and metrics as active lines and the other common keys as commented defaults:

mode = advisory

# Where /metrics is served. Empty disables it.
metrics = 127.0.0.1:9110

# heartbeat = 30m
# sizing = p95
# cpu = true
# cpu-headroom = 2
# drop-in-dir =
# history = 24h
# control = /var/lib/fpm-tune/control.sock
# recommend = /var/lib/fpm-tune/recommended.conf

fpm-tune mode and a re-run of install-service rewrite single lines and leave the rest, including your comments, as they were. After editing by hand: sudo systemctl restart fpm-tune.

Files and paths

path what it is
/usr/local/bin/fpm-tune the binary; install.sh falls back to ~/.local/bin, then ~/bin
/etc/fpm-tune/config the service settings
/etc/systemd/system/fpm-tune.service the unit install-service writes
/etc/logrotate.d/fpm-tune the logrotate snippet, when installed with --log-file
/var/lib/fpm-tune/state.json the learned baselines, and where php-fpm was last seen
/var/lib/fpm-tune/fpm-tune.lock the state lock; one process learns at a time
/var/lib/fpm-tune/control.sock the control socket, mode 0600, root's
/var/lib/fpm-tune/recommended.conf the recommendation file, advisory mode
/var/lib/fpm-tune/backup/ <id>-transaction.json (a change in flight), the previous drop-in it saved, and <id>-master.json (where php-fpm lives); not scratch space
/run/fpm-tune/<id>-apply.lock the pool-directory lock; one writer per directory (/tmp/fpm-tune-locks on a host without /run)
<pool directory>/zz-fpm-tune.conf the sizes it wrote
<pool directory>/zz-fpm-tune-status.conf the status pages it turned on
/metrics, /healthz, /history.json on the metrics address

<id> is the first eight hex digits of a SHA-256 of the pool directory, so two masters sharing a backup directory keep separate records. The pool directory is the one the master includes, /etc/php/8.4/fpm/pool.d on Debian and Ubuntu.

Pool markers

A pool declares its own class and headroom in its config, as env[...] lines that php-fpm accepts and fpm-tune reads:

[media]
env[FPM_TUNE_WORKLOAD] = subprocess-heavy
env[FPM_TUNE_CPU_HEADROOM] = 3

FPM_TUNE_WORKLOAD names the class, overriding --workload for this pool: web (aliases api, simple; spawns nothing, the default), bursty (a child now and then), subprocess-heavy (aliases subprocess, media, children; a child on most requests). Case does not matter. An unknown name is a warning in the plan, and nothing is reserved for that pool's children. Measurement refines the class once a baseline exists. See Spawned children.

FPM_TUNE_CPU_HEADROOM is the pool's own headroom factor, 1 to 100, overriding --cpu-headroom. A value that is not a number in that range is a warning in the plan and the host's headroom is used, so one pool's typo does not stop the host being planned. See CPU.

Exit status

0 on success, including -h, help and version. 1 on any error, printed to stderr as fpm-tune: ...: an unknown command, no command, a positional argument, a flag that does not parse, a refusal, or a failed run. apply exits 1 when the reload was signalled but the run ended before the master was seen to survive (the change stands; run apply again to confirm it), and when the change was applied but its record could not be saved. apply-now exits 1 when the daemon reports an error.