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.
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.jsonfile 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
- 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.
- 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.
- Do you juggle projects on different .NET versions? SDKs install side by side. Keep them all and pin per repository with
global.jsoninstead of uninstalling. - 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.
- 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-sdksin the pipeline first. If the preinstalled SDK matches yourglobal.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 newanddotnet buildfail; 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
dotnetCLI. A runtime alone cannot build anything. - Install through winget, Homebrew, or apt, then verify with
dotnet --infoand restart your terminal. dotnet new consoleplusdotnet runis 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.jsonpins 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
