An infographic shows dotnetup managing .NET SDKs and runtimes across Windows, Linux, and macOS.
For years, setting up a multi-version.NET development environment across Windows, Linux, and macOS has felt a bit like assembling furniture with three different instruction manuals and a missing Allen wrench. Depending on your operating system and security posture, you were either wrestling with system package managers like apt or brew, unpacking raw tarballs into custom directories, running elevated Windows installers, or scripting around dotnet-install.ps1 and dotnet-install.sh. Worse, once those SDKs and runtimes landed on your drive, cleaning up obsolete servicing builds often required a separate utility—or manual folder surgery.

On October 15, 2026, Microsoft officially moved to end that friction by announcing the public preview of dotnetup, a cross-platform toolchain manager for.NET. Introduced in a Microsoft.NET Blog post by Program Manager Chet Husk, dotnetup brings a dedicated, user-local version manager to the.NET ecosystem, explicitly drawing inspiration from tools that developers in other languages have relied on for years, such as Rust’s rustup and Node’s nvm.

While dotnetup has been quietly taking shape in public repositories—even bootstrapping the .NET SDK repository’s own main branch builds since its soft internal launch in June 2026 —the public preview opens the workflow to everyday developers and CI/CD engineers. Here is a complete technical breakdown of how dotnetup works, how it handles Windows and cross-platform environments, its exact command workflows, and the critical caveats you need to know before adopting it.


An infographic shows dotnetup managing .NET SDKs and runtimes across Windows, Linux, and macOS. Why.NET Needed a Dedicated Toolchain Manager​

Historically,.NET’s installation story suffered from a split personality. On Windows, Visual Studio or standalone MSI/EXE installers typically dropped SDKs and runtimes into system-wide Program Files directories, requiring administrator privileges. On Linux and macOS, developers bounced between distribution feeds, Homebrew formulas, and standalone install scripts.

That patchwork created four recurring headaches for developers and IT administrators:

  1. Privilege Collisions: System-wide package managers and installers require sudo or local Administrator rights, which creates friction on locked-down enterprise workstations and containerized build agents.
  2. State and Cleanup Drift: While dotnet-install scripts are great for pulling down binaries, they do not track why a version was installed or clean up old monthly servicing releases when a newer patch arrives.
  3. Nightly and Preview Friction: Testing a daily runtime build or a preview SDK meant hunting down archive links on GitHub, unpacking them into isolated folders, and manually manipulating PATH or DOTNET_ROOT environment variables.
  4. Inconsistent Cross-Platform UX: The commands used to provision a developer laptop on macOS bore no resemblance to provisioning an Ubuntu container or a Windows PowerShell environment.

According to Microsoft’s announcement and the accompanying Microsoft Learn documentation, dotnetup solves these problems by storing all SDKs, runtimes, and tracking metadata inside user-owned directories rather than system-managed paths. Even more importantly, it separates installation requirements (your intent, such as tracking the .NET 10 channel or honoring a repository's global.json) from the concrete resolved versions on disk.

CapabilityLegacy dotnet-install ScriptsSystem Installers (apt, brew, MSI)dotnetup (Public Preview)
Default Privilege LevelUser-local (or custom path)Admin / sudo requiredUser-local (non-admin by default)
Tracks Channel IntentNo (one-shot download)Limited to OS feed cadenceYes (latest, 10.0, preview, daily)
Automatic Garbage CollectionNoVaries by package managerYes (removes unreferenced versions on update)
Signature & Checksum ValidationBasic / manualHandled by OS package managerBuilt-in validation
global.json IntegrationPartial (reads version, no state tracking)NoneNative requirement tracking & optional file updating
Unified Cross-Platform CLISeparate .sh and .ps1 syntaxCompletely different per OSIdentical dotnetup CLI across OSes

Summary: By decoupling user-level toolchain management from OS-level package managers and stateless download scripts, dotnetup brings stateful, non-admin lifecycle management to.NET.


The Windows Nuance: User-Local Now, System-Wide Later​

Before Windows developers rush to replace their entire Visual Studio SDK setup, it is important to understand a key architectural boundary in this initial public preview.

Right now, dotnetup is strictly built for non-admin, user-local operation. Everything it downloads and manages lives inside your user profile directory. However, on Windows, system-level.NET installations (those living in C:\Program Files\dotnet installed via Visual Studio, Winget, or standalone installers) are ubiquitous.

Chet Husk notes in the launch announcement that dotnetup does not currently manage system-level Windows installations, though adding support for Windows admin installers is explicitly on the roadmap for a future release. In the meantime, Microsoft’s dotnet/sdk documentation notes that dotnetup setup can detect SDKs and runtimes in the system-managed.NET directory and install matching native-architecture versions into the dotnetup-managed user directory. Furthermore, when performing runtime updates on Windows, dotnetup properly accounts for Windows Desktop runtime requirements alongside the core .NET and ASP.NET Core runtimes.


Getting Started: Installation and Environment Modes​

Microsoft provides one-line bootstrap scripts for macOS, Linux, and PowerShell (pwsh) that download the dotnetup binary.

  • PowerShell (pwsh):
    irm [url]https://aka.ms/dotnetup/get-dotnetup.ps1[/url] | iex
  • Linux (Bash):
    curl -fsSL [url]https://aka.ms/dotnetup/get-dotnetup.sh[/url] | bash
  • macOS (Zsh):
    curl -fsSL [url]https://aka.ms/dotnetup/get-dotnetup.sh[/url] | zsh

Once the binary is on your machine, you have three distinct ways to initialize and expose the managed .NET toolchain, as detailed in Microsoft Learn’s dotnetup overview:

1. Interactive Initialization (Recommended for Workstations)​

Running the standard initialization command launches an interactive prompt:

dotnetup init

By default, init suggests installing the latest stable SDK channel and configuring Terminal Mode, which updates your shell profile so that dotnet is immediately available in your terminal sessions.

2. Non-Interactive Setup (Ideal for Scripts and CI)​

If you are provisioning a machine or container via an automated script and cannot answer interactive prompts, you can install a channel and configure your shell profile in two discrete steps:

Code:
dotnetup install latest
dotnetup env set shell

(Note: According to the Microsoft Learn CLI reference, invoking a bare dotnetup command with no arguments also runs the SDK install workflow.) After setting the shell environment, you either run the outputted shell-specific refresh command or open a new terminal session.

3. Zero-Profile-Modification Mode (env set none)​

What if you are testing dotnetup on a machine where you already have a delicately balanced system-wide .NET installation and you refuse to let a preview tool touch your PATH or shell profile? Microsoft built an escape hatch specifically for this scenario:

Code:
dotnetup install latest
dotnetup env set none

In this mode, dotnetup is placed on your PATH, but the managed dotnet binary is not. Existing system-wide .NET installations remain your default. Whenever you want to invoke the dotnetup-managed toolchain, you use the forwarding command:

Code:
dotnetup dotnet --version
dotnetup dotnet build

To inspect your environment at any time, run:

dotnetup list

If you are building automation scripts around dotnetup and need to parse the state programmatically, the tool supports JSON output:

dotnetup list --format json

Summary: Whether you want an interactive setup, an unattended CI bootstrap, or a quarantined installation that never touches your default PATH, dotnetup provides explicit environment modes to match your risk tolerance.


Tracking Intent: Channels, Pinning, and Monthly Servicing​

The standout architectural feature of dotnetup is how it handles monthly.NET servicing updates (Patch Tuesday for developers). Instead of forcing you to manually download 10.0.4 when 10.0.3 is superseded—and then manually delete 10.0.3—dotnetup tracks your intent.

When you request an installation, you can specify a rolling channel or an exact version:

  • latest: Tracks the latest stable SDK, including jumping to new major releases when they ship.
  • 10.0: Tracks servicing and feature updates strictly within the.NET 10 channel.
  • 10.0.3: Pins an exact version. It will never advance during an update.

When Microsoft ships its monthly servicing updates, refreshing your local development machine requires a single command:

dotnetup update

Granular Update Scopes and Garbage Collection​

According to Microsoft Learn’s update documentation, dotnetup gives you fine-grained control over what gets refreshed:

  • dotnetup update: Updates all tracked SDK and runtime requirements across your manifest.
  • dotnetup sdk update: Updates tracked SDK requirements only.
  • dotnetup sdk update --all: Updates both SDK and runtime requirements.
  • dotnetup runtime update: Updates tracked runtime requirements only (covering core.NET, ASP.NET Core, and, on Windows, Windows Desktop runtimes).
  • dotnetup update --install-path D:\tools\dotnet: Restricts the update operation to a specific installation root.

If you only need a specific runtime flavor—such as refreshing the ASP.NET Core runtime on.NET 10 without pulling down the entire SDK—you can target it directly:

dotnetup runtime install [email][email protected][/email]

Crucially, after a successful update, dotnetup automatically runs garbage collection. Any previously installed SDK or runtime component that is no longer referenced by an active channel or an exact-version requirement is automatically removed from your disk. If another tracked channel or pinned requirement still depends on an older version, dotnetup leaves it intact. Furthermore, each requirement is processed independently during an update; if one channel fails to resolve or download, dotnetup continues processing the remaining requirements before reporting the failure at the end.

To remove a tracked channel or pinned version entirely, run the corresponding sdk uninstall or runtime uninstall command, which removes the requirement and cleans up the orphaned binaries. And to update the dotnetup binary itself as new preview features ship, run:

dotnetup self update


Testing Previews, Daily Builds, and Managing global.json​

For library authors and enterprise teams preparing for upcoming.NET releases (such as.NET 11), testing preview and nightly builds has historically been a chore. dotnetup treats pre-release channels as first-class citizens that sit cleanly alongside your stable toolchains:

  • Install the latest preview SDK:
    dotnetup sdk install preview
  • Install the latest daily (nightly) runtime build to verify a bug fix:
    dotnetup runtime install daily
  • Install an entire daily SDK toolchain:
    dotnetup sdk install daily

The global.json Workflow (and a Important Caveat)​

In professional.NET repositories, global.json is the source of truth that keeps every developer and build agent on a deterministic SDK version. dotnetup integrates directly with global.json files, allowing you to navigate into a repository and install the exact SDK required by the nearest global.json.

However, Microsoft Learn’s documentation highlights two critical operational details that development teams must keep in mind:

  1. Updating does not rewrite global.json by default: When dotnetup tracks an SDK requirement sourced from a repository’s global.json, running dotnetup sdk update will install the newly resolved SDK version on disk, but it will not edit your repository's global.json file unless you explicitly pass the --update-global-json flag:
    dotnetup sdk update --update-global-json
  2. Syncing manual global.json edits: Because dotnetup update processes stored requirements before garbage collection refreshes changed global.json sources, if you manually edit a repository’s global.json file, you should run dotnetup sdk install inside that repository before running an update. Otherwise, your first update pass may still process the old cached requirement.

Critical Realities: Security, CI/CD, and Roadmap​

As exciting as a unified.NET version manager is, veteran IT administrators and lead developers know to read the fine print. Based on Microsoft’s announcement and documentation, here are the practical boundaries of dotnetup today:

  • Updating Your Machine Does Not Patch Shipped Apps: Chet Husk explicitly reminds developers of a fundamental.NET deployment rule: updating the SDKs and runtimes on your development machine (or build server) helps you build and test against monthly security patches, but it does not patch applications you have already deployed. Specifically, if you publish self-contained applications (which bundle the.NET runtime alongside your executable), you must rebuild, republish, and redeploy those applications after running dotnetup update for the security fixes to reach production.
  • Performance and Validation Claims: Microsoft states that dotnetup provides signature and checksum validation of installed SDKs and runtimes, as well as faster installation of multiple components compared to dotnet-install scripts. While welcome improvements, Microsoft has not yet published formal benchmark numbers or architectural security whitepapers detailing the validation pipeline in the launch post.
  • Preview Status: dotnetup is currently in public preview. Microsoft Learn cautions that commands, options, and behaviors may change before general availability, and the formal support lifecycle for the GA release is not yet finalized.
  • CI/CD and AI Agent Roadmap: Because dotnetup requires zero root/admin privileges and runs non-interactively, you can use it in containerized or ephemeral CI workers right now. Looking ahead, Microsoft plans to integrate dotnetup directly into official GitHub Actions and Azure DevOps tasks—replacing the underlying dotnet-install script implementations those pipelines rely on today. Additionally, the team plans to build out "agent skills" so that automated AI coding assistants reach for dotnetup naturally when configuring.NET environments.

Bottom Line for Windows and.NET Developers​

For developers who split their time between Windows, WSL2, macOS, and Linux containers—or anyone tired of hunting down orphaned SDK folders eating up gigabytes of SSD space—dotnetup is one of the most practical quality-of-life improvements the.NET CLI team has delivered in years. While Windows developers who rely heavily on Visual Studio's system-wide Program Files installations will need to wait for future roadmap updates to manage those elevated installs via dotnetup, its user-local mode (dotnetup env set none or shell profile integration) is ready for testing today.

Have you spun up dotnetup in your local PowerShell profile or WSL2 instance yet, or are you waiting for system-wide Windows installer support to land? Let us know how it handles your global.json workflows in the WindowsForum community discussions!

 

References

  1. Introducing dotnetup: A toolchain manager for .NET .NET Blog 2026-10-15T16:50:00+00:00
  2. (Awareness) .NET SDK `main` branch is using `dotnetup` to `build` · Issue #54601 · dotnet/sdk github.com
  3. sdk/documentation/general/dotnetup at release/dnup · dotnet/sdk github.com