Safeguard
Developer Tools•

How do you get repeatable NuGet restores with packages.lock.json?

Turn on the NuGet lock file with RestorePackagesWithLockFile, then use locked mode in CI so restore installs the exact packages or fails. Steps and limits.

Safeguard Security Team
4 min read

Set the RestorePackagesWithLockFile property to true so NuGet writes packages.lock.json, commit that file, and run CI restores in locked mode. Locked mode restores the exact packages in the lock file or fails if your project dependencies changed after the file was created.

What the lock file captures

NuGet's documentation says that with the property enabled, restore generates packages.lock.json at the project root, listing all package dependencies. That is the full closure, so it includes transitive packages, not only the ones you reference directly. The format is versioned, and the version depends on features you use, such as transitive pinning in Central Package Management.

<PropertyGroup>
  <RestorePackagesWithLockFile>true</RestorePackagesWithLockFile>
</PropertyGroup>

There is a small quirk. Once a project has a packages.lock.json in its root, the lock file is always used for restore, even if the property is not set. The documentation notes that creating a blank file is another way to opt in.

Restore behavior

When a lock file is present, NuGet does a quick check for changes in the dependencies declared in the project files. If nothing changed, it restores what the lock file lists, with no re-evaluation of dependencies. If it detects a change, it re-evaluates the package graph and updates the lock file to reflect the new closure.

That second behavior is convenient on a developer machine and wrong in CI, where nobody wants the pipeline to change dependencies on the fly.

Locked mode for CI

The documentation provides locked mode for exactly this case.

dotnet restore --locked-mode

In locked mode, restore either installs the exact packages listed in the lock file or fails if you changed the declared dependencies after the lock file was created. You can also set the RestoreLockedMode property in the project file.

SettingLock file missing or staleTypical use
Lock file, normal restoreNuGet updates itLocal development
Lock file, locked modeRestore failsCI and release builds
No lock fileResolves fresh each timeAvoid for shipped apps

Adoption steps

  1. Turn on the property in a shared build props file, so every project gets it.
  2. Restore once, review and commit the lock files.
  3. Add locked mode to CI and release builds.
  4. Update dependencies in a branch, regenerate, and review the lock diff in the pull request.
  5. Pin transitive packages you need to control. Central Package Management has an opt-in transitive pinning feature that promotes a transitive dependency to a top-level one implicitly. The documentation notes downgrades are not allowed, and an attempt raises error NU1109.

Limits

The lock file records versions. It is not a substitute for checking where packages come from, which is a separate topic in NuGet's documentation. If you change settings that affect the dependency tree, such as enabling pruning, the documentation warns the regenerated lock file may list fewer packages than before.

Where Safeguard fits

Safeguard's dependency scanning and SBOM generation can check the packages in a .NET project, including transitive ones, against known vulnerabilities, and a free scan is available at gold.safeguard.sh/scan.

Try it yourself

Open the inventory and look for transitive packages, which are the ones a lock file records and a project file never mentions.

Click through it at your own pace, or open it full screen.

Quick check

What is locked mode?

A restore setting that installs exactly what packages.lock.json lists, or fails if the project dependencies changed after the file was created.

Does the lock file include transitive packages?

Yes. The documentation says it lists all the package dependencies for the project.

Sources

Last reviewed: 19 October 2026.

Never miss an update

Weekly insights on software supply chain security, delivered to your inbox.

Self-healing security runs on Safeguard.

Your first fix PR is minutes away.

No sales call required, even your agent can complete the purchase over MCP.