Skip to content
← All packages

fpm-tune

beta

Sizes PHP-FPM pools against the memory a machine actually has, using the memory its workers actually use. It measures what each pool costs, divides the budget, and writes it back. Beta: it writes production configuration.

A server running many sites has many pools competing for one pool of RAM. Setting pm.max_children by hand means either leaving capacity unused or finding the ceiling through an OOM kill. fpm-tune measures what each pool's workers really cost, divides the budget accordingly, and writes it back. It runs standalone: its own discovery, its own measurement, its own budget detection, its own metrics.

Beta, and it writes production config

The tags are prereleases, and it changes pm.* settings on a live host and reloads the master. Start with plan, which writes nothing, or serve --recommend, which watches and writes its conclusion to a file nothing loads. A daemon without --apply changes nothing and never will.

Three commands

plan shows what it would change and why, and writes nothing. apply writes the settings and reloads once. serve keeps measuring and publishes metrics, touching nothing until you add --apply. Watching without acting is a first-class way to run it rather than a step on the way to something else, and --recommend gives that daemon somewhere to put its conclusion: PHP-FPM configuration you can read, diff and paste, rewritten only when the recommended settings actually change, so its modification time answers when the advice last moved rather than whether the daemon is up.

Every pool comes with the evidence for its number: the peak workers busy, the measured memory per worker, and the median, p95, p99 and worst readings behind it. One number cannot answer the question on its own.

What Makes The Measurement Honest

Never Learns From An Idle Pool

A quiet pool's workers give memory back to the operating system, so they read small. That is a lull, not a cheaper application. A pool must be serving at least a request a second before a smaller reading counts as evidence. Below that threshold a wrong answer wastes memory; above it a wrong answer loses the host.

Adjusts Rather Than Pins

The estimate rises quickly when a pool gets more expensive and falls on a half-life measured in time, not in samples. Sizing to a percentile of the day pins the host to its busiest hour, and a per-sample decay lets the scrape interval silently change the behaviour.

Time Only Counts If It Was Watched

Elapsed time is the weight, which is right while the looking is regular and wrong the moment it stops. Each pool remembers how often it is actually looked at, so a gap after a restart can never move the estimate further than one ordinary scrape.

Follows The Peak Of The Sawtooth

max_requests recycles workers, so memory climbs and resets. The peak is the number that has to fit, and PHP-FPM resets its own high-water marks on reload, so the peak is remembered here instead.

Charges For Spawned Children

A worker that shells out to ffmpeg or imagemagick starts a separate process with memory the worker's RSS does not include. The budget is charged for it anyway, so sizing on worker memory alone prices the child at zero and overcommits the host the moment the pool gets busy.

Measures Small Pools Too

A pool that never runs two workers at once, or recycles them before they warm up, was invisible to a stricter version of this and got a table's guess for ever. Its readings now count toward what it costs, though still not toward permission to shrink it.

Built to fail safe

It touches only pm.* keys, in one file of its own inside the directory your master already includes. Your pool config is never edited, and deleting that file returns everything to what you configured. The change set is one atomic rename, so a growth and the reduction that funds it reach the host together or not at all, and it is validated against a sandboxed copy first so a configuration php-fpm would reject never reaches the directory it globs, not even for the length of a fork.

What is about to be written is recorded first, with a phase, so an interrupted run is finished or undone on the next start, and a rollback is rehearsed before it is performed. The backup directory is not scratch space: it holds the previous file and a note of where php-fpm lives, which is how the tool repairs a host whose master will not start. That is the one situation where nothing can be discovered, because there is nothing running to discover. A tmpfiles rule that cleans that directory takes away both the undo and the repair.