Forge & Ploi
Forge & Ploi
Forge and Ploi boxes are the case fpm-tune was built for. A handful of sites, each
with its own PHP-FPM pool, all sharing one Ubuntu VM, and MySQL (and often Redis)
sitting on that same box, eating into the RAM php-fpm thinks it owns. There's no
container limit holding php-fpm back, so a bad pm.max_children on one busy site can
take the whole machine down with it.
Here's the whole flow, start to finish. It's the same on both. They lay php-fpm out
the same way (php8.x-fpm, a pool file per site under pool.d/).
1. Install it
SSH in as your deploy user and grab the binary:
curl -fsSL https://raw.githubusercontent.com/cboxdk/fpm-tune/main/install.sh | sh
Everything below wants root, because it reads php-fpm's config and (later) reloads
the master, so run the commands with sudo.
2. Turn on the status page
Try a plan first:
sudo fpm-tune plan
On a fresh Forge or Ploi box this will tell you it found your pools but has nothing
to measure. The stock pool files ship with pm.status_path commented out, and
that's the page fpm-tune reads to learn what a worker costs. Turn it on:
sudo fpm-tune enable-status
This doesn't touch your pool files. It writes a separate zz-fpm-tune-status.conf
drop-in, validates the whole config with php-fpm -t before anything moves, reloads
gracefully, and rolls back if the master doesn't come back. Now plan again:
sudo fpm-tune plan
This time you get the real thing: per-pool worker cost and what it'd set each
pm.max_children to. Changes nothing. Read it. Does the ranking match what you
know about these sites? The shop being the heavy one, the docs site being cheap?
3. Let it watch for a while
Don't jump to apply. Install it as a background service. It starts in advisory mode, so it watches and recommends but writes nothing:
sudo fpm-tune install-service
Then watch it think:
journalctl -u fpm-tune -f
Leave that for a day, or a week. It logs a heartbeat so you can see it's alive, and a fresh line whenever its recommendation for a pool actually moves. What you're waiting to see is that its numbers settle and stop surprising you: a real traffic peak, a deploy, a quiet Sunday, all folded in. That's the beta talking: trust it on your workload before you let it act.
4. Let it act
Once the recommendations look right, flip the switch:
sudo fpm-tune mode apply
Same service, no reinstall. It just restarts in apply mode. From here it writes each
pool's pm.max_children and reloads php-fpm gracefully when the right size drifts far
enough from the current one to be worth a reload. Back to watch-only any time:
sudo fpm-tune mode advisory
5. The MySQL-on-the-box catch
This is the part that bites on a shared Forge/Ploi box. php-fpm doesn't have the machine to itself. MySQL might be holding 3-4 GB, Redis another few hundred meg. If fpm-tune sized pools against all the RAM, it'd hand php-fpm memory MySQL is already using, and the OOM killer would settle the argument.
It doesn't. On a bare VM it reads /proc/meminfo and leaves what the other services
are actually using out of php-fpm's budget, on top of a headroom margin, so the
whole host stays under target, not just php-fpm. You'll see it in the plan:
budget: 7.6 GiB total
4.2 GiB used by other services
512 MiB headroom kept
That's it being a good neighbour on its own. But "what MySQL is using right now" isn't a promise about next Tuesday. If you want a hard wall (MySQL can never be starved by php-fpm, full stop), cap php-fpm's memory at the OS level so the kernel enforces it no matter what fpm-tune thinks:
sudo systemctl set-property php8.4-fpm.service MemoryMax=4G
(Match the version to your box: php8.3-fpm, php8.4-fpm, whatever Forge/Ploi
installed.) That takes effect live, no restart, and survives reboots. fpm-tune then
sizes pools inside that ceiling, and you've got a guarantee instead of a courtesy.
The reasoning behind both is in the budget.
When it says the host is full
If a pool is queueing and fpm-tune can't fix it (no free budget, nothing to borrow from an idle neighbour), it tells you the host is out of capacity and stops. That isn't a bug to tune around. On a Forge/Ploi box it usually means one honest question: is it time for a bigger droplet, or does a heavy site need to move off? See alerting for the signal to watch.