# dotnet-outdated: a global tool for outdated packages and CI checks

> dotnet-outdated reports outdated NuGet packages in .NET projects from the command line, a gap the CLI leaves where the IDE's package manager would fill it. The options that matter for CI are `-f|--fail-on-updates`, `-o|--output` with a format, and `-pre`, which defaults to Auto rather than Never.

**dotnet-outdated/dotnet-outdated** — A .NET Core global tool to display and update outdated NuGet packages in a project

- Repository: https://github.com/dotnet-outdated/dotnet-outdated
- Stars: 1,691 · Forks: 172
- Language: C#
- License: MIT
- Published: 2026-10-16 · Updated: 2026-10-16 · Language: en
- Canonical page: https://hysenlabs.com/projects/dotnet-outdated-dotnet-outdated

## The CLI gap the tool exists to fill

Visual Studio's NuGet Package Manager will tell you a newer version of a package exists. The .NET command line tools do not. That asymmetry is the entire reason dotnet-outdated exists, and the README states it in those terms: the .NET Core command-line tools provide no built-in way to report on outdated NuGet packages.

What you get is a .NET Core global tool that reports on outdated NuGet packages in .NET Core and .NET Standard projects, and optionally upgrades them. The path argument accepts a `.sln`, `.slnx`, `.slnf`, `.csproj`, `.fsproj` or a `.cs` file-based app, or a directory containing a solution or project. With no argument it uses the current directory, which makes `dotnet outdated` the shortest useful invocation.

The path list is a small signal of how the tool has kept up. `.slnf` is a solution filter, and `.cs` file-based apps are a newer project style that a tool written against only `.csproj` would not handle. A tool that lists these in its own usage text is being tested against the current SDK surface rather than a version from memory.

## Installation is a global tool, and the old one collides with it

Installation needs a currently supported .NET SDK, then a single global install:

```bash
dotnet tool install --global dotnet-outdated-tool
```

The package identifier is `dotnet-outdated-tool`, not `dotnet-outdated`. Upgrading an existing installation uses:

```bash
dotnet tool update --global dotnet-outdated-tool
```

Now the part that will bite you. This repository is a continuation of the original `dotnet-outdated` tool created by Jerrie Pelser, and the README instructs anyone who already has the original installed to run `dotnet tool uninstall --global dotnet-outdated` before installing this version. Both register the `dotnet outdated` command, so leaving the original in place gives you two tools competing for one verb and no obvious indication of which one ran.

If your CI image was built years ago, check which one is on the PATH before trusting any output. The README does not describe a compatibility mode between the two.

## The options that decide what a run actually reports

The usage text is long, and most of it is about narrowing or shaping the report rather than finding it.

`-pre|--pre-release` accepts Auto, Always or Never and defaults to Auto. Auto means the tool looks for pre-release versions when a package has one, which is the behaviour that produces surprise upgrade suggestions for a package whose maintainers publish betas. Never is the setting for a stable-only policy, and it is the one most teams want to set explicitly rather than inherit.

`-vl|--version-lock` takes None, Major or Minor, defaulting to None. Minor keeps an upgrade inside the current major version, which is the sane setting for a service that treats major bumps as breaking changes.

`-t|--transitive` enables transitive dependency detection, and `-td|--transitive-depth` sets how many levels deep to analyse, with an integer default of 1. That default is the important number: transitive checking at depth one reports direct dependencies of your direct dependencies, and every extra level multiplies both the output size and the number of packages you are implicitly claiming to maintain.

`-inc|--include` and `-exc|--exclude` filter by substring, are culture and case insensitive, and can be repeated, where a single match is enough to include or to exclude a package. `-ot|--older-than` takes a number of days, default 0, and only includes versions older than that, which is how you stop a package released this morning from appearing in a report.

## Reports, exit codes, and upgrading without a prompt

Three options turn the tool from a report into a pipeline gate.

`-o|--output` names a report file and `-of|--output-format` chooses the format: json by default, or csv and markdown. JSON is the machine-readable one, and the format list is short enough that a script parsing it should not assume more will be added.

`-f|--fail-on-updates` returns a non-zero exit code when updates are found. That single flag is what turns an informational listing into a CI check, and it is the option that gives the tool its place in a pull request workflow.

`-u|--upgrade[:<TYPE>]` performs the upgrade, with TYPE set to Auto by default or Prompt. Prompt is the one to understand carefully in a non-interactive context: an automated run with the default Auto will modify project files rather than asking.

Three further flags change what gets touched. `-i|--include-auto-references` adds auto-referenced packages to the report. `-n|--no-restore` adds the reference without performing a restore preview and compatibility check, which is faster and skips the check that would tell you whether the new version breaks your build. `-utd|--include-up-to-date` includes every dependency in the report, not just the outdated ones, which is what you want when generating a full inventory rather than a change list.

`-r|--recursive` searches a directory for all projects, and `-fba|--include-file-based-apps` extends that search to loose `.cs` file-based apps. `-ifs|--ignore-failed-sources` turns package source failures into warnings instead of errors, which is the setting to reach for when a private feed is unreachable and you still want the public feed results.

## Why -u touches unrelated lines in your csproj, and the hostfxr error

Two documented failure modes are worth reading before you enable upgrades in CI.

The first is the FAQ entry on why unrelated changes appear in `.csproj` files when running with `-u`. The practical answer is to review the diff and to keep upgrades in a separate commit from feature work.

The second is an error about `hostfxr.dll`, `libhostfxr.so` or `libhostfxr.dylib` not being found. That is the.NET host resolving layer, and its absence means the tool was installed against an SDK that is no longer on the machine, or that a global tool is being invoked with an SDK version that does not match where it was installed.

The fix in practice is reinstalling the tool against the SDK you actually run, rather than carrying a global tool across SDK upgrades. The README documents the error as a known FAQ entry without prescribing a single remedy.

The repository carries a `test-projects/` directory and a `test/` suite, plus `test-projects` fixtures referenced by the screenshot images, so the upgrade behaviour is exercised against sample projects in CI.

## Conclusion

dotnet-outdated fits a .NET team that wants outdated package detection in a shell script or a pull request check rather than only in Visual Studio, and it fits projects that can accept the transitive depth of one by default. It does not fit a repository where the original tool by the same name is already installed, since both register the `dotnet outdated` command and the README requires uninstalling the old one first. Verify first that your feed credentials resolve from the command line, because a secure feed failure stops the tool unless you pass `-ifs|--ignore-failed-sources`, then start with a report run using `-o|--output` in markdown before letting `-f|--fail-on-updates` gate a pipeline.

## FAQ

### How do I install dotnet-outdated?

With a currently supported .NET SDK installed, run `dotnet tool install --global dotnet-outdated-tool`. Use `dotnet tool update --global dotnet-outdated-tool` to upgrade an existing installation.

### I already have the original dotnet-outdated tool. What do I do?

This project is a continuation of the original tool by Jerrie Pelser, and the README says to run `dotnet tool uninstall --global dotnet-outdated` before installing this version.

### How do I make dotnet-outdated fail a CI build?

Pass `-f|--fail-on-updates`, which returns a non-zero exit code when updates are found. Combine it with `-o|--output` and `-of|--output-format` to save a json, csv or markdown report.

### Does dotnet-outdated look at transitive dependencies?

Only when you pass `-t|--transitive`. The `-td|--transitive-depth` option sets how many levels deep it analyses, with an integer default of 1.

### Why does dotnet-outdated change unrelated lines in my csproj?

The README lists this as a FAQ entry. Upgrading a package regenerates the package reference and the SDK writer normalises the file, so formatting and attribute order can shift along with the version change.

### What is the error about hostfxr.dll or libhostfxr.so not found?

It is a documented FAQ entry in the README, and it refers to the .NET host resolving library being missing, which points at a mismatch between the SDK the global tool was installed with and the SDK you run it on.

## Sources

- [dotnet-outdated/dotnet-outdated on GitHub](https://github.com/dotnet-outdated/dotnet-outdated)
- [Issues](https://github.com/dotnet-outdated/dotnet-outdated/issues)
- [License: MIT](https://github.com/dotnet-outdated/dotnet-outdated/blob/master/LICENSE)
- [README](https://github.com/dotnet-outdated/dotnet-outdated/blob/master/README.md)
- [Releases](https://github.com/dotnet-outdated/dotnet-outdated/releases)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/dotnet-outdated-dotnet-outdated
