Installing .NET SDK and Setting Up Your First Project

Learn how to install .NET SDK on Windows, macOS, or Linux, verify it with dotnet --info, then build your first console and web API projects in minutes.

Executive Summary: To write and run C# you need exactly one download: the .NET SDK. When you install .NET SDK you get the compiler, the runtimes, NuGet package management, and the dotnet CLI in a single bundle, which is why installing only a runtime is the most common setup mistake.

A new contributor clones your repository, opens a terminal, runs dotnet run, and gets a wall of confusion: zsh: command not found: dotnet on macOS, or a runtime-only machine where dotnet build fails with “It was not possible to find any installed .NET SDKs”. Both failures share one root cause: the .NET SDK, the part of .NET that compiles code, was never installed. This guide fixes that, step by step, on every major operating system.

To install .NET SDK means to set up the .NET Software Development Kit: the C# compiler (Roslyn), the MSBuild build engine, the test and publish tooling, both .NET runtimes, and the dotnet CLI, packaged together. A runtime on its own only executes applications that someone else already compiled. The SDK is what turns your source code into those applications.

The examples assume .NET SDK 10.0, the current Long Term Support release, with C# 14. Every command works in PowerShell on Windows, zsh on macOS, and bash on Ubuntu. You need a terminal and about 20 minutes; no prior C# or .NET experience is required. If you are weighing which .NET release to target, read how .NET’s LTS and STS releases differ. The short version: start new projects on the current LTS.

After setting up more build machines than I can count, my advice is simple: install the SDK with a package manager rather than a downloaded file, and treat the dotnet CLI as the source of truth for building, running, and testing. Every editor and IDE wraps these same commands, so when something breaks, the CLI is the layer you debug.

SDK vs runtime: what you are actually downloading

The official download page offers three families of installers, and picking the wrong one wastes an afternoon. The table shows what each can do.

Download Compiles code Runs console apps Runs ASP.NET Core apps Typical use
.NET SDK Yes Yes Yes Development machines, CI build agents
.NET Runtime No Yes No Servers running published console or worker apps
ASP.NET Core Runtime No Yes Yes Servers running published web apps

The ASP.NET Core runtime includes the base .NET runtime, so it runs both kinds of application. The rule is simple: if the machine ever compiles code, install the SDK; it contains everything else. Servers that only run published apps get the matching runtime, and nothing more.

+--------------------------------------------------+
|                    dotnet CLI                     |
|    new   build   run   test   publish   format    |
+--------------------------------------------------+
|        SDK tooling: MSBuild, Roslyn, NuGet        |
+--------------------------------------------------+
|     Runtimes: .NET and ASP.NET Core (bundled)     |
+--------------------------------------------------+

How to install .NET SDK on each operating system

Windows

Use winget, the package manager built into Windows:

winget install Microsoft.DotNet.SDK.10

The package name follows the pattern Microsoft.DotNet.SDK.<major>, so older targets work the same way (Microsoft.DotNet.SDK.8). If you develop with Visual Studio, its installer manages SDK versions for you and the CLI picks them up automatically. The official download page carries the standalone installers when winget is unavailable.

macOS

Homebrew is the fastest path:

brew install --cask dotnet-sdk

The cask tracks the newest release, and version-specific casks can lag behind by days. If you need one exact version, use the .pkg installer from the download page instead. Either way, open a new terminal afterward so PATH picks up the install.

Linux

Ubuntu, Debian, and Fedora ship .NET in their standard package feeds, so one command is enough on Ubuntu:

sudo apt-get update && sudo apt-get install -y dotnet-sdk-10.0

Fedora uses sudo dnf install -y dotnet-sdk-10.0. For other distributions, follow the official install guide, which covers each package manager and the supported versions per distribution.

Verify the install

Run the one command that settles every setup question:

dotnet --info

The output is long; the parts that matter look like this (trimmed, and your patch numbers will be newer):

.NET SDK:
  Version:           10.0.100

Runtime Environment:
  OS Name:     macOS
  OS Version:  15.0
  OS Platform: Darwin

.NET SDKs installed:
  10.0.100 [/usr/local/share/dotnet/sdk]

.NET runtimes installed:
  Microsoft.AspNetCore.App 10.0.0 [/usr/local/share/dotnet/shared/Microsoft.AspNetCore.App]
  Microsoft.NETCore.App 10.0.0 [/usr/local/share/dotnet/shared/Microsoft.NETCore.App]

The last three digits of the SDK version are its feature band. 10.0.100 is the launch band, later bands such as 10.0.200 add tooling features, and monthly patches bump the final digits. The runtimes carry the security patches. Two shorter commands help when you script around this:

dotnet --list-sdks
dotnet --list-runtimes

Your first console project

With the SDK confirmed, create a project and run it, three commands total:

dotnet new console -o HelloDotnet
cd HelloDotnet
dotnet run

The output is the whole point:

Hello, World!

Open Program.cs and you will find one line:

Console.WriteLine("Hello, World!");

That is a complete program. Modern C# supports top-level statements: no class declaration, no Main method, no ceremony. The compiler generates the entry point, and you add explicit classes and namespaces only when the program grows to need them. The project file, HelloDotnet.csproj, is nearly as small:

<Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroup>
    <OutputType>Exe</OutputType>
    <TargetFramework>net10.0</TargetFramework>
    <Nullable>enable</Nullable>
    <ImplicitUsings>enable</ImplicitUsings>
  </PropertyGroup>

</Project>

Four properties carry the weight. OutputType says this is an executable, TargetFramework pins the runtime version, Nullable turns on nullable reference types (the compiler warns you about possible nulls). ImplicitUsings adds the common using directives so the one-line program needs none. Build outputs land in bin/ and obj/. Both are disposable and should never be committed, and dotnet new gitignore writes a .gitignore that covers them.

Build, run, and test from the terminal

Four commands cover the daily loop:

dotnet build                  # compile the project
dotnet run                    # build, then execute
dotnet run -c Release         # execute the optimized build
dotnet publish -c Release -o ./publish   # produce deployable output

Two behaviors matter more than the commands themselves. First, restore is implicit: any command that needs NuGet packages restores them first, so you almost never run dotnet restore by hand (CI systems sometimes pin it explicitly for caching). Second, the default configuration is Debug; Release enables the optimizations you measure and ship. dotnet test runs your test suite; the guide on unit testing with xUnit builds a full test project from scratch.

Solutions and multiple projects

Real applications split into projects quickly: the console app, a class library for domain logic, a test project. A solution file groups them so one command builds everything in dependency order.

dotnet new sln -n PlaybookDemo
dotnet new classlib -o PlaybookDemo.Core
dotnet new console -o PlaybookDemo.App
dotnet sln add PlaybookDemo.Core PlaybookDemo.App
dotnet add PlaybookDemo.App reference PlaybookDemo.Core

dotnet build at the solution root now compiles both projects, and PlaybookDemo.App can use the types in PlaybookDemo.Core because of the project reference. The classic .sln format is still the default. The SDK also supports the newer XML-based .slnx format, and support evolves by SDK version. So check dotnet sln --help for your installed SDK before switching formats.

A first look at a web API project

The same CLI creates web applications:

dotnet new webapi -o TaskApi
cd TaskApi
dotnet run

Since .NET 8, the webapi template defaults to the minimal API style and offers a -controllers option for the controller style. The terminal prints the listening address:

Now listening on: http://localhost:5092

The port comes from Properties/launchSettings.json and differs per project; stop the server with Ctrl+C. Routing, model binding, and validation are their own topic, covered in depth in building a Web API with ASP.NET Core. The point here is smaller: the SDK runs web apps exactly like console apps, one dotnet command, no separate web server install.

Choosing an editor

The CLI works with any editor, so pick on comfort, not necessity. The common choices:

Editor Cost Notes
VS Code with the C# Dev Kit Free Lightweight, terminal-centric, works on all platforms
Visual Studio Free (Community) or paid Windows, full IDE, manages SDK installs itself
JetBrains Rider Paid, free for non-commercial use Cross-platform IDE with strong refactoring

Whichever you choose, confirm that a bare terminal can build and run your project first. That habit pays off the first time an IDE-specific cache breaks and you need to prove the code itself is fine.

How real systems do this

Production teams treat the SDK as pinned infrastructure rather than a machine-global accident, and three patterns repeat across serious codebases.

  • Pinned SDK versions. A global.json file in the repository root locks the SDK feature band for everyone who clones the repo:
{
  "sdk": {
    "version": "10.0.100",
    "rollForward": "latestFeature"
  }
}

With latestFeature, the build takes patch updates inside the pinned feature band and refuses to drift onto a different one. The global.json reference documents every roll-forward mode and how version resolution works.

  • CI installs a pinned SDK. Hosted runners come with SDKs preinstalled. But pipelines pin an exact version with a setup step so a runner image update can never change your compiler overnight. The guide on CI/CD pipelines for .NET projects shows the restore, build, and test stages end to end.
  • Containers separate build from run. Container builds compile inside an SDK image and ship a smaller runtime image, so the SDK never reaches production. The containerization guide later in this series covers the multi-stage pattern for .NET apps.

A mistake I have seen in production is a deployment script that ran dotnet publish on an application server that had only the ASP.NET Core runtime installed. The build failed with “It was not possible to find any installed .NET SDKs”, and the near-fix was installing the full SDK on that server. The correct fix was publishing artifacts in CI and copying them over. Production machines run applications; they should never compile them.

Decision framework: which install path fits you

  1. Will this machine compile code? If yes, install the SDK; it contains both runtimes. If it only runs already-built applications, install the one matching runtime.
  2. Do you already use a package manager? winget, Homebrew, and apt keep .NET updated alongside the rest of the system. Manual installers need manual maintenance.
  3. Do you juggle projects on different .NET versions? SDKs install side by side. Keep them all and pin per repository with global.json instead of uninstalling.
  4. Is this a build server or CI runner? Pin the SDK version in the pipeline and cache it; never rely on whatever the runner image happens to carry.
  5. Do you use Visual Studio on Windows? Let its installer manage SDK versions. The CLI picks them up automatically either way.

When NOT to install the .NET SDK manually

  • Production app servers. Install only the runtime matching the app type, or ship container images that already include it. A server that can build code invites quick fixes that then break during the next deployment.
  • Hosted CI runners. Check dotnet --list-sdks in the pipeline first. If the preinstalled SDK matches your global.json, skip the install step and save minutes per run.
  • Dev container workflows. If your team standardizes on Dev Containers, the SDK belongs in the container image, not the laptop. The host needs only the editor and a container runtime.

Common mistakes

  • Installing a runtime instead of the SDK. Published apps run, but dotnet new and dotnet build fail; development machines need the SDK.
  • Skipping the terminal restart. The installer updates PATH, but shells opened before the install do not see the change. “command not found” right after a successful install means open a new terminal.
  • Uninstalling an older SDK after upgrading. Side-by-side installation is the design; removing one breaks every older project on the machine. Pin per project instead.
  • Committing bin and obj. Build outputs churn in every commit and cause merge conflicts; generate a .gitignore with dotnet new gitignore.
  • Running dotnet tool install with sudo on Linux. Global tools install per user under ~/.dotnet/tools. A root-installed tool is invisible to your normal account.
  • Installing the SDK on production servers to fix a deploy script. It hides the real problem: deployment ships artifacts, it does not compile them.

Key takeaways

  • The .NET SDK is the single development bundle: compiler, runtimes, NuGet tooling, and the dotnet CLI. A runtime alone cannot build anything.
  • Install through winget, Homebrew, or apt, then verify with dotnet --info and restart your terminal.
  • dotnet new console plus dotnet run is a complete build and run cycle with zero configuration.
  • Restore is implicit: build, run, test, and publish all restore NuGet packages first.
  • Solutions group projects, project references connect them, and global.json pins the SDK version per repository.
  • Publish artifacts in CI and run them on runtimes in production; the SDK belongs on developer machines and build agents.

FAQ

How do I install .NET SDK on Windows, macOS, and Linux?

On Windows run winget install Microsoft.DotNet.SDK.10, on macOS run brew install --cask dotnet-sdk, and on Ubuntu run sudo apt-get install -y dotnet-sdk-10.0. All three pull from Microsoft’s official packages. Verify with dotnet --info and open a new terminal so the PATH change takes effect.

What is the difference between the .NET SDK and the .NET runtime?

The SDK contains the compilers and build tools that turn source code into applications, and it includes the runtimes. A runtime only executes applications that are already compiled. Development machines need the SDK; servers that run published apps need only the matching runtime.

Can I install multiple .NET SDK versions side by side?

Yes, and that is the supported setup. Each SDK installs into its own directory, and dotnet --list-sdks shows them all. A global.json in a repository root pins which one each project uses, and its rollForward setting controls how newer patches are selected.

How do I check which .NET version is installed?

Run dotnet --list-sdks for installed SDKs and dotnet --list-runtimes for runtimes. For diagnosis, dotnet --info adds operating system details and shows exactly what the dotnet command resolves to, which makes it the command to reach for during setup problems.

Which .NET version should I install for a new project?

Start on the current LTS release, which at the time of writing is .NET 10, supported until November 2028. STS releases ship every year for teams that need the newest features and can upgrade on a short cycle. The guide on how .NET’s LTS and STS releases differ covers the full calendar and the upgrade path.

Make the CLI your source of truth

Everything built on top of .NET, Visual Studio, Rider, VS Code, your CI pipelines, wraps the same dotnet commands. When you can build, run, and test from a bare terminal, every environment you touch later makes sense. The natural next step is the language itself: C# basics, from types and variables to control flow, and you can find every guide in this series on the .NET series index. Keep one rule of thumb close: if it works in the dotnet CLI, it works everywhere. If it only works in your IDE, it works nowhere.

Last updated on 12 October 2026

Share this article

Leave a Reply

Your email address will not be published. Required fields are marked *