A cron job that "runs" isn't necessarily reliable: if it runs longer than its interval, it can overlap and corrupt data. This episode teaches flock for locking, retry with backoff, idempotent scripts, and timeout — the patterns that make a job production-ready.

In episode 8 we understood anacron and system directories. Now we go up a level: from "job runs" to "job is reliable". The difference shows when something unexpected happens — a job runs two minutes too long, a script crashes halfway, or a server is overloaded.
There are three pillars of reliability we'll build: lock (prevent overlap), retry (handle transient failures), and idempotency (safe to rerun), plus timeout as a safety net.
Imagine a backup that usually finishes in 40 minutes, but one day runs for 70 minutes. With an hourly schedule, the next instance starts before the first one finishes — two backup processes run at once, overwrite each other's files, and corrupt both.
Cron doesn't prevent this by default. You have to do it yourself.
flock locks a file and only runs the command if the lock is available:
0 * * * * flock -n /var/lock/backup.lock /usr/local/bin/backup.sh-n (non-blocking): if another process already holds the lock, the command doesn't run — it doesn't wait.-n.The result: an overlapping second job exits immediately without damaging anything.
For full control and clear logs, use flock inside the script:
#!/bin/bash
set -euo pipefail
exec 9>/var/lock/backup.lock
if ! flock -n 9; then
echo "Job sebelumnya masih berjalan, keluar." >> /var/log/backup.log
exit 1
fi
rsync -a /data /backup >> /var/log/backup.log 2>&1An old trick that's still useful — directory creation is atomic:
if ! mkdir /var/lock/backup.run 2>/dev/null; then
echo "Lock sudah ada."; exit 1
fi
trap 'rmdir /var/lock/backup.run' EXITA failed mkdir means the lock is already held. trap ... EXIT ensures the lock is cleaned up even if the script errors.
Warning
When using locks, always handle failure to acquire the lock explicitly. If you don't, the second job will "fail silently" — and no one will know. Write a message to the log, and (in episode 12) send an alert.
A momentary network drop, a database restart — many transient failures recover with a retry. A simple retry with a loop:
#!/bin/bash
MAX=3
for i in $(seq 1 $MAX); do
if curl -fsS https://api.example.com/sync; then
exit 0
fi
echo "Percobaan $i gagal, menunggu..." >> /var/log/sync.log
sleep $((i * 30))
done
echo "Gagal setelah $MAX percobaan" >> /var/log/sync.log
exit 1A backoff pattern (sleep grows longer each attempt) avoids hammering a service that's already struggling. Don't retry forever at a fixed interval — set a limit, then fail with a clear exit code.
Idempotent means running an operation many times produces the same result as running it once. A full backup that overwrites with the latest data, a sync that produces the same state — both are idempotent.
rsync -a --delete /data/ /backup/ # hasil akhir selalu sama
pg_dump -Fc db > /backup/db.dump # menimpa, bukan menumpukThe opposite of idempotent: a script that accumulates — e.g. pg_dump >> file or creating a new file each time without cleaning up old ones. A job rerun after a retry will produce duplicates.
A script can hang — waiting on the network, on a lock that never releases. timeout cuts off the process after a given duration:
0 * * * * timeout 30m /usr/local/bin/backup.sh >> /var/log/backup.log 2>&1timeout 30m kills the process (SIGTERM by default) if it exceeds 30 minutes. Without this, a hung job holds the lock forever and blocks every subsequent execution.
Tip
The complete production combination: flock -n (anti-overlap) + timeout (anti-hang) + internal retry (anti-transient-failure) + idempotency (safe to repeat). These four layers make a cron job almost incapable of harming the system.
| Pattern | Problem Solved | Tool |
|---|---|---|
| Lock | Two overlapping jobs | flock -n, mkdir |
| Retry | Transient failures | Loop + backoff |
| Idempotency | Duplicates on rerun | --delete, overwrite not append |
| Timeout | Job hangs forever | timeout 30m |
Key takeaways:
flock -n.timeout stops a hung job from blocking everything.In episode 10 we'll apply all of this to a real case: backup and maintenance automation — scheduling rsync, restic, and pg_dump, verifying backup results, plus logrotate, tmp cleanup, and careful package updates!