lockfile and proper-lockfile are utilities for managing concurrent access to resources using file-based locks, ensuring only one process modifies a file at a time. lockfile-lint serves a different purpose β it validates dependency lockfiles (like package-lock.json) for security policies and integrity. While the names sound similar, the first two handle runtime process synchronization, whereas the last one secures your supply chain during builds.
Developers often confuse these packages because of their names, but they solve two completely different problems. lockfile and proper-lockfile manage runtime process synchronization using file locks. lockfile-lint validates dependency lockfiles for security risks. Using the wrong tool can lead to race conditions or security vulnerabilities. Let's break down how they work and when to use each.
lockfile vs proper-lockfileBoth packages prevent multiple processes from writing to the same file simultaneously. They create a temporary .lock file to signal ownership. However, their implementation details differ significantly.
lockfile is the legacy option.
// lockfile: Callback-based API
const lockfile = require('lockfile');
lockfile.lock('data.txt', { wait: true }, function (err) {
if (err) return console.error(err);
// Do work...
lockfile.unlock('data.txt', function (err) {
if (err) console.error(err);
});
});
proper-lockfile is the modern standard.
// proper-lockfile: Promise-based API
const properLockfile = require('proper-lockfile');
async function run() {
const release = await properLockfile.lock('data.txt', { retries: 1000 });
try {
// Do work...
} finally {
await release(); // Ensures unlock even if error occurs
}
}
run().catch(console.error);
lockfile-lintlockfile-lint does not lock files for concurrency. Instead, it reads your package-lock.json or yarn.lock to ensure dependencies come from trusted sources. This is critical for supply chain security.
lockfile-lint validates dependency integrity.
# lockfile-lint: CLI usage in CI
npx lockfile-lint --path package-lock.json --validate-https --allowed-hosts npm yarn
You can also use it programmatically within a build script.
// lockfile-lint: Programmatic API
const lockfileLint = require('lockfile-lint');
const validator = lockfileLint.LockfileValidator();
const result = validator.validate({
lockfilePath: './package-lock.json',
validateHttps: true,
allowedHosts: ['npm', 'yarn']
});
if (!result.valid) {
console.error('Security check failed:', result.errors);
process.exit(1);
}
When a process crashes without unlocking, the lock file remains. This can block other processes forever.
lockfile requires manual cleanup.
// lockfile: Manual stale check
lockfile.check('data.txt', function (err, isLocked) {
if (isLocked) {
// Must manually inspect file age and unlink if stale
// Error-prone and platform-dependent
}
});
proper-lockfile handles this automatically.
// proper-lockfile: Automatic stale handling
// The 'update' option keeps the lock fresh while work continues
const release = await properLockfile.lock('data.txt', {
update: 1000
});
// Lock auto-releases if process crashes before release()
You have a background job processing a shared queue file.
proper-lockfile// proper-lockfile: Safe concurrent access
await properLockfile.lock('queue.json');
await processQueue();
await properLockfile.unlock('queue.json');
You are updating an old Node.js script that already uses lockfile.
lockfile (Temporary)// lockfile: Legacy compatibility
lockfile.lock('legacy.txt', cb);
You want to block installs from GitHub repos or untrusted registries.
lockfile-lint# lockfile-lint: CI Pipeline Step
npx lockfile-lint --path package-lock.json --allowed-hosts npm
| Feature | proper-lockfile | lockfile | lockfile-lint |
|---|---|---|---|
| Primary Use | File Concurrency | File Concurrency | Dependency Security |
| API Style | Promises (Async/Await) | Callbacks | CLI & API |
| Stale Locks | Auto-cleanup | Manual Handling | N/A |
| NFS Support | β Yes | β Often Fails | N/A |
| Security | Process Safety | Process Safety | Supply Chain Safety |
proper-lockfile is the clear winner for file locking. It saves you from hard-to-debug race conditions and works in modern environments like Docker. Treat lockfile as legacy code β do not start new projects with it.
lockfile-lint belongs in your security toolkit, not your runtime code. Add it to your CI pipeline to ensure no one sneaks in untrusted dependencies.
Final Thought: Don't let the similar names fool you. Use proper-lockfile to manage running processes and lockfile-lint to manage your dependency tree. Mixing them up won't work β they solve different layers of the stack.
Avoid lockfile in new projects. It relies on callbacks instead of Promises, lacks robust stale lock recovery, and often fails on network drives. Only use it if maintaining legacy code that already depends on it.
Choose lockfile-lint for CI/CD pipelines to enforce security policies on your dependency tree. It is not a file locking tool β use it to block installs from untrusted hosts or detect tampered lockfiles before deployment.
Choose proper-lockfile for any new project requiring file locking. It supports Promises, handles stale locks automatically, and works reliably on network file systems (NFS) and Docker volumes where basic locking fails.
A very polite lock file utility, which endeavors to not litter, and to wait patiently for others.
var lockFile = require('lockfile')
// opts is optional, and defaults to {}
lockFile.lock('some-file.lock', opts, function (er) {
// if the er happens, then it failed to acquire a lock.
// if there was not an error, then the file was created,
// and won't be deleted until we unlock it.
// do my stuff, free of interruptions
// then, some time later, do:
lockFile.unlock('some-file.lock', function (er) {
// er means that an error happened, and is probably bad.
})
})
Sync methods return the value/throw the error, others don't. Standard node fs stuff.
All known locks are removed when the process exits. Of course, it's possible for certain types of failures to cause this to fail, but a best effort is made to not be a litterbug.
Acquire a file lock on the specified path
Acquire a file lock on the specified path
Close and unlink the lockfile.
Close and unlink the lockfile.
Check if the lockfile is locked and not stale.
Callback is called with cb(error, isLocked).
Check if the lockfile is locked and not stale.
Returns boolean.
A number of milliseconds to wait for locks to expire before giving up.
Only used by lockFile.lock. Poll for opts.wait ms. If the lock is
not cleared by the time the wait expires, then it returns with the
original error.
When using opts.wait, this is the period in ms in which it polls to
check if the lock has expired. Defaults to 100.
A number of milliseconds before locks are considered to have expired.
Used by lock and lockSync. Retry n number of times before giving up.
Used by lock. Wait n milliseconds before retrying.