Find the Process Holding a Linux File Lock with lslocks

A service hanging on startup over a stale-looking lock file is a classic 2am page. lslocks tells you who actually holds it before you delete anything. This guide covers seeing which processes hold local file locks, filtering the list to one process, and saving machine-readable output for a diagnostic script. The examples use the locally installed util-linux 2.39.3 package, whose executable reports util-linux 2.41.3 on this host, so check your own version before relying on columns added by a newer build.

Allow about ten minutes. You need a shell and the util-linux package. Most commands are read-only and need no elevated privileges. Reading another user's lock paths can be limited by file permissions, and sudo may be needed if you need a complete system view. This guide observes locks only: it does not kill processes, remove lock files, restart services or alter application state.

1. Confirm the installed command

Start with the version and help text. This catches the common mistake of reading documentation for one util-linux release while running another:

$ lslocks --version
lslocks from util-linux 2.41.3
$ lslocks --help
Usage:
 lslocks [options]

List local system locks.

The local manpage is stamped util-linux 2.39.3 and documents the common options used here. The installed help also exposes --list-columns, which is useful when you need to discover fields rather than guess their names.

Checkpoint: if lslocks is missing, install the util-linux package through your distribution's normal package mechanism, then repeat the version check. Do not substitute a similarly named third-party script.

2. Read the default lock list

Run the command without options:

$ lslocks
COMMAND       PID TYPE SIZE MODE PATH
flock       12345 FLOCK      WRITE /run/example.lock
worker      23456 POSIX  4.0K READ  /var/lib/example/data.db

The rows on your machine will be different.

The list covers FLOCK locks made with flock(2), POSIX locks made with fcntl(2) or lockf(3), and OFD locks made with fcntl(2).

Do not interpret a visible lock as an error. Locks are often the normal coordination mechanism for databases, daemons and command-line tools. The useful question is usually whether the holder is expected, whether another process is waiting, and whether the lock is on the path you thought it was.

Paths and command names can be truncated for display: that is a presentation choice, not proof that the file name ends in an ellipsis.

3. Filter by the process you are investigating

Use --pid with a real process ID. The option filters locks held by that process:

$ target_pid=23456
$ lslocks --pid "$target_pid"
COMMAND   PID  TYPE SIZE MODE PATH
worker  23456 POSIX 4.0K READ /var/lib/example/data.db

Use a PID from ps, pgrep or the service status you are already examining. A missing row can mean the process holds no lock, the lock ended between commands, or the current user cannot inspect the relevant path. It does not identify a lock file that should be deleted.

For a reproducible smoke test, create a temporary lock in a shell and inspect the holder from another shell. This changes only a temporary file and ends when the sleeping process exits:

$ lock_path=$(mktemp /tmp/lslocks-demo.XXXXXX)
$ ( flock -n 9; sleep 30 ) 9>"$lock_path" &
$ target_pid=$!
$ lslocks --pid "$target_pid" --output COMMAND,PID,TYPE,SIZE,MODE,PATH
COMMAND PID TYPE SIZE  MODE  PATH
sleep   1234 FLOCK       WRITE /tmp/lslocks-demo.ab12CD

The PID and random suffix will differ. Wait for the background process to finish, then remove the temporary file with rm -- "$lock_path" if it remains. That removal is safe here because the path was created by mktemp; never adapt the command to delete a guessed production lock path. There is no service rollback because no service was touched.

4. Choose columns that answer the question

Use --output to make output shorter and stable for a human check or a small script:

$ lslocks --output COMMAND,PID,TYPE,MODE,PATH
COMMAND       PID TYPE MODE PATH
worker      23456 POSIX READ /var/lib/example/data.db

Useful fields include BLOCKER, the PID blocking the lock; START and END, the byte range; M, the mandatory-lock state; and HOLDERS, where supported by the installed build. Ask the command what it supports instead of assuming a field exists:

$ lslocks --list-columns
COMMAND <string>        command of the process holding the lock
    PID <integer>       PID of the process holding the lock
   TYPE <string>        kind of lock
   MODE <string>        lock access mode
   PATH <string>        path of the locked file
BLOCKER <integer>       PID of the process blocking the lock

Use --notruncate when the full path matters. Use --bytes when a script needs numeric sizes rather than the default human-readable form. The default size units use powers of 1024 and may be abbreviated, so do not parse the displayed suffixes as decimal SI units.

5. Produce JSON for a diagnostic script

--json emits a top-level locks array. This is easier to consume than aligned columns:

$ lslocks --json --pid "$target_pid"
{
   "locks": [
      {
         "command": "sleep",
         "pid": 1234,
         "type": "FLOCK",
         "size": null,
         "mode": "WRITE",
         "m": false,
         "start": 0,
         "end": 0,
         "path": "/tmp/lslocks-demo.ab12CD"
      }
   ]
}

The exact fields and values depend on the lock. A JSON null size is valid output, not a failed lookup. For scripts, check the command's exit status and handle an empty array; do not assume every lock has a readable path or a positive PID.

6. Interpret the fields without chasing the wrong fix

Warning: lock files are not interchangeable with locks. Removing a stale-looking file does not necessarily release a kernel lock and can corrupt an application's coordination. Stop the owning service using its documented procedure only after confirming that the process is genuinely stuck and that you have a recovery plan.

Done means