1. Duplicate entries across crontabs
The same job might exist in two places:
# Check user crontab crontab -l # Check root crontab sudo crontab -l # Check system crontab cat /etc/crontab # Check drop-in directory ls -la /etc/cron.d/ cat /etc/cron.d/* # Check hourly/daily/weekly/monthly aggregators ls /etc/cron.hourly /etc/cron.daily /etc/cron.weekly /etc/cron.monthly
If the same script appears in two places, both are scheduled independently and fire on their own.
2. Two cron daemons running
On systems where you've installed a new cron implementation without removing the old:
ps aux | grep -E 'cron|crond'
If you see both cron and crond, or two instances of the same, you have duplicate schedulers.
sudo systemctl status cron sudo systemctl status crond # Disable the one you're not using: sudo systemctl disable --now cron
3. Replicated Kubernetes pods or containers
If you put @Scheduled in a Spring app and deploy 3 pods, the job runs 3 times — once per pod. Same for Rails schedule.rb, Django celery beat, etc.
Fixes:
- Use Kubernetes CronJob (single execution) instead of in-app scheduling
- Use ShedLock or similar to lock the job across instances
- Move scheduling to a single dedicated pod via deployment topology
- Use Quartz with JDBC store for proper clustering
4. DST fall-back duplication
Once a year, when clocks "fall back" in the autumn, the 1 AM-2 AM hour repeats. A job scheduled at 30 1 * * * can fire twice — once at 1:30 AM DST and once at 1:30 AM standard time.
Most cron implementations actually de-duplicate this correctly. But if your job runs out of a Docker container, a Kubernetes scheduler, or a custom script: verify.
Fix: schedule outside the 1-3 AM window or run in UTC. See our DST guide.
5. Long-running jobs overlapping
If your job takes 65 minutes and is scheduled hourly, eventually two instances run concurrently. This isn't really "twice" — it's "still running" — but the symptom is the same: two processes doing the work.
Fix with flock to ensure non-overlapping execution:
0 * * * * /usr/bin/flock -n /tmp/myjob.lock /usr/local/bin/myjob.sh
The -n means "fail immediately if locked" — so the second instance exits without doing anything if the first is still running.
Diagnosing — log every run
If you can't tell whether the issue is duplicate scheduling or duplicate triggers, log every invocation with a timestamp and PID:
echo "$(date) PID=$$" >> /var/log/myjob.log # ... rest of script ...
Then look at the log. Two entries within seconds of each other = double scheduling. Two entries far apart = your scheduling is correct but something else is causing the duplicate.