It’s recommended practice to set up a Redis queue when working with Laravel and Statamic sites. These queues are the virtual equivalent of a real-world queue. A queue is a list of jobs for the program that need to be executed, especially tasks that might take too long to perform during a regular web request.
When you run a queue on your website, Statamic will automatically add certain jobs to it, like committing to your Git repository, clearing the static cache, or warming pages for that cache. My Starter Kit, Peak, has a feature that allows clients to automatically generate Open Graph (OG) images, which are used when sharing pages on social media. Generating an OG image is something you don’t want to wait for, so it should be done in the background. For this, in Statamic, I’ve always relied on a simple single-queue worker. This article covers how to properly configure Laravel Horizon to handle multiple queues when using Statamic and Peak.
Laravel Horizon
At one point, I needed to generate 50+ OG images all at once. This would take about an hour, and during that time, other important jobs—like clearing the static cache or committing website changes to Git—would be delayed. I set out to create multiple queues. Michael Aerni, a beloved Statamic community member, helped configure Laravel Horizon to accomplish this. Thank you, Michael!
Laravel Horizon is a tool for configuring and monitoring single or multiple queues. You run one simple daemon (a background process) on your server, and Horizon handles the rest. If you’re looking to move from single queue workers to managed queues using Laravel Horizon, there are a couple of things you need to know and configure.
Installing Horizon
Install Laravel Horizon by running the following commands:
1composer require laravel/horizon2php artisan horizon:install
The first command installs Laravel Horizon using Composer, and the second command publishes the Horizon assets and config file.
Next, you’ll want to start a daemon. This is a server-level configuration in your Forge or Ploi-managed server. However, Ploi offers a button to generate one in the Laravel tab of your site.
1php /home/directory/to/your/site/artisan horizon
This daemon is a background process that keeps Horizon running. Whenever you redeploy, you need to restart Horizon. You can do this by adding the following to your deploy script:
1php artisan horizon:terminate
If you were previously using a queue worker, you can remove php artisan queue:restart
from your deploy script and delete your queue worker. It’s no longer needed since Horizon now manages your queues.
When using Horizon with Statamic, by default, you can only access the Horizon dashboard when logged in as a Super User. This is a good default setting. Statamic even automatically shows a link to https://yoursite.com/horizon
in the Control Panel toolbar when logged in as a Super User.
Configuring Horizon
Statamic features an environment variable to assign static cache warm jobs to a separate queue. Peak does something similar for generating OG images. Add the following variables to your .env
file:
1SOCIAL_IMAGE_QUEUE_NAME=social-images2STATAMIC_STATIC_WARM_QUEUE=static-cache
This tells the Peak SEO add-on to use a queue called social-images
for OG jobs and Statamic to use static-cache
for warm requests. Other jobs will automatically be added to the default queue.
Horizon doesn’t yet know how to handle these multiple queues or how many resources to allocate to them. Properly configuring Horizon is crucial. Supervisors manage queued jobs. You can create multiple supervisors to handle different queues. In config/horizon.php
, you’ll see I created three supervisors. The first one is the default and handles all jobs on the default queue. I haven’t made any configuration changes here.
The second and third supervisors handle warming the static cache and generating OG images.
1/* 2|-------------------------------------------------------------------------- 3| Queue Worker Configuration 4|-------------------------------------------------------------------------- 5| 6| Here you may define the queue worker settings used by your application 7| in all environments. These supervisors and settings handle all your 8| queued jobs and will be provisioned by Horizon during deployment. 9|10*/11 12'defaults' => [13 'supervisor-1' => [14 // Contains the default configuration.15 ],16 17 'supervisor-2' => [18 'connection' => 'redis',19 'queue' => ['static-cache'],20 'balance' => 'auto',21 'autoScalingStrategy' => 'time',22 'maxProcesses' => 3,23 'maxTime' => 3600, // How long a worker should run.24 'maxJobs' => 5, // Amount of jobs before the worker restarts.25 'memory' => 128,26 'tries' => 1,27 'timeout' => 120, // Max length of one process.28 'nice' => 0,29 ],30 31 'supervisor-3' => [32 'connection' => 'redis',33 'queue' => ['social-images'],34 'balance' => 'auto',35 'autoScalingStrategy' => 'time',36 'maxProcesses' => 1,37 'maxTime' => 3600, // How long a worker should run.38 'maxJobs' => 5, // Amount of jobs before the worker restarts.39 'memory' => 512, // Up the memory a bit.40 'tries' => 3, // Tries.41 'timeout' => 120, // Max length of one process.42 'nice' => 0,43 ],44],45 46'environments' => [47 'production' => [48 'supervisor-1' => [49 'maxProcesses' => 6, // Grant 6 simultanious process to supervisor-1.50 'balanceMaxShift' => 1, // A maximum of one new process will be generated every:51 'balanceCooldown' => 3, // three seconds.52 ],53 ],54 55 'local' => [56 'supervisor-1' => [57 'maxProcesses' => 3,58 ],59 ],60],
As you can see, the configuration is fairly verbose. We now have a default queue that can spawn six processes at once. I’ve noticed that warming the static cache and generating social images can be quite intensive on the server, so I limit those queues to three and one processes, respectively. This means that generating 50 OG images all at once can take up to an hour, which is completely reasonable.
Preventing redundant jobs
Both Statamic and Peak are configured to ensure these jobs are unique. This means that if you trigger warm or OG requests for a single entry multiple times, the previous jobs will be purged from the queue to prevent redundancy. However, when you redeploy, the Laravel cache—and therefore the unique IDs—get cleared. To prevent redundancy in your static cache warming queue, you can add the following to your deploy script:
1php artisan horizon:clear --queue=static-cache --force
This will clear the static-cache
queue before re-triggering a full warm of the static cache. The --force
flag ensures that we don’t encounter an interactive prompt on production environments, which could cause a failed deployment.
Depending on your needs and the type of server you’re running, you might want to tweak this configuration. All the various options are explained in the Horizon and Queue docs on the Laravel website.
Finally, here’s how our Horizon dashboard looks with jobs running on multiple queues:
Error handling
By default, Laravel logs errors in the database. Statamic doesn’t use a database by default, so this can cause issues and fill up your laravel.log
. Since Laravel 11 and Statamic 5, the default database is an SQLite database. To ensure error handling is properly logged in your SQLite database, make sure your .env
is configured as follows:
1DB_CONNECTION=sqlite2# DB_HOST=127.0.0.13# DB_PORT=33064# DB_DATABASE=laravel5# DB_USERNAME=root6# DB_PASSWORD=
If no SQLite database is present in your project, create it in your project root by running:
1touch database/database.sqlite
Lastly, make sure to migrate the default database structure by adding this to your deploy script:
1php artisan migrate --force
The --force
flag again ensures that we don’t encounter an interactive prompt on production environments, preventing failed deployments.
And that’s it! You now have a fully configured queue system for Statamic and Peak using Laravel Horizon.