The Queen supervisor is a local control plane. It starts ordinary Laravel
queue:work queen children, assigns them to queues, changes their number and drains them during
pause, scale-down and shutdown. The Queen broker never starts PHP processes.
Both engines consume the same resolved configuration:
| Engine | Master | Workers | Distinct behavior |
|---|---|---|---|
| Queen PHP | A Laravel application remains resident | artisan queue:work queen |
Reference implementation and fallback |
| Queen Rust | Rust remains resident after one Artisan config load | artisan queue:work queen |
Lower master memory, process-group fencing and crash-loop circuit breaker |
Switching engines does not change the pool configuration, CLI controls or dashboard protocol.
Requirements
The supervisor engines currently target Unix process semantics.
- PHP 8.3 or newer.
symfony/processfor the PHP engine.- PHP
pcntlandposixfor process control, state ownership and the Composer launcher. - PHP
pharand an availableproc_openfor native installation and smoke verification. - An outer process monitor such as systemd, Kubernetes or Supervisor to restart the master if it exits.
The queue driver can run without these additions when another process manager owns fixed workers. Native Windows is not supported by either Queen supervisor today.
Prepare private state
Status, the local ownership lock, control requests and bounded worker telemetry share one private directory. Create it as the same user that will run the supervisor:
install -d -m 0700 /srv/example/queen-supervisor-stateQUEEN_SUPERVISOR_STATE_DIRECTORY=/srv/example/queen-supervisor-stateConfigure a pool
Publish config/queen.php, then edit supervisor.supervisors. This example gives one pool a total
budget of 20 workers across two queues:
'supervisor' => [
'poll_interval' => 3,
'shutdown_grace' => 75,
'process_limit' => 20,
'state_directory' => env(
'QUEEN_SUPERVISOR_STATE_DIRECTORY',
storage_path('queen-supervisor'),
),
'supervisors' => [
'jobs' => [
'connection' => 'queen',
'consumer_group' => 'laravel',
'queues' => ['high', 'default'],
'balance' => 'auto',
'strategy' => 'time',
'min_processes' => 2,
'max_processes' => 20,
'target_clear_seconds' => 60,
'default_runtime_seconds' => 1,
'balance_cooldown' => 3,
'balance_max_shift' => 2,
'scale_down_delay' => 10,
'timeout' => 60,
'retry_after' => 90,
'tries' => 3,
'memory' => 128,
],
],
],The important inequalities are enforced at startup:
retry_aftermust be longer thantimeout;shutdown_gracemust be longer than every pool’stimeout;max_processesmust cover every queue inautomode;- aggregate pool maxima must fit
process_limit; a renewal-enabled worker reserves two child process slots, one for Artisan and one for its lazy helper; - control TTL and heartbeat timeout must exceed the bounded reconciliation loop.
Inspect the document that either engine will consume:
php artisan queen:supervisor-config --prettyThis form redacts tokens and header values. --for-engine contains credentials and must not be
written to logs or ordinary build artifacts.
Balancing modes
| Mode | Worker allocation | Use it for |
|---|---|---|
auto |
Changes the total target and assigns workers by queue pressure | Elastic queues with no strict priority |
simple |
Keeps processes fixed and spreads them evenly |
Predictable capacity |
off |
Gives every worker the ordered comma-separated queue list | Strict Laravel queue priority |
strategy=size uses group-specific effective backlog divided by
target_jobs_per_process. strategy=time multiplies backlog by observed worker runtime and aims
for target_clear_seconds. Before telemetry exists, the time strategy uses
default_runtime_seconds.
balance_max_shift and balance_cooldown limit how quickly the pool changes. A lower target must
remain stable for scale_down_delay before Queen drains capacity. Draining workers continue to
count against process_limit, including their reserved renewal helper, so a reallocation cannot
create a temporary process spike.
Run the PHP engine
The PHP engine is available directly from the Composer package:
php artisan queen:superviseIt uses the same capped exponential restart backoff as Rust. After five consecutive failures it
opens the pool circuit and, after restart_backoff_max, admits one probe until that worker is
stable.
Use the PHP engine first when native artifacts are not published for the host, or as a reference during a Rust rollout.
Run the Rust engine
The Composer package includes a launcher, not a binary for every platform. Installation is an explicit deployment step, so Composer never downloads and executes native code by itself:
php artisan queen:supervisor-install
vendor/bin/queen-supervisor --php php --artisan artisanThe installer selects the exact package-pinned version and host target, verifies the release manifest, archive SHA-256, executable version and local receipt, then publishes by atomic rename. The launcher verifies the receipt and binary hash again before every start.
Prepare an application-owned installation leaf. Its immediate parent must already be a real directory, not a symlink, and an existing leaf must not be group- or world-writable:
install -d -m 0755 /srv/example/queen-supervisor-binQUEEN_SUPERVISOR_INSTALL_PATH=/srv/example/queen-supervisor-binThis avoids depending on a deployment where Laravel’s storage/ path is a symlink. The binary
leaf is separate from the private 0700 runtime-state directory.
| Target | Status |
|---|---|
| Linux amd64 | Static musl pipeline prepared; native release qualification pending |
| Linux arm64 | Static musl pipeline prepared; native release qualification pending |
| macOS amd64 and arm64 | Native preview; signing, notarization and full E2E qualification pending |
| Windows x64 and arm64 | Unsupported; native process and locking backends are not implemented |
For an offline or controlled mirror, the installer accepts a local manifest/archive pair or an HTTPS base URL. High-assurance deployments can also pin the SHA-256 of a manifest already verified through the release’s Sigstore bundle. The default installer does not independently establish Sigstore trust.
Inspect and control either engine
The CLI uses the state directory, so the commands are identical for PHP and Rust:
php artisan queen:supervisor status
php artisan queen:supervisor status --json
php artisan queen:supervisor status --check
php artisan queen:supervisor status --check-capacity
php artisan queen:supervisor status --check-liveness
php artisan queen:supervisor pause
php artisan queen:supervisor continue
php artisan queen:supervisor terminatestatus --check exits non-zero unless the current generation is live and every pool is ready: its
depth is current and it has non-zero capacity whenever capacity is desired. A pool with surviving
workers stays ready while a replacement is in backoff or probe; that degraded circuit remains
visible in pool health. status --check-liveness checks only the master owner and heartbeat, which
keeps liveness separate from serving readiness. Per-pool capacity_satisfied exposes ordinary
scale-up lag without making readiness flap during a healthy rebalance. Controls are fenced to the
exact instance_id; a stale command cannot apply to a replacement master.
Use status --check-capacity for a stricter processing-health gate that requires
running >= desired and a healthy restart circuit for every pool. This check is intentionally
separate because it may be false during a normal elastic scale-up or replacement while minimum
serving readiness remains healthy.
Readiness is not an application SLO. Monitor oldest-job age, completion rate, failures and DLQ growth separately. The child-process budget also excludes the master, container init and arbitrary subprocesses created by job code, so leave explicit headroom in the systemd/container PID limit.
Pause drains current workers and starts no replacements until continue. It does not suspend a PHP
process while that process retains a prefetched tail. Terminate drains for shutdown_grace, then
forces the remaining process group down.
Production checks
Before placing a Queen supervisor in production:
- Keep
prefetch=1andack_batch=1unless the workload is short, idempotent and measured. - Enable
lease_renewalwheneverprefetch > 1or job runtime cannot fit safely inside the original lease. - Use a shared cache store with distributed-lock support when synchronized failed jobs can be mutated by multiple processes or hosts.
- Monitor
queen:supervisor status --checkand the outer process manager. - Send
terminateduring deployment so the restarted master loads the new code and configuration. - Keep workers idempotent. Queen provides at-least-once delivery, not exactly-once external side effects.
- Exercise graceful and forced shutdown with the application’s longest real job before rollout.
The singleton limit applies to the master, not to its worker count. Multiple Queen broker endpoints provide broker failover for depth reads; they do not provide active-active supervisor leadership.
Next, enable the local dashboard or follow the Horizon migration runbook.