Contao periodically executes some tasks via its own cron functionality. The following is a list of tasks executed by Contao’s own bundles:

Task Interval
Generate calendar RSS feeds daily
Purge expired comment subscriptions daily
Purge temp folder daily
Purge search cache daily
Generate XML sitemap daily
Purge expired registrations daily
Purge expired Opt-In tokens daily
Generate news RSS feeds daily
Purge expired newsletter subscriptions daily

Configuring the Cron Job

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.

You can disable the front end cron by going to System » Settings » Cron job settings and enabling the setting Disable the command scheduler. After disabling the front end cron you should periodically let Contao executes its cron jobs, by either making a request to a web URL, or by executing them via the command line.


In order to trigger cron job execution via a web URL, a request to the _contao/cron, route, e.g., needs to be made. In a Linux crontab you could use the following instructions for example:

* * * * * wget -q -O /dev/null

Command Line

This feature is only available in Contao 4.9 and later.

You can also execute the cron jobs directly via the command line:

$ 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:

* * * * * php /path/to/contao/vendor/bin/contao-console contao:cron

Registering Cron Jobs

Registering custom cron jobs is similar to registering to hooks.

Using the PHP Array Configuration

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 …

Using Service Tagging

This feature is only available in Contao 4.9 and later.

Cron jobs can also be registered using the contao.cronjob service tag with the following options:

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.
# config/services.yaml
                name: contao.cronjob
                interval: 0 */2 * * *
                method: onEveryTwoHours

Using Service Annotation

You can also use the Contao\CoreBundle\ServiceAnnotation\CronJob service annotation together with the Terminal42\ServiceAnnotationBundle\ServiceAnnotationInterface to tag the service accordingly.

// src/Cron/ExampleCron.php
namespace App\Cron;

use Contao\CoreBundle\ServiceAnnotation\CronJob;
use Terminal42\ServiceAnnotationBundle\ServiceAnnotationInterface;

 * @CronJob("hourly")
class ExampleCron implements ServiceAnnotationInterface
    public function __invoke(): void
        // Do something

The annotation can either be used on the class or on individual methods. When it is used on the class, either the __invoke method will be used - or an auto generated method name (e.g. onMinutely), if present.

If you need an interval like */5 * * * you need to escape either the * or / with \, since */ would close the PHP comment.


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;

class HourlyCron
    public function __invoke(string $scope): void
        // Do not execute this cron job in the web scope
        if (Cron::SCOPE_WEB === $scope) {

        // …


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.

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.