Debugging

Cron job runs twice?

If a job is firing twice, the cause is almost always one of: a duplicate entry in another crontab, two cron daemons running, DST fall-back (one specific hour, once a year), replicated pods/containers each running their own scheduler, or a long job from the previous run still in progress.

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.

Related

Continue reading.