Running Laravel Horizon in a multi-server environment with Laravel Forge and Envoyer

This post is more than a year old and may be obsolete. It was published September 27, 2021.

I just set up Laravel Horizon to manage our queue in a multi-server environment. The documentation is a little thin in this area but the ultimate solution is pretty simple.

Our environment

We have a load balancer, two web servers, a database server, and a queue server. The database server runs Redis and the web servers and queue server are all configured to connect to it. We only want to process queues on our dedicated queue server - not on our web servers.

Installing Horizon

Follow the documentation to get Horizon dependencies and config into your project.

app/Providers/HorizonServiceProvider.php includes some helpful features like notifications and gating access on production. I used our auth()->user()->isSuperAdmin() method and you'll probably have something similar in your project.

I did not need to make any changes.

Running Horizon

If you are running just one server, this is pretty trivial. This is where things got uncertain for me, but it's not very complicated!


On your queue worker, add a daemon to run artisan horizon. If your site name is the default 'app-worker', this command will be

1php /home/forge/app-worker/current/artisan horizon

This will ensure that Horizon is always working on that queue server.

On your web servers, add nothing. You will not run any jobs on your web servers, which is how I prefer to run things.


Add the following deploy hook:

1cd {{release}}
3php artisan horizon:purge
4php artisan horizon:terminate

this should only run on your queue server so ensure you just have that server checked in the On Server section. Drag this deploy hook after Activate New Release.

This deploy hook will force Horizon to reload and pick up any recent code changes. Since you set up a daemon previously, that process will be killed and then restarted automatically.

Testing it out

Run a fresh deploy at Envoyer, then visit to verify that the admin panel is running properly.

This blog is an experiment: less about education and more about documenting the oddities I've experienced while building web apps.

My hope is that you're here because of a random search and you're experiencing something I've had to deal with.

Recent Posts

View all