Building .NET applications
DeployHQ ships seven .NET SDK series in the build environment, selectable per project. This article covers which versions are available, how to pick one, and how global.json is handled at build time.
What's new in June 2026
- .NET 10.0 LTS is now available and is the default for new projects, and for projects set to Use the latest stable version. Microsoft supports .NET 10.0 until 2028-11-14.
- All other SDK series have been updated to their latest patches. The pinned versions are now real SDK versions (previously they were runtime versions, which weren't quite the right thing to advertise).
global.jsonat the repository root is now respected.DOTNET_CLI_TELEMETRY_OPTOUT=1is set on every build, so the .NET CLI no longer sends usage data to Microsoft.DOTNET_ROOTis now exported correctly, so child processes spawned bydotnet tool install,dotnet publish, MSBuild, and similar tools find the SDK reliably.
Choosing a .NET SDK version
Head to your project's Build Configuration and look for the Language Versions section. The .NET dropdown offers:
- Use the latest stable version (.NET 10.0) -- recommended; your project follows whatever we ship as the latest stable series.
- .NET 10.0 (currently 10.0.301)
- .NET 9.0 (currently 9.0.315)
- .NET 8.0 (currently 8.0.422)
- .NET 7.0 (currently 7.0.410)
- .NET 6.0 (currently 6.0.428)
- .NET 5.0 (currently 5.0.408)
- .NET 3.1 (currently 3.1.426)
The "(currently x.y.z)" suffix is the exact SDK patch version installed for that series. We bump these regularly -- see the API language versions reference for the authoritative list.
The selected version is on $PATH during your build, so dotnet --version, dotnet restore, dotnet build, and dotnet publish all work without further configuration.
End-of-life series
3.1, 5.0, 6.0, and 7.0 are still available for backward compatibility, but Microsoft no longer issues security updates for them. We recommend migrating to 8.0 or above when you're able to.
Using global.json to pin a version from your repo
If your repository has a global.json at the root with an sdk.version field, the build environment reads it and selects the matching SDK series automatically. This means you don't need to set DOTNET_VERSION manually if your repo already pins the version.
{
"sdk": {
"version": "8.0.422"
}
}
JSONC is supported, so both // line comments and /* block comments */ in global.json are handled correctly, matching Microsoft's spec.
Resolution order
When more than one version source is present, DeployHQ picks in this order:
- Explicit
DOTNET_VERSIONenvironment variable sdk.versionfromglobal.json- The default (.NET 10.0)
What if my pinned version isn't installed?
If your global.json pins a series we don't ship (for example, an unreleased version), the build logs a warning and falls back to .NET 10.0 instead of failing with an opaque "SDK not found" error.
Why dotnet --version may not match the pin exactly
We pin the series based on your global.json, and the build then runs against whichever SDK patch we ship for that series. For example, if your global.json says 8.0.100 and we ship 8.0.422, the build uses 8.0.422.
This is necessary because the .NET CLI's default rollForward: latestPatch policy only rolls forward within the same feature band -- it won't cross feature-band boundaries. The third digit of a .NET 8 patch encodes the feature band: 8.0.100 is in the 1xx band, 8.0.422 is in the 4xx band, so pinning 8.0.100 against an installed 8.0.422 would refuse to run. We resolve this by temporarily moving global.json aside (renamed to global.json.original) for the duration of the build, then restoring it on completion, so the SDK we ship is the one your build commands actually use.
If you need an exact patch that we don't ship, you can install it yourself from your build commands using Microsoft's dotnet-install.sh script.
Related
- Configure custom build environments -- general guide to language version configuration
- API language versions reference -- programmatic configuration and the canonical list of installed patches