Fifty workers all waiting on the same upstream is a site that is down because of a service most of its pages do not use.
pm.max_children = 50
without bulkheads:
one slow dependency at 20s → the whole pool at 2.5 req/s
with a concurrency cap per dependency:
payments: max 10 concurrent → 40 workers still free
search: max 10 concurrent
analytics: max 5 concurrent
the cap is the bulkhead. the timeout is not.
A timeout bounds how long one request waits and says nothing about how many wait simultaneously, which is the arithmetic people miss: workers divided by upstream latency is the maximum request rate. A semaphore in Redis with an expiry is a workable cap in PHP and is cruder than the thread pools other platforms use for this. Rejecting immediately when the cap is reached is the point — a queue behind the bulkhead is the pool again with extra steps.