Cron Tasks Setup for Web Application
Cron tasks run code on schedule: cleanup of stale data, digest emails, report generation, cache updates. Main challenge during setup is preventing parallel execution of the same task and ensuring execution monitoring.
Laravel Task Scheduling
// app/Console/Kernel.php
protected function schedule(Schedule $schedule): void
{
// Daily digest at 9:00 Moscow time
$schedule->job(SendDailyDigestJob::class)
->dailyAt('09:00')
->timezone('Europe/Moscow')
->withoutOverlapping() // don't run if previous is still executing
->onOneServer() // only on one server with horizontal scaling
->runInBackground();
// Cleanup stale sessions — every hour
$schedule->command('sessions:cleanup')
->hourly()
->withoutOverlapping(5) // max 5 minutes overlap
->appendOutputTo(storage_path('logs/sessions-cleanup.log'));
// Check notifications queue every minute
$schedule->command('notifications:send-pending')
->everyMinute()
->runInBackground()
->skip(fn() => !config('features.notifications'));
// Weekly report on Mondays at 8:00
$schedule->job(WeeklyReportJob::class)
->weekly()
->mondays()
->at('08:00');
}
# Crontab: run scheduler every minute
* * * * * cd /var/www/myapp && php artisan schedule:run >> /dev/null 2>&1
Node.js: node-cron
import cron from 'node-cron';
import { db } from './database';
import { emailService } from './services/email';
// Daily cleanup at 3:00
cron.schedule('0 3 * * *', async () => {
const lock = await acquireLock('cleanup-expired-tokens');
if (!lock) return; // another instance already executing
try {
const deleted = await db.query(
'DELETE FROM password_reset_tokens WHERE expires_at < NOW()'
);
console.log(`Cleaned ${deleted.rowCount} expired tokens`);
} finally {
await releaseLock('cleanup-expired-tokens');
}
}, { timezone: 'Europe/Moscow' });
// Every 5 minutes: update exchange rates
cron.schedule('*/5 * * * *', async () => {
try {
const rates = await fetchExchangeRates();
await cache.set('exchange_rates', rates, 300);
} catch (err) {
console.error('Exchange rates update failed:', err);
}
});
Distributed Lock via Redis (prevent duplication)
// For Laravel: standard Cache::lock()
$schedule->call(function () {
$lock = Cache::lock('daily-digest', 3600);
if (!$lock->get()) {
return; // another server already executing
}
try {
app(DigestService::class)->sendAll();
} finally {
$lock->release();
}
})->dailyAt('09:00')->onOneServer();
Monitoring: Healthchecks.io / Laravel Health
# Crontab with ping notification
* * * * * cd /var/www/myapp && php artisan schedule:run \
&& curl -fsS https://hc-ping.com/YOUR-UUID > /dev/null 2>&1
// Notification on scheduler error
$schedule->job(SendDailyDigestJob::class)
->dailyAt('09:00')
->pingOnSuccess('https://hc-ping.com/success-uuid')
->pingOnFailure('https://hc-ping.com/fail-uuid')
->emailOutputOnFailure('[email protected]');
Implementation timeline
Setup cron schedule for Laravel or Node.js application: 1 day. With distributed lock, execution monitoring and alerts: 2 days.







