Contao periodically executes some tasks via its own cron functionality. These include mainly cleanup tasks such as
since 5.0 Starting with Contao 5 all cronjobs are registered as services and tagged using the contao.cronjob
tag. Thus you can find all
cronjobs on your system using the following command:
$ vendor/bin/contao-console debug:container --tag contao.cronjob
The aformentioned command can also be used in Contao 4.13. However, this will not find cronjobs that are registered
via the legacy config.php
(see below). Unfortunately there is no convenient way in Contao 4 to display registered
legacy cronjobs. If you want to look these up you could either search for any $GLOBALS['TL_CRON']
definitions in your
Contao instance via your IDE, or use Xdebug for example in order to inspect the $GLOBALS['TL_CRON']
array.
since 5.3 Starting also with Contao 5.3 you will find a special contao.cron.supervise_workers
cronjob. This cronjob will automatically
start worker processes for the asynchronous messaging feature. There is, however, a fallback in case you do not
configure a proper contao:cron
cronjob (see next section). Then all messages (from the default Contao Managed Edition message
queues) will be processed within kernel.terminate
of the web process.
By default the cron tasks are executed after a response is sent back to the visitor when a request to the Contao site has been made.
It is recommended to run PHP via PHP-FPM, otherwise cron execution and search indexing will block any subsequent request by the same user.
In Contao 4 you can disable the front end cron by going to System » Settings » Cron job settings and enabling the setting Disable the command scheduler. You can also force this setting via your application’s config:
# config/config.yaml
contao:
localconfig:
disableCron: true
After disabling the front end cron you should periodically let Contao execute its cron jobs, either via the command line (recommended) or by making a request to the web URL.
since 5.1 Starting with Contao 5.1 you cannot disable the front end cron. Instead Contao will detect whether you are letting the cron jobs be executed periodically and thus disable their execution in the front end automatically.
This feature is available in Contao 4.9 and later.
Executing the cron jobs via the command line is done via the contao:cron
command:
$ vendor/bin/contao-console contao:cron
This is also the recommended way of periodically executing Contao’s cron jobs. In a Linux crontab you could use the following instructions for example:
* * * * * /usr/bin/php /path/to/contao/vendor/bin/contao-console contao:cron
There is no HTTP request context available on the command line. However, Contao needs this to generate the sitemap for example. You can set the domain either in the settings of your website roots or you can define a default domain in your application configuration. See the Symfony Routing Documentation for more details.
# config/parameters.yaml
parameters:
router.request_context.host: 'example.org'
router.request_context.scheme: 'https'
since 5.0 You are also able to force the the execution of cron jobs via the --force
parameter:
$ vendor/bin/contao-console contao:cron --force
since 5.0 You can also execute just one specific cron job from the command line:
$ vendor/bin/contao-console contao:cron "App\Cron\ExampleCron"
The latter can also be combined with the --force
option.
In order to trigger the execution of cron jobs via a web URL, a request to the _contao/cron
route, e.g. https://example.org/_contao/cron
, needs to be made. In a Linux crontab
you could use the following instructions for example:
* * * * * wget -q -O /dev/null https://example.org/_contao/cron
Registering custom cron jobs is similar to registering to hooks. As of Contao 4.13, there are four different ways of registering a cron job. The recommended way is using PHP attributes. Which one you use depends on your setup. For example, if you still need to support PHP 7 you can use annotations. If you still develop cron jobs for Contao 4.4 then you still need to use the PHP array configuration.
Using attributes or annotations means it is only necessary to create one file for the respective adaptation when using Contao’s default
way of automatically registering services under the App\
namespace within the src/
folder.
Generally cron jobs can be registered through the contao.cronjob
service tag. The following options are supported for this service tag:
Option | Description |
---|---|
interval | Can be minutely , hourly , daily , weekly , monthly , yearly or a full CRON expression, like */5 * * * * . |
method | Will default to __invoke or onMinutely etc. when a named interval is used. Otherwise a method name has to be defined. |
since 4.13 Contao implements PHP attributes (available since PHP 8) with which you can tag your service to be registered as a cron job.
// src/Cron/ExampleCron.php
namespace App\Cron;
use Contao\CoreBundle\DependencyInjection\Attribute\AsCronJob;
#[AsCronJob('hourly')]
class ExampleCron
{
public function __invoke()
{
// Do something …
}
}
In this case the cron job is executed once per hour. As mentioned before this parameter can also be a full CRON expression, e.g.
*/5 * * * *
for “every 5 minutes”.
since 4.9 Contao also supports its own annotation formats via the Service Annotation Bundle.
// src/Cron/ExampleCron.php
namespace App\Cron;
use Contao\CoreBundle\ServiceAnnotation\CronJob;
/**
* @CronJob("hourly")
*/
class ExampleCron
{
public function __invoke()
{
// Do something …
}
}
In this case the cron job is executed once per hour. As mentioned before this parameter can also be a full CRON expression, e.g.
*/5 * * * *
for “every 5 minutes”.
If you need an interval like */5 * * * *
you need to escape either the *
or /
with \
, since */
would close the PHP comment.
since 4.9 As mentioned before you can manually add the contao.cronjob
service tag in your service configuration.
# config/services.yaml
services:
App\Cron\ExampleCron:
tags:
- { name: contao.cronjob, interval: hourly }
// src/Cron/ExampleCron.php
namespace App\Cron;
class ExampleCron
{
public function __invoke()
{
// Do something …
}
}
Only the interval
parameter is required. In this case the cron job is executed once per hour. As mentioned before this parameter can also
be a full CRON expression, e.g. */5 * * * *
for “every 5 minutes”.
This method is deprecated since Contao 4.13 and does not work in Contao 5 anymore.
You can register your own cron jobs using the $GLOBALS['TL_CRON']
arrays. It is
an associative array with the following keys, representing the available intervals:
minutely
hourly
daily
weekly
monthly
To register your own job, add another array item with the class and method
of your cron job to one of the intervals in your config.php
:
// contao/config/config.php
$GLOBALS['TL_CRON']['hourly'][] = [\App\Cron\ExampleCron::class, 'onHourly'];
// src/Cron/ExampleCron.php
namespace App\Cron;
class ExampleCron
{
public function onHourly(): void
{
// Do something …
}
}
In some cases a cron job might want to know in which “scope” it is executed in -
i.e. as part of a front end request or as part of the cron command on the command
line interface. The Cron
service will pass a scope parameter to the cron job’s
method.
namespace App\Cron;
use Contao\CoreBundle\Cron\Cron;
use Contao\CoreBundle\Exception\CronExecutionSkippedException;
class HourlyCron
{
public function __invoke(string $scope): void
{
// Skip this cron job in the web scope
if (Cron::SCOPE_WEB === $scope) {
throw new CronExecutionSkippedException();
}
// …
}
}
The above example uses the CronExecutionSkippedException
(available since Contao 4.9.38 and 5.0.8) which will tell Contao’s Cron
service that the excution of this cron job was skipped and thus the last run time will stay untouched in the database. Thus the cron job
will be executed again at the next opportunity, ensuring that its logic is always executed within the CLI scope in this case.
This feature is available in Contao 5.1 and later.
The cron job framework executes jobs synchronously in the order they were tagged (normal service priority tags). This means that if you e.g. have 10 cron jobs, and they all take 20 seconds to run, it will take the framework 200 seconds to complete. For most cron jobs, this is not a problem because they don’t usually run 20 seconds.
However, if you have cron jobs that trigger child processes or are asynchronous in any other way, you would want
them to start immediately in parallel without blocking the other cron jobs. You can do this by returning a
GuzzleHttp\Promise\PromiseInterface
:
namespace App\Cron;
use Contao\CoreBundle\Cron\Cron;
use Contao\CoreBundle\Exception\CronExecutionSkippedException;
use GuzzleHttp\Promise\Promise;
class HourlyCron
{
public function __invoke(string $scope): void
{
// Skip this cron job in the web scope
if (Cron::SCOPE_WEB === $scope) {
throw new CronExecutionSkippedException();
}
return new Promise(static function () use (&$promise): void {
// Do something that is asynchronous
$promise->resolve('Done with asynchronous process.');
});
}
}
Because most asynchronous processes are most likely things like a spawned child process using Symfony’s Process
component, Contao also provides a utility service for that:
namespace App\Cron;
use Contao\CoreBundle\Cron\Cron;
use Contao\CoreBundle\Exception\CronExecutionSkippedException;
use Contao\CoreBundle\Util\ProcessUtil;
class HourlyCron
{
public function __construct(private ProcessUtil $processUtil) {}
public function __invoke(string $scope): void
{
// Skip this cron job in the web scope
if (Cron::SCOPE_WEB === $scope) {
throw new CronExecutionSkippedException();
}
// Long-running process - probably not "ls" :-)
$promise = $this->processUtil->createPromise(new Process(['ls']));
// There's even a helper for another application command, so you don't have to worry about
// finding the right PHP binary etc.:
$promise = $this->processUtil->createPromise(
$this->processUtil->createSymfonyConsoleProcess('app:my-command', '--option-1', 'argument-1')
);
return $promise;
}
}
Contao keeps track of a cronjob’s last execution in the tl_cron_job
table. Thus,
if you want to test a cron job even though it has already been executed within
its defined interval, either truncate the whole table or delete the entry for the
specific cron job you want to test. If the table is empty every cronjob will be
executed on the first cron call. After that only on its defined interval.
In Contao 4.4, the table is called tl_cron
and it contains only the last execution
times of the named intervals, not the last execution time of individual cron jobs.
This is not necessary anymore in Contao 5.0 and up as you can use the --force
command line option as explained above.