Why cron job setup is not just crontab?
We specialize in professional cron job setup for Laravel, Node.js, and Go applications. Cron is a good old daemon that runs commands on a schedule, but relying on crontab without monitoring and duplicate protection risks production. Imagine: a digest sending task runs every minute, but while the old one is still running, a new one overlaps—the database crashes under load. Or a token cleanup task fails, and we find out a week later. In our project with 500,000 users, duplicates caused a 40% performance drop, requiring emergency fixes. Professional cron job configuration includes distributed locks, execution monitoring, and automatic error notifications. Our team, with 5+ years of experience and 200+ completed projects, has configured hundreds of such tasks for projects on Laravel, Node.js, and Go. Let's tell you how to do it right.
Problems solved by professional cron job setup
Duplicate prevention when horizontal scaling
If you have two servers and crontab on both, the task runs twice. The solution: onOneServer() in Laravel or a distributed lock via Redis. Example below. Without that, duplicate emails, duplicate charges, and extra database load are guaranteed. On one project, we helped a client save $12,000 per year by eliminating task duplication.
Missed executions and hidden errors
Without monitoring, you won't know a task didn't run for 3 days. We use Healthchecks.io or custom Telegram/Slack notifications. Laravel's built-in pingOnFailure() is 10 times more reliable than manually checking logs. In the last 12 months alone, we recorded over 50 incidents where monitoring saved the project.
Lock conflicts and race conditions
--withoutOverlapping and withoutOverlapping(5) in Laravel set the maximum overlap time. In Node.js, acquireLock with TTL. Without this, two copies of a task can simultaneously read and write the same data, leading to database corruption. Losses from missed tasks can reach $5,000 per month. Our approach reduces race conditions by 90% and costs as little as $1,500 for a standard setup.
How we set up cron jobs: stack and examples
Laravel Task Scheduling (primary stack)
Laravel is our main backend framework. php artisan schedule:run calls the config in app/Console/Kernel.php. Example:
// 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 start if previous still running ->onOneServer() // only on one server when horizontal scaling ->runInBackground(); // Cleanup expired sessions—every hour $schedule->command('sessions:cleanup') ->hourly() ->withoutOverlapping(5) // maximum 5 minutes overlap ->appendOutputTo(storage_path('logs/sessions-cleanup.log')); // Every minute: check notification queue $schedule->command('notifications:send-pending') ->everyMinute() ->runInBackground() ->skip(fn() => !config('features.notifications')); } # 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 running 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 running } try { app(DigestService::class)->sendAll(); } finally { $lock->release(); } })->dailyAt('09:00')->onOneServer(); Monitoring: Healthchecks.io / Laravel Health
// Scheduler error notification $schedule->job(SendDailyDigestJob::class) ->dailyAt('09:00') ->pingOnSuccess('https://hc-ping.com/success-uuid') ->pingOnFailure('https://hc-ping.com/fail-uuid') ->emailOutputOnFailure('[email protected]'); More about locking
To prevent duplicates in Laravel, use `->onOneServer()` together with a distributed lock. In node-cron and go-cron, locking must be implemented manually via Redis or etcd.Approach comparison: Laravel Schedule vs node-cron vs go-cron
| Criteria | Laravel Schedule | node-cron | go-cron |
|---|---|---|---|
| Built-in monitoring | ✅ Ping on success/failure | ❌ Needs external | ❌ Needs external |
| Distributed lock | Via Redis / database | Via Redis | Via etcd |
| Ease of maintenance | High (artisan) | Medium | Low |
| Duplicate protection | onOneServer() + ->withoutOverlapping() | Manual | Manual |
Typical mistakes and their consequences
| Mistake | Consequence | Solution |
|---|---|---|
Forget runInBackground() | Task blocks scheduler, subsequent tasks delayed | Add runInBackground() |
Not setting onOneServer() | Duplicates on multiple servers | Use onOneServer() + distributed lock |
| Ignore monitoring | Errors go unnoticed for weeks | Connect Healthchecks.io or email |
| Too small lock TTL | Lock released early, duplicates | Set TTL = max execution time + buffer |
Why monitoring is important
Without monitoring, you risk discovering a problem too late. In our experience, a client didn't know that their report generation task hadn't run for 2 weeks—data was 30% outdated. After implementing monitoring with Telegram alerts, response time dropped to 15 minutes, and incident resolution costs decreased by 60%. We recommend pinging a health endpoint every minute—this covers 99% of scenarios.
What is included in the work (deliverables)
- Task code with duplicate protection (distributed lock).
- Execution monitoring (Healthchecks.io or custom).
- Error notifications (Slack, Telegram, email).
- Documentation for maintenance and adding new tasks.
- Training for the client's team.
Estimated timeline and cost
- Simple setup (2-3 tasks): from 1 day, typical cost $1,500–$3,000.
- Complex with monitoring and locks: from 2 days, typical cost $3,000–$5,000.
Process
- Analysis — determine which tasks are needed: cache cleanup, mailings, report generation.
- Design — define intervals, locks, monitoring.
-
Implementation — write code with error handling and
withoutOverlapping. - Testing — run in staging, check logs.
- Deployment — configure crontab and healthchecks.
Our engineers guarantee your scheduler will run stable. If you want to avoid duplicate problems, order professional cron job setup. Contact us for a free project assessment.







