Using Laravel Horizon outside of Production and Local cover image

Using Laravel Horizon outside of Production and Local

James Bannister • February 11, 2019 • 3 minute read

laravel

Laravel is pretty awesome as far as frameworks go. But what really makes it amazing is the suite of open-source software sitting behind it; one of which is Horizon.

Horizon is a dashboard and queue-worker configuration tool built to provide powerful insight and control over Redis queues.

Getting started is pretty easy, the documentation covers this in detail and there's a tonne of tutorials out there if you need further help getting started.

It doesn't take long and within 10 or 15 minutes, you have this powerful dashboard up and running and already processing some test jobs in your local environment.

You're happy with the installation and setup, and so you push the work you've done up to version control and then deploy it to your staging, pre-production, beta, whatever-you-call-it-befpre-production environment. You make a few actions to fire off a few queued jobs and then you check your Horizon dashboard and see this:

Horizon with no queue workers

Horizon is running, there are jobs queued, but there are not queue workers running.

You check your local machine again, as you swear it was working and you see:

Horizon with queue workers

Huh?! It's working in local, but not on your staging site...

Turns out this is covered in a closed issue reported in the Horizon repo.

The issue lies at the bottom of your config/horizon.php file, under the 'Queue Worker Configuration' comment.

Out-of-the-box, Horizon ships with the below queue worker configuration:

// config/horizon.php

'environments' => [
    'production' => [
        'supervisor-1' => [
            'connection' => 'redis',
            'queue' => ['default'],
            'balance' => 'simple',
            'processes' => 10,
            'tries' => 3,
        ],
    ],
    'local' => [
        'supervisor-1' => [
            'connection' => 'redis',
            'queue' => ['default'],
            'balance' => 'simple',
            'processes' => 3,
            'tries' => 3,
        ],
    ],
],

That is to say, Horizon ships with an environment setting for production and local, but nothing else. If the environment that you've deployed to is not listed here then Horizon is not able to boot up any queue workers or supervisors as it doesn't know what to load.

Knowing this, the solution becomes fairly simple; add an environment configuration for that which you have deployed to. In my case, it is staging:

// config/horizon.php

'environments' => [
    'production' => [
        'supervisor-1' => [
            'connection' => 'redis',
            'queue' => [
                env('QUEUE_DEFAULT', 'default'),
            ],
            'balance' => 'simple',
            'processes' => 10,
            'tries' => 3,
        ],
    ],
    'staging' => [
        'supervisor-1' => [
            'connection' => 'redis',
            'queue' => [
                env('QUEUE_DEFAULT', 'default'),
            ],
            'balance' => 'simple',
            'processes' => 5,
            'tries' => 3,
        ],
    ],
    'local' => [
        'supervisor-1' => [
            'connection' => 'redis',
            'queue' => [
                env('QUEUE_DEFAULT', 'default'),
            ],
            'balance' => 'simple',
            'processes' => 3,
            'tries' => 3,
        ],
    ],
],

You can see that I've added a configuration for staging, but those of you with keen eyes may also notice another change; I've updated the queue array in the configuration.

Credit for this goes to sebastiansulinski on the same Horizon issue. This prevents multiple instances of Horizon (occurs if you are running more than one Larvel application per server with Horizon running) competing for jobs from other Laravel applications.

By following this approach, and updating our config/queue.php file appropriately, we ensure that we are pushing our queued jobs to a queue specific to that Laravel instance we are running and that our Horizon instance is only completing jobs on that queue:

// config/queue.php

'connections' => [

    'redis' => [
        'driver' => 'redis',
        'connection' => 'default',
        'queue' => env('QUEUE_DEFAULT'),
        'retry_after' => 90,
    ],

],

'queues' => [
    'default' => env('QUEUE_DEFAULT'),
]

With both of these approaches implemented, you'll have Horizon running across all of your application environments and projects, as well as the ability for them all to run on the same server, with multiple instances of Horizon running.


This has been my second post on my new site. If you have any feedback, thoughts, or different approaches to this then I'd love to hear from you; I'm @jryd_13 on Twitter.

Thanks!