Skip to main content
An installation is not complete when the web server answers. Two background processes must run continuously, and their absence is invisible from the browser: the admin panel works, channels look connected, and no incoming message is ever answered. This document describes each service, why it exists, and how to supervise it.

The services

Horizon

Every inbound message, every outbound send and every flow execution runs in a queued job. Horizon supervises the workers that consume them. The queues are separated by purpose, and config/horizon.php groups them into supervisors by priority: Two things about this matter operationally: Do not consolidate the queues into one worker pool. A broadcast to fifty thousand contacts would then sit in front of a live conversation, and the person waiting for an answer would get one when the broadcast finishes. Horizon needs pcntl and posix. Without them it fails to start, and because the web tier is unaffected the installation looks healthy. Restart Horizon on every deploy. Workers are long-lived PHP processes holding the previous release in memory:
This lets in-flight jobs finish and exits; the supervisor restarts the process with the new code. Killing workers outright drops jobs that were mid-flight.

Scheduler

Laravel’s scheduler must be invoked once a minute. It drives scheduled triggers, flow log partition creation and cleanup tasks. Traditional cron:
Under systemd, a timer is preferable — it logs to the journal and does not depend on a mail-capable cron daemon:
Run exactly one scheduler across the whole installation. Two hosts both ticking means every scheduled trigger fires twice.

Horizon under systemd

Restart=always matters more than usual: Horizon terminates itself when a worker exceeds its memory limit, and expects a supervisor to bring it back.

Verifying

A queue depth that only grows is the signature of Horizon not running, or running without the queues configured.