.NET SDK and project files
What the SDK actually installs, how an SDK-style project file describes a build, and how to pin a repository to one SDK version.
SDK versus runtime
The runtime is what executes a compiled .NET application. The SDK is a superset: it bundles a runtime plus the C# compiler, MSBuild and the dotnet CLI. A build machine needs the SDK; a production server usually needs only the runtime.
| Command | Answers the question |
|---|---|
dotnet --info | Everything at once: SDK version, runtimes, base path, OS and RID |
dotnet --list-sdks | Which SDKs are installed and where |
dotnet --list-runtimes | Which runtimes are installed, per component (Microsoft.NETCore.App, Microsoft.AspNetCore.App) |
dotnet --version | The SDK that this directory resolves to, honouring global.json |
dotnet --list-sdks
# 8.0.404 [C:/Program Files/dotnet/sdk]
# 9.0.100 [C:/Program Files/dotnet/sdk]
dotnet --list-runtimes
# Microsoft.AspNetCore.App 8.0.11 [C:/Program Files/dotnet/shared/Microsoft.AspNetCore.App]
# Microsoft.NETCore.App 8.0.11 [C:/Program Files/dotnet/shared/Microsoft.NETCore.App]Runtimes are installed side by side and selected at launch: a framework-dependent app built for net8.0 runs on the newest compatible 8.x runtime on the machine, so patching the runtime fixes the app without rebuilding it.
The project file
An SDK-style project is a single XML file that declares intent, not file lists. Source files are globbed automatically, so adding a .cs file needs no edit. The Sdk attribute decides which default props and targets are imported — Microsoft.NET.Sdk for console and library code, Microsoft.NET.Sdk.Web for ASP.NET Core.
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Serilog.Sinks.Console" Version="6.0.0" />
<ProjectReference Include="../Core/Core.csproj" />
</ItemGroup>
</Project>TargetFrameworkselects the API surface. UseTargetFrameworks(plural, semicolon-separated) to multi-target.PackageReferencerestores NuGet packages per project; versions belong in a central file once you have more than a handful of projects.Nullableturns on the nullable reference type analysis — treat it as mandatory on new code.Directory.Build.propsin the repository root applies shared properties to every project beneath it, which is where common settings belong.
Keep the project file declarative. Logic in MSBuild targets is hard to debug and invisible to a reader opening the app for the first time.
Pinning the SDK version
By default the CLI picks the highest installed SDK. On a team that means one developer silently builds with a different compiler than CI. A global.json at the repository root removes that ambiguity.
{
"sdk": {
"version": "8.0.404",
"rollForward": "latestFeature"
}
}| rollForward value | Behaviour when the exact version is missing |
|---|---|
disable | Fail immediately — strictest, good for CI |
latestPatch | Accept a newer patch of the same feature band |
latestFeature | Accept a newer feature band within the same major version |
latestMajor | Accept the newest SDK available |
global.json and let CI fail loudly if the SDK is absent. A build that quietly uses a newer SDK produces different behaviour than the one you tested, and that difference surfaces in production rather than in the pipeline.FAQ
Do I need both the SDK and a separate runtime?
What is a RID?
win-x64, linux-arm64 or osx-x64. It names the OS and CPU architecture a build targets, and only matters once you publish self-contained or native.Related
The dotnet CLI workflow Publishing and deployment
Last refreshed 2026-09-18.