flock: Preventing Concurrent Runs with File Locks
What Is flock?
Conclusion: flock is a util-linux command that uses a file-based lock to stop a script or job from running twice at the same time, in a single line.
What you'll learn:
- The pattern for preventing cron job overlap with flock
- When to use exclusive / shared / non-blocking / timeout modes
- A drop-in snippet for self-locking a script
The practical patterns
- Guard a single command ->
flock -n /var/lock/job.lock cmd - Guard an entire script -> put the
FLOCKERboilerplate at the top - Want to wait ->
-w seconds; fail immediately ->-n
Assumptions (target environment)
flockships with util-linux and is present on virtually every Linux distro- Locks are advisory (cooperative): they only apply between processes that use flock
- Support on NFS / CIFS is limited (covered below)
Why Prevent Concurrent Runs?
Conclusion: If a backup or batch job restarts while the previous run is still going, you risk data corruption, double processing, and runaway load. flock prevents the overlap structurally.
Suppose a batch job runs every 5 minutes via cron, but one run happens to take longer than 5 minutes. The next start overlaps the previous run, and two processes write to the same file at once. The result is typically one of:
- Corrupted or interleaved output files and logs
- Duplicate processing (double emails, double billing)
- Processes piling up and exhausting memory / CPU
You could manage a PID lockfile yourself, but if the process dies via kill -9 or a power loss, a stale lock remains and future runs never start. With flock, the kernel releases the lock automatically when the process exits, so there is no cleanup to do.
How Do You Use flock?
Conclusion: The basic form is
flock lockfile command. The lockfile is created automatically if missing, and the lock is released the moment the command exits.
flock has three syntaxes.
# Form 1: lock a file/directory and run a command flock /var/lock/mytask.lock command args # Form 2: run a single command through the shell with -c flock /var/lock/mytask.lock -c 'command1 && command2' # Form 3: lock an already-open file descriptor number flock 200
A minimal example. Run echo while holding an exclusive lock:
flock -x /tmp/myapp.lock echo 'running under lock'
- The lockfile (
/tmp/myapp.lock) is created if it doesn't exist -xrequests an exclusive lock (it's the default, so it can be omitted)- The lock is released as soon as
echofinishes
The lockfile is a "key," not "data." It can be empty, and you never need to delete it. Leaving the file in place causes no problem, because the lock state is tied to the open file descriptor, not to the file's existence.
What Are the Key flock Options?
Conclusion: The four to know are
-n(don't wait),-w(time limit),-s(shared), and-E(exit code on failure).
| Option | Meaning |
|---|---|
-x, --exclusive |
Exclusive (write) lock. Default |
-s, --shared |
Shared (read) lock. Multiple holders allowed |
-n, --nonblock |
Fail immediately instead of waiting (exit status 1) |
-w, --timeout sec |
Wait up to N seconds, then fail. Fractions allowed (-w 0.5) |
-u, --unlock |
Drop the lock explicitly |
-E, --conflict-exit-code N |
Exit code on -n / -w failure (default 1) |
-o, --close |
Close the fd before running the command (child won't hold it) |
-c, --command |
Run a single command through the shell (sh -c) |
-w 0 is equivalent to --nonblock (zero seconds of waiting means no waiting).
How Do You Stop a cron Job From Overlapping?
Conclusion: Wrap the cron line in
flock -n lockfile. If the previous run is still going, this run exits quietly and no overlap occurs.
This is the most common flock use case. Write the crontab line like this:
# Back up every 5 minutes. Skip this run if the previous one is still going. */5 * * * * /usr/bin/flock -n /var/lock/backup.lock /opt/scripts/backup.sh
-nmeans exit immediately on overlap (skip rather than wait)- If the previous job has finished, the lock is acquired and the job runs
- By convention, put the lockfile in
/var/lock/or/run/lock/
If you'd rather wait a little when runs overlap, use -w:
# Wait up to 30 seconds, then give up */5 * * * * /usr/bin/flock -w 30 /var/lock/backup.lock /opt/scripts/backup.sh
Inside cron, write flock as an absolute path (/usr/bin/flock) so it works even when PATH is minimal.
How Do You Make a Script Self-Locking?
Conclusion: Put a file-descriptor block or the
FLOCKERboilerplate at the top of the script, and it guards itself against concurrent runs no matter how it's invoked.
Instead of writing flock on every cron line, build the lock into the script itself. Two common patterns.
Pattern 1: File descriptor approach
#!/bin/bash
exec 200>/var/lock/myscript.lock
flock -n 200 || { echo "already running"; exit 1; }
# --- everything below runs as an exclusive critical section ---
echo "do work..."
sleep 10exec 200>...opens the lockfile on fd 200 (200 is a convention; any free number from 9-255 works)flock -n 200locks that fd; if it can't, the||branch exits- When the script ends, the fd closes and the lock releases automatically
Pattern 2: FLOCKER boilerplate (from the man page)
#!/bin/bash
[ "${FLOCKER}" != "$0" ] && exec env FLOCKER="$0" flock -en "$0" "$0" "$@" || :
# --- everything below always runs under the lock ---
echo "do work..."This uses the script itself as the lockfile and checks the FLOCKER environment variable to decide whether it has already re-executed through flock. Paste it at the top and self-locking is done.
When you lock the script file itself (flock -en "$0"), it doesn't play well with scripts that rewrite themselves via > (self-updating scripts). It's safer to lock a dedicated file you never overwrite.
Non-Blocking vs Timeout: Which Do You Use?
Conclusion: Use
-nif it's fine to drop the overlapping run; use-w secondsif a short wait lets the work proceed. Check the exit code to detect failure.
# Fail immediately (for batches where skipping an overlap is fine) flock -n /var/lock/job.lock ./job.sh # Wait up to 10 seconds (for work where a brief contention should be absorbed) flock -w 10 /var/lock/job.lock ./job.sh
Detect whether the lock failed by checking the exit code. When -n / -w fails, the exit code is 1 by default and can be changed with -E.
flock -n -E 99 /var/lock/job.lock ./job.sh
if [ $? -eq 99 ]; then
echo "skipped because another process is running"
fiIf the lock succeeds and the command runs, flock's exit code is the command's own exit code. Pick an -E value your command never returns, so you can tell a lock failure apart from a real command result.
What Are the Common flock Pitfalls?
Conclusion: The main traps are no support on NFS, losing the lock when you recreate the data file with
>, and the lockfile's location.
1. It may not work on NFS / CIFS
The flock(2) implementation is limited on network filesystems. Depending on mount options, flock may always fail or simply not enforce the lock. Keep lockfiles on a local /var/lock / /run/lock, not on shared storage.
2. Recreating the data file drops the lock
The lock is tied to the open file descriptor. If you lock the output data file and later rebuild it with >, the inode changes and the lock becomes meaningless.
# Bad: locking data.txt while recreating data.txt flock data.txt sh -c '> data.txt; generate >> data.txt'
Lock a dedicated lockfile you never overwrite.
3. flock does not detect deadlock
If multiple locks are taken in a crossing order they can deadlock, but flock itself won't detect it. Keep it to one lock per script and avoid designs that span multiple locks.
Avoid these, and flock becomes a powerful, robust way to shut down concurrent runs in just a few characters.