How a WebSocket server turned into a worker pool
18 June 2026
The Phlo Daemon exists because of WebSockets, and stopped being about WebSockets within a day.
The first version was a persistent loop whose job was to keep a PHP app booted so a socket message could be handled without paying the boot cost each time. Writing it made something obvious: nothing in that loop was actually about sockets. It was a process that boots an app once and then dispatches any target at it. Sockets were one caller.
So it became phlo_serve(): boot once, then read newline-delimited JSON off stdin and dispatch whatever target arrives. Within two days the same pool was carrying synchronous requests, async requests, await() fan-out and streamed output, the WebSocket server had moved inside it rather than talking to it over HTTP, and the separate socket entrypoint was deleted.
What that buys an app is warm workers. A request that would spend most of its time booting the framework spends it on the work instead, and a background job you fire with phlo_async() lands in the same pool rather than paying for a fresh PHP process.
The pool is deliberately dull about fairness: every app has its own queue, a freed slot wakes all of them rather than only the pool that released it, and the order rotates so a busy app cannot keep taking the slot it just gave back.
One binary, one protocol, and the realtime layer became a feature of it rather than the reason for it.
Engine 1.0.1. See the Daemon chapter.