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.
Parameter | Value |
Connection Driver | Redis (disabled) |
Number of Processes | 1 |
Timeout (Seconds) | 60 |
Sleep Time (Seconds) | 3 |
Queue | default |
Maximum Tries | 0 |
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!"