Long-running PHP workers

PHP isn't only for websites — a while-loop worker runs fine on the PHP CLI application. Here's a verified worker with graceful shutdown, and exactly how restart, stop, and crash behave on Falix.

PHP has a reputation as a request-and-response web language, but a plain while (true) loop makes a perfectly good long-running worker: a queue processor, a poller, a scheduled-tasks daemon. The PHP CLI application runs exactly this kind of script. This guide shows a worker that shuts down cleanly when Falix stops it, and explains what restart, stop, and crash actually do here.

At a glance
You need A Falix server running the PHP CLI application (or PHP Composer if you have dependencies)
Plan Any — on free it runs while your session timer has time left, premium runs 24/7
Time Fifteen minutes
New to PHP here? PHP on Falix

Which PHP application?

A worker needs PHP CLI (runs php index.php and nothing else) or, if your code has Composer dependencies, PHP Composer (runs composer install first, then your file). Not the PHP Web Server — that's for websites served by nginx. A worker makes no inbound connections, so it needs no port at all. See PHP on Falix for the full comparison.

A worker that stops gracefully

Here's a complete index.php. It loops forever doing work, and — this is the important part — traps the signal Falix sends when you press Stop, so it can finish the current tick instead of being killed mid-job:

<?php
// Deliver signals as soon as they arrive, even during sleep().
pcntl_async_signals(true);

$running = true;
$shutdown = function (int $signo) use (&$running) {
    echo "received signal $signo, finishing up..." . PHP_EOL;
    $running = false;
};
pcntl_signal(SIGTERM, $shutdown);
pcntl_signal(SIGINT, $shutdown);

echo "worker started (pid " . getmypid() . ")" . PHP_EOL;

while ($running) {
    // ... do one unit of work here ...
    echo "tick at " . date("H:i:s") . PHP_EOL;

    sleep(5); // signal-friendly: a stop signal interrupts it instantly
}

echo "clean exit" . PHP_EOL;

How it works:

  • pcntl_async_signals(true) tells PHP to deliver signals the moment they arrive, so you don't have to sprinkle pcntl_signal_dispatch() through your loop.
  • pcntl_signal(SIGTERM, …) and SIGINT register your shutdown handler. Falix's Stop button sends a termination signal; trapping both covers the common cases.
  • The handler just flips $running to false. The loop finishes its current tick, falls out cleanly, and the process exits with status 0 — a graceful shutdown, not a hard kill.
  • sleep() is signal-friendly: an incoming signal interrupts it immediately, so Stop feels instant even mid-sleep.

🎯 Good to know: The pcntl and posix extensions ship with the PHP CLI image, so pcntl_signal and friends are available out of the box — no install needed.

What keeps the server "running"

A Falix app server is "running" for as long as your process is alive. A while (true) worker never returns, so the server stays up. The flip side matters just as much:

⚠️ Heads up: A PHP script that reaches the end and returns exits cleanly, and the panel then shows the server as stopped. That's not a crash — it's a finished program. If your worker is meant to run forever, make sure the loop condition really keeps it alive.

Restart, stop, and crash on Falix

Event What happens What your code should do
You press Stop / Restart Falix signals your process to shut down Trap SIGTERM/SIGINT (above) to finish the current job first
A fatal error (uncaught exception, out of memory) The process dies and the server shows stopped Read the Console — the error prints there; the first one is usually the cause
Out-of-memory kill Container killed, exit code 137 Free leaked memory; see Out of memory

Two ways to recover automatically from a crash: premium's Crash Detection (auto-restart, on the Settings page), or a Schedule with a server crashedStart trigger. Either one turns a 3 AM crash into a few seconds of downtime instead of hours.

Keeping a worker healthy over time

Long-lived PHP processes don't get the "fresh start every request" that a web app enjoys, so a couple of habits pay off:

  • Watch memory. PHP holds onto whatever you keep referenced. unset() large arrays when you're done, avoid growing a variable forever, and don't load an entire table into memory when you can page through it. A leaky worker eventually hits the 137 kill above.
  • A nightly restart is cheap insurance. A Schedule that restarts the worker once a day clears any slow creep. Because your worker shuts down gracefully, that restart is clean.
  • Log so you can see it working. echo goes straight to the Console. If lines ever seem to lag, call flush() after important output to push it out immediately.

Everything past the Falix-shaped parts is standard PHP — the official manual at php.net documents pcntl, signals, and CLI behaviour in full.


Next steps

Was this guide helpful?