A file-based mutex for Node.js — coordinate access to shared resources across multiple processes.
@ster5/global-mutex provides a simple, promise-based API for
cross-process locking with automatic stale-lock detection and retry
support. It supports an optional robust engine using
proper-lockfile or falls back to a zero-dependency native fs.mkdir locking mechanism.
- Cross-process locking — synchronize work across independent Node.js processes using the filesystem.
- Automatic stale-lock recovery — stale locks are automatically detected and removed (default: 2 minutes).
- Configurable retries — built-in retry logic with exponential back-off and jitter (retries forever by default).
- Promise-based API — clean
async/awaitinterface with awithLockscoped-lock pattern. - Dual ESM / CJS — ships both ES module (
.mjs) and CommonJS (.js) builds with full TypeScript declarations. - Zero runtime dependencies — pure Node.js
implementation using
mkdiras an atomic lock primitive.
npm install @ster5/global-mutexTo enable the robust lockfile engine (highly recommended to prevent race conditions on Windows), install proper-lockfile as a peer dependency:
npm install proper-lockfileBy default, the package will automatically use proper-lockfile if it is installed, and fall back to the native fs.mkdir technique if it is missing.
You can explicitly force a specific provider using the GLOBAL_MUTEX_PROVIDER environment variable:
GLOBAL_MUTEX_PROVIDER=native node my-app.js
# or
GLOBAL_MUTEX_PROVIDER=proper-lockfile node my-app.jsAcquires a file-based lock, executes the provided action, and
releases the lock when the action completes — even if it throws.
import { withLock } from "@ster5/global-mutex";
const result = await withLock({ fileToLock: "/tmp/my-app.lock" }, async () => {
// critical section — only one process at a time
return await doExclusiveWork();
});| Option | Type | Default | Description |
|---|---|---|---|
fileToLock |
string |
(required) | Path to the lock file. The file is created automatically if it does not exist. |
stale |
number |
120000 (2 min) |
Duration in ms after which a lock is considered stale and can be reclaimed. |
retries |
object |
{ forever: true, minTimeout: 100, maxTimeout: 2000, randomize: true } |
Retry configuration passed to proper-lockfile. |
fs |
object |
Node.js fs |
Optional custom filesystem implementation. |
All additional options from
proper-lockfile LockOptions
are also accepted.
Check whether a file is currently locked.
import { isLocked } from "@ster5/global-mutex";
if (await isLocked("/tmp/my-app.lock")) {
console.log("Another process holds the lock");
}The default stale duration constant (120000 ms / 2 minutes).
import { defaultStaleDuration } from "@ster5/global-mutex";import { withLock } from "@ster5/global-mutex";
const tasks = Array.from({ length: 10 }, (_, i) =>
withLock({ fileToLock: "/tmp/app.lock" }, async () => {
console.log(`Task ${i} running exclusively`);
await doWork(i);
}),
);
await Promise.all(tasks);
// All 10 tasks ran one at a timeimport { withLock } from "@ster5/global-mutex";
await withLock({ fileToLock: "/tmp/lock-a" }, async () => {
// holds lock A
await withLock({ fileToLock: "/tmp/lock-b" }, async () => {
// holds both lock A and lock B
await doWork();
});
});- Node.js ≥ 20
git clone git@github.com:node-opcua/global-mutex.git
cd global-mutex
npm install| Command | Description |
|---|---|
npm run build |
Build the package with tsup (outputs ESM + CJS + .d.ts to dist/) |
npm test |
Run the test suite with Vitest |
npm run test:watch |
Run tests in watch mode |
npm run lint |
Lint with Biome |
npm run format |
Format with Biome |
- CI — runs on every push to
masterand on pull requests. Tests against Node.js 20, 22, and 24 on Ubuntu, macOS, and Windows. - Publish — triggered by pushing a
v*tag or via manual workflow dispatch. Builds, tests, and publishes to npmjs with provenance.
MIT © 2021 Etienne Rossignon, 2022–2026 Sterfive SAS