Configuring Laravel Horizon with Cloudways Supervisor Jobs

·

3 min read

While working with a client who hosts their Laravel applications on Cloudways, I encountered some limitations in the platform's control panel. There were certain features, such as different PHP versions for different applications, utilising Horizon for queue management with supervisor job that seemed inaccessible.

Identifying the Challenge

The control panel lacked the flexibility to modify supervisor commands directly. While it offers options to configure supervisor jobs, these settings are primarily designed for Laravel's default queue:work commands.

ParameterValue
Connection DriverRedis (disabled)
Number of Processes1
Timeout (Seconds)60
Sleep Time (Seconds)3
Queuedefault
Maximum Tries0
Environment (Optional)production
Artisan Path/application_name/public_html/artisan

Here's an example of the supervisor job configuration generated in /etc/supervisor/conf.d/application_1.conf

[program:application_name_1]                                                                                                                   
command=php /home/xxxxxxx.cloudwaysapps.com/application_name/public_html/artisan queue:work redis --sleep=3 --quiet --timeout=60 --env=production --queue="default" --tries=0                                                                                                           

process_name=%(program_name)s_%(process_num)02d                                                                                          
autostart=true                                                                                                                           
autorestart=true                                                                                                                         
stopasgroup=true                                                                                                                         
killasgroup=true                                                                                                                         
user=application_name                                                                                                                          
numprocs=1                                                                                                                               
stdout_logfile=/home/xxxxxxx.cloudwaysapps.com/application_name/logs/application_name_1.log

As seen, the queue:work redis command is hardcoded into the supervisor configuration file, If we intend to change it, we will need to contact the Cloudways helpdesk, as modifying this file requires root ownership.

This restricts users from customising supervisor configurations beyond the predefined commands. This limitation becomes particularly challenging when users need to utilise different commands or prefer to use Horizon instead of the default queue:work command.

Cloudways "Standard" Support Scope

When I reached out to Cloudways support for assistance, I was informed that the feature wasn't covered under my current "Standard" support plan. To change the command from queue:work to horizon, I would have to upgrade to at least the "Advanced" plan, which costs $100/month or 10% of your invoice.

Finding an Alternative Solution

Determined to find a workaround, I went back to the Supervisor setting page to see what options were available. Realising that I could change the Artisan path, I decided to create a new Artisan file dedicated solely to launching the Horizon service.

By examining the artisan code below:

/** @var App\Console\Kernel $kernel */ 
$kernel = $app->make(Illuminate\Contracts\Console\Kernel::class);

$status = $kernel->handle(
    $input = new Symfony\Component\Console\Input\ArgvInput,
    new Symfony\Component\Console\Output\ConsoleOutput
);

The output of the $input parameter using dd:

Symfony\Component\Console\Input\ArgvInput^ {#26
  -tokens: array:8 [
    0 => "queue:work"
    1 => "redis"
    2 => "--sleep=3"
    3 => "--quiet"
    4 => "--timeout=60"
    5 => "--env=production"
    6 => "--queue=default"
    7 => "--tries=0"
  ]
  -parsed: null
  #definition: Symfony\Component\Console\Input\InputDefinition^ {#25
    -arguments: []
    -requiredCount: 0
    -hasAnArrayArgument: false
    -hasOptional: false
    -options: []
    -shortcuts: []
  }
  #stream: null
  #options: []
  #arguments: []
  #interactive: true
}

Therefore, the solution is simply to replace the $input with the desired command.

Replace the line:

$input = new Symfony\Component\Console\Input\ArgvInput

With:

$input = new Symfony\Component\Console\Input\ArrayInput([
    'command' => 'horizon'
])

Then, on your Cloudways Supervisor settings page, change the Artisan Path to the file that you created earlier, i.e., public_html/horizon.

Finally, your final horizon file should look like this:

#!/usr/bin/env php
<?php

define('LARAVEL_START', microtime(true));

require __DIR__.'/vendor/autoload.php';

$app = require_once __DIR__.'/bootstrap/app.php';

$kernel = $app->make(Illuminate\Contracts\Console\Kernel::class);

$status = $kernel->handle(
    // $input = new Symfony\Component\Console\Input\ArgvInput,
    $input = new Symfony\Component\Console\Input\ArrayInput(['command' => 'horizon']),
    new Symfony\Component\Console\Output\ConsoleOutput
);

$kernel->terminate($input, $status);

exit($status);

I hope this solution proves helpful for others facing similar challenges. If you have any questions or need further assistance, feel free to reach out. Happy coding!"

Did you find this article valuable?

Support Chaiwei's Coding Journey by becoming a sponsor. Any amount is appreciated!