lockfile vs lockfile-lint vs proper-lockfile
File Concurrency and Dependency Security Locks in Node.js
lockfilelockfile-lintproper-lockfileSimilar Packages:

File Concurrency and Dependency Security Locks in Node.js

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.

Npm Package Weekly Downloads Trend

3 Years

Github Stars Ranking

Stat Detail

Package
Downloads
Stars
Size
Issues
Publish
License
lockfile0259-128 years agoISC
lockfile-lint086839.1 kB619 days agoApache-2.0
proper-lockfile0286-216 years agoMIT

File Concurrency and Dependency Security Locks in Node.js

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.

🚦 File Locking: lockfile vs proper-lockfile

Both 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.

  • Uses callback-based APIs.
  • Can leave stale locks if a process crashes.
  • Known to fail on network file systems (NFS).
// 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.

  • Uses Promise-based APIs (async/await).
  • Automatically cleans up stale locks using update intervals.
  • Works reliably on NFS and Docker volumes.
// 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);

πŸ›‘οΈ Dependency Security: lockfile-lint

lockfile-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.

  • Checks if packages are hosted on allowed registries.
  • Ensures HTTPS is used for all resources.
  • Runs primarily in CI/CD pipelines.
# 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);
}

⚠️ Stale Lock Handling

When a process crashes without unlocking, the lock file remains. This can block other processes forever.

lockfile requires manual cleanup.

  • You must write extra code to detect and remove old locks.
  • Risk of two processes thinking they own the lock.
// 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.

  • Uses a heartbeat mechanism to keep locks alive.
  • If the process dies, the heartbeat stops and the lock expires.
// 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()

🌐 Real-World Scenarios

Scenario 1: Preventing Concurrent Writes

You have a background job processing a shared queue file.

  • βœ… Best choice: proper-lockfile
  • Why? You need async/await support and safety against crashes.
// proper-lockfile: Safe concurrent access
await properLockfile.lock('queue.json');
await processQueue();
await properLockfile.unlock('queue.json');

Scenario 2: Legacy Script Maintenance

You are updating an old Node.js script that already uses lockfile.

  • ⚠️ Choice: lockfile (Temporary)
  • Why? Refactoring to Promises might introduce bugs. Plan to migrate later.
// lockfile: Legacy compatibility
lockfile.lock('legacy.txt', cb);

Scenario 3: CI/CD Security Gate

You want to block installs from GitHub repos or untrusted registries.

  • βœ… Best choice: lockfile-lint
  • Why? It enforces policy before dependencies reach production.
# lockfile-lint: CI Pipeline Step
npx lockfile-lint --path package-lock.json --allowed-hosts npm

πŸ“Š Summary Table

Featureproper-lockfilelockfilelockfile-lint
Primary UseFile ConcurrencyFile ConcurrencyDependency Security
API StylePromises (Async/Await)CallbacksCLI & API
Stale LocksAuto-cleanupManual HandlingN/A
NFS Supportβœ… Yes❌ Often FailsN/A
SecurityProcess SafetyProcess SafetySupply Chain Safety

πŸ’‘ Final Recommendation

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.

How to Choose: lockfile vs lockfile-lint vs proper-lockfile

  • lockfile:

    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.

  • lockfile-lint:

    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.

  • proper-lockfile:

    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.

README for lockfile

lockfile

A very polite lock file utility, which endeavors to not litter, and to wait patiently for others.

Usage

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.
  })
})

Methods

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.

lockFile.lock(path, [opts], cb)

Acquire a file lock on the specified path

lockFile.lockSync(path, [opts])

Acquire a file lock on the specified path

lockFile.unlock(path, cb)

Close and unlink the lockfile.

lockFile.unlockSync(path)

Close and unlink the lockfile.

lockFile.check(path, [opts], cb)

Check if the lockfile is locked and not stale.

Callback is called with cb(error, isLocked).

lockFile.checkSync(path, [opts])

Check if the lockfile is locked and not stale.

Returns boolean.

Options

opts.wait

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.

opts.pollPeriod

When using opts.wait, this is the period in ms in which it polls to check if the lock has expired. Defaults to 100.

opts.stale

A number of milliseconds before locks are considered to have expired.

opts.retries

Used by lock and lockSync. Retry n number of times before giving up.

opts.retryWait

Used by lock. Wait n milliseconds before retrying.