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

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.scheme: 'https'

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 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 to tag the service accordingly:

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

use Contao\CoreBundle\ServiceAnnotation\CronJob;

 * @CronJob("hourly")
class ExampleCron
    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. 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.