Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Horizon Laravel Package

blomstra/horizon

Integrates Laravel Horizon with Flarum so administrators can monitor Redis queues, jobs, and workers from the admin panel. Offers a convenient in-app view of queue health and failed jobs without switching to a separate Horizon dashboard.

View on GitHub
Deep Wiki
Context7

Getting Started

blomstra/horizon wires Laravel Horizon — the Redis queue dashboard and supervisor — into Flarum, exposing it as an admin-only page. It is thin glue: a service provider, an auth bridge (Horizon's default gate only allows local), a route/asset mount for the Vue SPA, and (depending on version) a horizon console wrapper. You can read the entire surface in extend.php and src/ in ten minutes.

Prerequisites (non-negotiable):

  • PHP ext-redis, ext-pcntl, ext-posix — Horizon will not boot without pcntl/posix, and many shared hosts block them.
  • A reachable Redis server. Horizon supports only the Redis queue driver; sync or database queues produce an empty dashboard.
  • Flarum's queue pointed at Redis in the site's config.php.

Install:

composer require blomstra/horizon
php flarum extension:enable blomstra-horizon
php flarum cache:clear
php flarum assets:publish

Where to look first

  1. config.php — set queue.driver to redis (Flarum's default is sync, which is why first-time users see nothing).
  2. vendor/blomstra/horizon/extend.php — see which routes, gate and commands it registers before you debug anything.
  3. Admin → Horizon — Jobs / Completed / Failed / Metrics tabs.

First use case: flip the queue to Redis, then trigger a job you know exists (e.g. a reply that sends an email notification, or an extension job like image processing). Watch it appear under Completed, then watch it appear under Failed after you throw an exception in it. That round trip validates the whole chain — Redis, worker, auth, dashboard — in five minutes. Metrics graphs stay empty until horizon:snapshot has run at least a couple of times (see Gotchas).

A minimal config.php queue/redis block, roughly:

'queue' => [
    'driver'     => 'redis',
    'connection' => 'default',
    'queue'      => 'default',
],
'redis' => [
    'host'     => '127.0.0.1',
    'port'     => 6379,
    'database' => 3,        // dedicated DB for queues, not the cache DB
],

Exact key names depend on your Flarum version — validate with php flarum info and confirm jobs land in Redis (redis-cli -n 3 keys 'queues:*').

Implementation Patterns

Run Horizon as the worker process. Horizon replaces queue:work entirely — it starts its own supervisors from horizon.php. Under systemd/supervisor:

[program:flarum-horizon]
command=php /var/www/forum/flarum horizon
directory=/var/www/forum
autostart=true
autorestart=true
user=www-data
stopwaitsecs=3600

(php flarum horizon if the extension registers the wrapper; otherwise php /var/www/forum/vendor/bin/horizon.) Delete any existing queue:work unit in the same deploy — two competing worker pools is a classic self-inflicted incident.

Deploy hook: Horizon workers are long-lived and hold old code in memory. Every deploy needs a graceful recycle:

php flarum cache:clear
php flarum migrate --force
php flarum horizon:terminate   # workers finish current job, then restart

Tag your jobs so the dashboard is searchable. Horizon reads a tags() method off the job class; without it the Completed/Failed search is nearly useless in a forum where every job looks like SendNotificationJob.

use Illuminate\Contracts\Queue\ShouldQueue;

class SendDigest implements ShouldQueue
{
    public function tags(): array
    {
        return ['digest', 'user:'.$this->user->id];
    }
}

Eloquent-backed jobs get tags for free if the model implements Illuminate\Queue\SerializesModels and you follow the QueueableEntity convention; the manual tags() above covers everything else.

Shape supervisors around Flarum's two very different workloads. Mail/notification jobs are slow and external-bound; cache/maintenance jobs are fast. Two supervisors in horizon.php beat one:

'environments' => [
    'production' => [
        'default'       => ['connection' => 'default', 'queue' => ['default'],
                            'balance' => 'auto', 'processes' => 4, 'tries' => 3,
                            'timeout' => 60, 'maxJobs' => 500, 'memory' => 128],
        'notifications' => ['connection' => 'default', 'queue' => ['notifications', 'mail'],
                            'balance' => 'simple', 'processes' => 2, 'tries' => 5,
                            'timeout' => 120, 'maxJobs' => 250, 'memory' => 192],
    ],
],

Day-to-day monitoring workflow: Metrics tab → is wait time climbing (under-provisioned) or flat with low throughput (Redis/network bottleneck)? Failed tab → read the exception, use Retry after a fix. Completed tab → search by tag (user:1234) to trace one user's jobs. Pending → backlog during a mail storm. Use Pause on a supervisor before a risky migration, Continue afterwards, Purge to clear a broken queue.

Scheduling the snapshot. Horizon's metrics come from horizon:snapshot, which must run every ~5 minutes. Flarum schedules through php flarum schedule:run in cron; check whether the extension registered it (php flarum schedule:list or inspect the container's scheduler). If not, add it yourself in a tiny extension or system cron:

*/5 * * * * cd /var/www/forum && php flarum horizon:snapshot >> /dev/null 2>&1

Extension point for non-admin access. Horizon gates access through Horizon::auth(). If you need a support role (not full admin) to see the dashboard, register your own gate in a small extension's service provider rather than patching the package — the extension already relies on that same hook.

Gotchas and Tips

  • Empty dashboard = wrong queue driver. Verify queue.driver is redis and that the connection name in queue.connection matches a Redis block. sync executes jobs inline and never touches Redis, so Horizon has literally nothing to show.
  • Redis used for cache and queues is the #1 footgun. If the queue shares a DB with the cache and maxmemory-policy is allkeys-lru, Redis will silently evict queued jobs. Give queues their own database (or instance) with noeviction.
  • 403s with a blank-ish page. Horizon's default gate is local-only; the extension bridges it to Flarum admins. If the SPA loads but its /horizon/api/* calls return 403, you're either not an admin or another extension re-registered the gate. Check the network tab — this is a two-minute diagnosis, don't reinstall first.
  • Blank white panel on subdirectory installs. Horizon's SPA assumes it owns the root path. Installs under /forum and reverse proxies that strip prefixes routinely break asset and API URLs. Same failure class: stale compiled assets — run php flarum assets:publish and hard-reload before filing a bug.
  • Metrics graphs empty for the first ~10 minutes. Not a bug: no snapshots yet. See the cron above.
  • horizon:terminate is not optional. Workers keep the old code after a deploy. If a "fixed" job still fails with the old stack trace, you forgot to terminate.
  • Timeout mismatch. Horizon's timeout must be lower than the job's $timeout and PHP's max_execution_time; otherwise jobs get MaxAttemptsExceededException while still genuinely running, then execute twice.
  • Memory creep. Flarum's boot is heavy and workers are long-lived. Keep maxJobs in the few hundreds and memory below memory_limit; a worker that dies silently looks like "jobs just stopped" in the dashboard.
  • pcntl/posix missing kills the command at startup with a cryptic message. php flarum horizon:status from the CLI is the fastest way to confirm the environment, not to debug via the web UI.
  • Version pinning is your friend. Horizon's major version tracks the Illuminate version Flarum ships. A stray composer update can pull a Horizon that conflicts with (or is untested against) your Flarum branch. Pin it in composer.json and re-verify in staging on every Flarum upgrade.
  • Failed-jobs table grows unbounded in the database. Schedule a periodic php flarum horizon:clear (or purge from the UI) so a bad week of mail jobs doesn't bloat the DB.
  • Maintenance risk: 2 stars, 0 dependents, last release May 2024. It's maintained by a reputable team but is effectively low-traffic glue. Treat it as pinned infrastructure, not a dependency you upgrade casually; if it breaks, reading extend.php will usually tell you whether to fork the 200-odd lines or drop it and expose stock Horizon behind a small custom Flarum provider with your own Horizon::auth() gate.
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
store-doc/laravel-storage-driver
plus-it/laravel-translation-manager
diephp/perhaps
diephp/telegraph
osmaviation/spreadsheet
microsoft/fluentui-system-icons
mmccook/php-json-canonicalization-scheme
middlewares/firewall
qubiqx/montapacking
nyoncode/laravel-package-console
pnscripts/filament-broadquill-lite
pnscripts/laravel-sentfold-lite
vleap/mx-sdk-laravel-internal
twidpay/utils
htetmyathlaing/scout-elasticsearch-driver
artisan-toolbox/front-awesome
ls/client-assistant
users-au/laravel-client
cgv/localized-resource
noith/settings