flock: The Cron Mutex You're Not Using
Cron will start your job again while the last run is still going. Two lines with flock stop the overlap — and the lock can't go stale.
Cron has exactly one guarantee: it starts your job on time. It has zero opinions about whether the previous run finished. Your nightly sync usually takes four minutes — then one slow night it takes six, and at minute five cron fires a second copy. Two processes now write the same rows, the same files, the same API calls.
This isn't hypothetical. Overlap bugs appear precisely when things are already bad: slow disks, a big batch, a hung upstream. The failure correlates with your worst days.
Why this matters
Every overlapping run is a duplicate-side-effect generator: double emails, double charges, half-written exports, two pg_dumps fighting over one file. And the homegrown fixes are often worse than the bug. PID files go stale after a kill -9 or a reboot, and then the job refuses to run at all. You want a lock that dies with the process holding it — which is exactly what the kernel already knows how to do.
How it works
flock takes an advisory lock on a file. The clever part is what the kernel ties the lock to: the open file descriptor, not the file's contents. When your process exits — cleanly, via SIGKILL, via OOM — the descriptor closes and the lock evaporates. No stale locks, no cleanup scripts.
The CLI wrapper handles simple cases: flock -n /tmp/job.lock ./job runs the command under the lock and exits with status 1 if the lock is already held. The -n flag means "don't wait." For scripts, open a descriptor yourself with exec 9>lockfile, then call flock -n 9. The lock now lives exactly as long as your script, and every child process runs inside it.
Where this helps
- Overrunning batch jobs — an ETL that occasionally blows past its schedule stops double-inserting rows.
- Backups — two dump jobs racing to the same output path produce a backup you'll only discover is broken during the restore.
- Housekeeping — retention, compaction, and cache-warm scripts that must not fight each other.
- Multi-host cron — put the lockfile on shared storage and several servers running the same job effectively elect a winner.
Watch out
- The lock is advisory. It only protects code that takes it. A process writing the same data directly will not be stopped.
-nskips the run entirely. Correct for idempotent catch-up jobs, wrong where every invocation must happen — use-w 60to wait up to a minute instead.flockover NFS depends on server support and is historically flaky. Test yours before trusting it across hosts.- systemd timers already refuse to start a unit that's still running. If you're on timers, you may have this for free.
Try it yourself
Drop these lines at the top of any cron-run script. Run it in two terminals to watch the second copy exit instantly.
#!/usr/bin/env bash
# crontab: 0 2 * * * /usr/local/bin/nightly-etl.sh
exec 9>/tmp/nightly-etl.lock
if ! flock -n 9; then
echo "$(date -Is) previous run still active, skipping" >>&2
exit 0
fi
# everything below can no longer overlap with itself
echo "running etl..."
sleep 300 # simulate a slow job
TL;DR
- Cron starts jobs on schedule; it never checks that the previous run finished.
- Overlapping runs mean duplicate writes — and they strike exactly when your systems are slow.
- Add two lines,
exec 9>…andflock -n 9, to every cron-run script today.