Skip to content

Scheduled commands and queue

Command Frequency Needed
anvil:tick every minute always: workflow deadlines and escalations, reminders of overdue tasks, modules listening to the event anvilTick
anvil:prune daily always: expired exports, read notifications older than --days (90), API access log older than $apiAccessLogDays, stored API answers older than a day, logouts announced by single sign-on providers after 30 days
anvil:api:review daily with the API: notices about expiring tokens and clients and unused clients
anvil:audit:verify daily recommended: verifies the hash chain, exit code 1 if it was tampered with

With cron (crontab of the user PHP runs as):

# m h dom mon dow command
* * * * * cd /var/www/app && flock -n /tmp/anvil-tick.lock php spark anvil:tick > /dev/null
15 3 * * * cd /var/www/app && php spark anvil:prune > /dev/null
30 3 * * * cd /var/www/app && php spark anvil:api:review > /dev/null
45 3 * * * cd /var/www/app && php spark anvil:audit:verify > /dev/null

Only standard output is discarded: errors (and a failed audit verification) go to standard error and reach cron’s mail or your job monitoring. flock keeps a slow tick from overlapping with the next one. Run the scheduler on one server.

If the application uses codeigniter4/tasks (not a dependency of Anvil), schedule the commands in app/Config/Tasks.php and run only php spark tasks:run every minute from cron:

public function init(Scheduler $schedule): void
{
$schedule->command('anvil:tick')->everyMinute()->singleInstance()->named('anvil-tick');
$schedule->command('anvil:prune')->daily('3:15 am')->named('anvil-prune');
$schedule->command('anvil:api:review')->daily('3:30 am')->named('anvil-api-review');
$schedule->command('anvil:audit:verify')->daily('3:45 am')->named('anvil-audit-verify');
}

Exports with more rows than Config\Anvil::$exportSyncLimit (1000) are created by a job in the queue Config\Anvil::$exportQueue (anvil). Without a worker they never finish. The queue is the only one Anvil uses. Run the worker as a service, e.g. with systemd:

/etc/systemd/system/anvil-queue.service
[Unit]
Description=Anvil export queue
After=network.target mysql.service
[Service]
User=www-data
WorkingDirectory=/var/www/app
ExecStart=/usr/bin/php spark queue:work anvil -max-jobs 100 -max-time 3600
Restart=always
RestartSec=5
[Install]
WantedBy=multi-user.target

The worker exits after 100 jobs or an hour and systemd starts it again, which also frees memory. Failed jobs: php spark queue:failed. See Exports.