What Is dotnetup? The .NET Version Manager We've Been Missing

Every .NET developer has lived this small daily annoyance: which SDK is actually on this machine?
You've got the one Visual Studio installed, the one winget pulled in, the one the dotnet-install
script dropped into some folder, maybe one your Linux distro packaged, and then a global.json in your
repo politely insisting on a version that may or may not be present. Sorting that out has always been a
little adventure.
Other ecosystems solved this years ago. Rust has rustup. Node has nvm. Python has pyenv.
You type one command, you get the toolchain version you asked for, scoped to your user, no admin
rights, no drama. .NET never had a blessed, official equivalent — until now.
That tool is dotnetup, and it's exactly the thing we've been missing.
So what is it?
dotnetup is the official cross-platform .NET SDK and runtime version manager. Its whole job is to
install, track, and switch between .NET SDKs and runtimes cleanly — the same role rustup plays for
Rust. It's being built by the .NET team inside the dotnet/sdk repo,
and the goal is for it to eventually become the recommended way to get .NET onto a machine.
The design choices are the ones you'd hope for after watching how rustup and friends matured:
- User-scoped installs, no elevation. It drops SDKs into a per-user directory — no
sudo, no admin
prompt, no fighting with machine-wide state. That alone is a big deal for locked-down corporate
laptops and CI runners. - It reads your
global.json. Point it at a repo and it figures out which SDK that repo actually
needs and installs exactly that. No more "works on my machine because I happen to have 8.0.404." - Side-by-side runtimes. It can install multiple runtimes at once, which is precisely what you want
when a library multi-targetsnet8.0,net9.0, andnet10.0and you need to run the tests against
all three. - A manifest that tracks what's installed, per channel, so updates are auditable and old versions
can be cleaned out instead of quietly piling up forever. - Native AOT-compiled and signed. The tool itself is a fast, self-contained native binary with
signed releases — sensible for something whose job is downloading and running other binaries.
What it looks like in practice
The flagship command is the one that makes the repo "just work":
# In a repo with a global.json — installs exactly the SDK (and runtimes) it pins
dotnetup install
That single line reads global.json, sees which SDK version the project wants, and acquires it. The
official patterns demo walks through the
three scenarios that cover most real life:
- Multi-runtime testing — a library targeting
net8.0/net9.0/net10.0, wheredotnetup install
grabs the SDK and every runtime needed to actually run the test matrix. - Cross-platform web app —
global.jsonpins the SDK, and the samedotnetup installgives every
developer and every CI runner identical tooling on Windows, macOS, and Linux, with zero manual
setup steps. - SDK bump — you change the pinned version in
global.json, commit, and that change automatically
propagates to all developers and CI runners next time they rundotnetup install.
You can also reach for specific things directly, including the preview channels that used to be a pain
to script:
# Grab a preview SDK / runtime explicitly
dotnetup sdk install preview
If you've ever written a brittle shell snippet to fetch a preview SDK in a build script, you'll
appreciate having a real, supported command for it.
Why this is more than a convenience
The detail that made me sit up: the .NET SDK's own main branch now uses dotnetup to bootstrap its
build. Building the SDK from source has a classic chicken-and-egg problem — you need an SDK to build
the SDK — and dotnetup is becoming how that bootstrap is solved (download a pre-built, checksum-verified
toolchain instead of compiling one first). When a tool is trusted to bootstrap the very thing it
installs, that tells you the team is serious about it.
There's also a clear line of sight on the roadmap that matters for where I spend a lot of my time
lately — AI agents and CI. Planned capabilities include oneshot execution (run a command against a
specific SDK version without permanently installing it — perfect for A/B testing whether your build
behaves the same on 9.0 vs 10.0), self-updating, signature verification, and first-class
CI/CD and agent integration. An agent that can provision the exact .NET it needs, in a user-scoped
dir, with a verified signature, is a much safer thing to hand a task to than one running a curl-pipe-bash
install script.
The honest caveats
This is early days. As I write, dotnetup is in preview and the practical way to try it is to build
it from source — the native AOT executable lives on a release/dnup-style branch of the dotnet/sdk
repo, and you publish it onto your PATH yourself. It's not yet the one-line install on the .NET
website (that's the destination, not today's reality). So treat it as "watch this closely and play
with it," not "rip out your current setup this afternoon."
But the direction is unambiguously right. .NET has needed a rustup of its own for years, and dotnetup
is finally it: user-scoped, global.json-aware, multi-runtime, signed, and trusted enough to bootstrap
the SDK itself. The day dotnetup install becomes the first thing you run after cloning any .NET
repo — and it just works, on every OS, for every contributor — is going to quietly remove a whole
category of "which SDK?" headaches from our lives.
I'll be following it. If you've already tried the preview and have war stories or tips, tell me — I'm
always listening on the links on the about page.