Table of Contents
Laravel’s task scheduler is powerful, but when tasks are dynamically loaded from a database instead of hardcoded in app/Console/Kernel.php, finding their actual execution frequency becomes less obvious.
If your Kernel.php looks like this:
protected function schedule(Schedule $schedule)
{
foreach (ScheduledTask::active()->get() as $task) {
$schedule->command($task->getCommand())
->cron($task->getCronExpression());
}
}
You won’t see individual task schedules in the code – they’re in the database.
Finding the Schedule
Query the database table directly to see what’s actually scheduled:
SELECT
name,
command,
cron_expression,
is_active,
last_run_at,
next_run_at
FROM scheduled_tasks
WHERE command LIKE '%import:data%'
ORDER BY command;
This reveals the actual cron expressions, active status, and execution history – everything hidden behind the dynamic loader.
Why This Pattern Exists
Database-backed schedules let you:
- Manage schedules without deployments – Change frequencies through admin panels or migrations
- Store execution metadata – Track runs, failures, and overlapping prevention
- Enable/disable tasks dynamically – No code changes required
The tradeoff: schedules are less discoverable. When debugging “why isn’t this running?”, remember to check the database, not just the code.
Common Gotchas
Schema variations: Column names vary by implementation. Your table might use cron instead of cron_expression, or active instead of is_active. Run DESCRIBE scheduled_tasks first.
Multiple environments: Staging and production databases may have different schedules. Always verify against the environment you’re debugging.
Overlapping prevention: Tasks with withoutOverlapping() won’t run if the previous execution hasn’t finished. Check started_at and finished_at timestamps.
Next time a scheduled task behaves mysteriously, skip the code and query the database first – that’s where the truth lives.
Leave a Reply