Laravel: Docker + Cron Jobs

When we are using Docker for running our Laravel project, we must take into account that just one php-fpm process is running and we don’t have access to some crontab and we definetely don’t want to make use of the host for running a PHP process.

We need to understand that when we are making use of Docker, the project will run on a container that will start the php-fpm process. So the container is just going to run this, and not any other processes as queue workers or schedulers. This is good since it let us handle the different functionalities separately and work with them independently.

But how?

Let’s take a look by a simple example that prints a simple message into our Log files:

Step 1: Setting up the service

On out docker-compose.yml file, add a new service:

...

  #Scheduler
  scheduler:
    image: laravel-api
    command: ./bin/run-scheduler.sh
    container_name: scheduler_laravel_api
    restart: unless-stopped
    working_dir: /var/www
    volumes:
      - ./:/var/www
      - ./php/local.ini:/usr/local/etc/php/conf.d/local.ini
    networks:
      - app-network

...

#Network
networks:
  app-network:
    driver: bridge

...

We name the service as scheduler_laravel_api and the command will be implemented on a new file run-scheduler.sh on a new root folder bin. Now, we implement the run-scheduler.sh file:

Step 2: Writing the shell command

Create a new file named run-scheduler.sh inside the bin folder. If the folder doesn’t exist, create it:

#!/usr/bin/env bash

while [ true ]
do
  php /var/www/artisan schedule:run --verbose --no-interaction &
  sleep 60
done

This is basically an infinite loop that sleeps for every 60 seconds and execute the schedule:run command, This will run the Laravel’s tasks and allow us to execute them, al least, every 60 seconds. In other words, a Cron Job.

Step 3: Writing the Laravel’s new Cron Job

In order to do this, we need to create the new Command from the terminal. We name it as Scheduledtask for demo purposes.

Since we are working with containers, we must execute the following line for creating out Scheduledtask command:

docker-compose exec app php artisan make:command ScheduledTask

And we also execute composer dumop-autoload to make sure our new changes are applied:

docker-compose exec app composer dump-autoload

A brand new file ScheduledTask is created under app/Console/Commands. We can replace the contents with the following:

namespace App\Console\Commands;

use Illuminate\Console\Command;

use Illuminate\Support\Facades\Log;

class ScheduledTask extends Command
{
    /**
     * The name and signature of the console command.
     *
     * @var string
     */
    protected $signature = 'scheduled:task';

    /**
     * The console command description.
     *
     * @var string
     */
    protected $description = 'Scheduler Task Test';

    /**
     * Create a new command instance.
     *
     * @return void
     */
    public function __construct()
    {
        parent::__construct();
    }

    /**
     * Execute the console command.
     *
     * @return mixed
     */
    public function handle()
    {
        return Log::info('This is a brand new Scheduled Task');
    }
}

There’s not so much to tell about the contents. The signature variable indicated how the command must be referenced; and inside the handle() method we implement the code to execute periodically. In this case, a simple message «This is a brand new Scheduled Task» will be printed into our log file every time the job is executed.

Step 4: Executing the job periodically

Finally, we just need to tell Laravel to execute our command as often as we want. To do so, we must edit our app/Console/Kernel.php file.

At the $commands array, we need to add our ScheduledTask class:

protected $commands = [
    Commands\ScheduledTask::class
];

And at the schedule method we specify how often the command must be executed. In this case, we want it to be executed every minute:

protected function schedule(Schedule $schedule)
{
    $schedule->command('scheduled:task')->everyMinute();
}

The whole file should look like this:

namespace App\Console;

use Illuminate\Console\Scheduling\Schedule;
use Illuminate\Foundation\Console\Kernel as ConsoleKernel;

class Kernel extends ConsoleKernel
{
    /**
     * The Artisan commands provided by your application.
     *
     * @var array
     */
    protected $commands = [
        Commands\ScheduledTask::class
    ];

    /**
     * Define the application's command schedule.
     *
     * @param  \Illuminate\Console\Scheduling\Schedule  $schedule
     * @return void
     */
    protected function schedule(Schedule $schedule)
    {
        $schedule->command('scheduled:task')->everyMinute();
    }

    /**
     * Register the commands for the application.
     *
     * @return void
     */
    protected function commands()
    {
        $this->load(__DIR__.'/Commands');

        require base_path('routes/console.php');
    }
}

And that’s all!

Now, at every minute the message «This is a brand new Scheduled Task» should be printed on the log file.

This is a very simple example where we just want to print some message, but we can do whatever we want such as database operations, notifications sendings, and so on.

Reference: https://dev.to/joselfonseca/docker-y-laravel-2nk3