How to Install .NET Framework and .NET on Windows Server 2025

Windows Server 2025 supports both the legacy .NET Framework and the modern cross-platform .NET (formerly .NET Core) runtime side by side. Understanding the difference between these two ecosystems is essential for any server administrator managing Windows-based applications. .NET Framework is a Windows-only runtime that ships with the operating system and powers classic ASP.NET Web Forms, WCF, and WPF applications. Modern .NET (versions 5 through 9 and beyond) is the open-source, cross-platform successor that supports high-performance APIs, microservices, and containerized workloads. This tutorial walks through checking what is already installed, installing .NET Framework 4.8, installing the .NET 8 SDK and Runtime, managing multiple side-by-side versions, and configuring environment variables correctly on Windows Server 2025.

Prerequisites

  • Windows Server 2025 with administrative privileges
  • PowerShell 5.1 or PowerShell 7.x (recommended)
  • Internet access or access to Windows Server Update Services (WSUS) for package downloads
  • Windows Remote Management (WinRM) enabled if managing remotely

Step 1: Check Currently Installed .NET Framework Versions

Before installing anything, audit what versions of .NET Framework are already present. Windows Server 2025 ships with .NET Framework 4.8.1 included as a built-in Windows feature. You can verify this using the Get-WindowsFeature cmdlet and by inspecting the registry.

# List all .NET Framework-related Windows Features
Get-WindowsFeature -Name "NET-Framework-*" | Format-Table Name, DisplayName, InstallState

# Check the .NET Framework version via the registry
$regKey = "HKLM:SOFTWAREMicrosoftNET Framework SetupNDPv4Full"
$release = (Get-ItemProperty -Path $regKey -Name Release).Release
Write-Host "Release DWORD: $release"

# Map release number to version string
switch ($release) {
    { $_ -ge 533320 } { Write-Host ".NET Framework 4.8.1 or later" }
    { $_ -ge 528040 } { Write-Host ".NET Framework 4.8" }
    { $_ -ge 461808 } { Write-Host ".NET Framework 4.7.2" }
    default           { Write-Host "Unknown version: $release" }
}

# Check the runtime directory (legacy approach)
[System.Runtime.InteropServices.RuntimeEnvironment]::GetRuntimeDirectory()

The Get-WindowsFeature output will show features such as NET-Framework-45-Features, NET-Framework-45-Core, and NET-WCF-Services45. If the InstallState column shows Installed, that component is already active.

Step 2: Install .NET Framework 4.8 via Windows Feature

If .NET Framework 4.8 is not yet installed, you can add it through Server Manager or PowerShell. On Windows Server 2025, this is typically a Windows Feature install rather than a standalone package download.

# Install .NET Framework 4.8 and all sub-features
Install-WindowsFeature -Name NET-Framework-45-Features -IncludeAllSubFeature -IncludeManagementTools

# Verify the installation completed successfully
$result = Install-WindowsFeature -Name NET-Framework-45-Core -IncludeManagementTools
if ($result.Success) {
    Write-Host "Installation succeeded. Restart required: $($result.RestartNeeded)"
} else {
    Write-Error "Installation failed."
}

# Optionally install WCF Services support
Install-WindowsFeature -Name NET-WCF-Services45 -IncludeAllSubFeature

After installation, a restart may be required depending on the current state of the server. Check the RestartNeeded property of the result object to determine whether a reboot is necessary before proceeding.

Step 3: Install .NET 8 SDK Using winget

The modern .NET runtime and SDK are not Windows Features — they are standalone Microsoft packages. The easiest method on Windows Server 2025 is to use winget, the Windows Package Manager, which is available by default.

# Ensure winget is available
winget --version

# Search for available .NET SDK packages
winget search Microsoft.DotNet.SDK

# Install the .NET 8 SDK (includes the runtime)
winget install Microsoft.DotNet.SDK.8 --silent --accept-package-agreements --accept-source-agreements

# Install only the .NET 8 Runtime (for hosting, not development)
winget install Microsoft.DotNet.Runtime.8 --silent --accept-package-agreements --accept-source-agreements

# Install the ASP.NET Core Runtime separately if needed
winget install Microsoft.DotNet.AspNetCore.8 --silent --accept-package-agreements --accept-source-agreements

Step 4: Install .NET 8 SDK via Direct Download

In environments without internet access or where winget is restricted, download the installer directly from Microsoft and run it silently.

# Download the .NET 8 SDK installer
$sdkUrl = "https://dot.net/v1/dotnet-install.ps1"
$installScript = "$env:TEMPdotnet-install.ps1"

# Use the official dotnet-install.ps1 script (recommended for server environments)
Invoke-WebRequest -Uri $sdkUrl -OutFile $installScript -UseBasicParsing

# Install .NET 8 SDK to default location
& $installScript -Channel 8.0 -Runtime dotnet

# Install ASP.NET Core Runtime
& $installScript -Channel 8.0 -Runtime aspnetcore

# Install the full SDK (includes build tools)
& $installScript -Channel 8.0

# Specify a custom install path
& $installScript -Channel 8.0 -InstallDir "C:Program Filesdotnet"

Step 5: Verify Installed SDKs and Runtimes

After installation, verify that the SDK and runtime are correctly recognized by the dotnet CLI. These commands list all versions installed in the default discovery paths.

# Verify dotnet CLI is in PATH
dotnet --version

# List all installed SDKs
dotnet --list-sdks

# List all installed runtimes
dotnet --list-runtimes

# Show full environment information
dotnet --info

# Expected output example:
# .NET SDKs installed:
#   8.0.403 [C:Program Filesdotnetsdk]
#
# .NET runtimes installed:
#   Microsoft.AspNetCore.App 8.0.10 [C:Program FilesdotnetsharedMicrosoft.AspNetCore.App]
#   Microsoft.NETCore.App 8.0.10 [C:Program FilesdotnetsharedMicrosoft.NETCore.App]

Step 6: Configure Environment Variables

Correct environment variable configuration ensures that the right version of .NET is used when running applications and that side-by-side installations do not conflict.

# Add the default dotnet install path to the system PATH permanently
$dotnetPath = "C:Program Filesdotnet"
$currentPath = [Environment]::GetEnvironmentVariable("PATH", "Machine")

if ($currentPath -notlike "*$dotnetPath*") {
    [Environment]::SetEnvironmentVariable(
        "PATH",
        "$currentPath;$dotnetPath",
        "Machine"
    )
    Write-Host "Added $dotnetPath to system PATH."
}

# Set DOTNET_ROOT so applications can find the runtime
[Environment]::SetEnvironmentVariable("DOTNET_ROOT", $dotnetPath, "Machine")

# Disable telemetry (recommended for server environments)
[Environment]::SetEnvironmentVariable("DOTNET_CLI_TELEMETRY_OPTOUT", "1", "Machine")

# Set roll-forward policy for patch versions
[Environment]::SetEnvironmentVariable("DOTNET_ROLL_FORWARD", "Minor", "Machine")

# Reload the current session's environment
$env:PATH = [Environment]::GetEnvironmentVariable("PATH", "Machine")

Step 7: Managing Multiple .NET Versions Side by Side

Windows Server 2025 supports running multiple .NET versions simultaneously. Applications specify their target runtime via a global.json file or a runtimeconfig.json, so different applications can use .NET 6, .NET 8, and .NET 9 on the same machine without conflict.

# Install .NET 6 LTS alongside .NET 8
& $installScript -Channel 6.0

# Install .NET 9 (current)
& $installScript -Channel 9.0

# Pin a specific SDK version for a project using global.json
$globalJson = @{
    sdk = @{
        version      = "8.0.403"
        rollForward  = "latestPatch"
    }
}
$globalJson | ConvertTo-Json | Set-Content -Path "C:AppsMyProjectglobal.json"

# Verify which SDK is selected in a directory context
Set-Location "C:AppsMyProject"
dotnet --version
# Should output: 8.0.403

# List all runtimes to confirm side-by-side installation
dotnet --list-runtimes

Each application can target its required runtime independently. The .NET SDK selects the appropriate runtime based on the application’s runtimeconfig.json generated at build time. There is no need to uninstall older versions to install newer ones — Microsoft explicitly supports and recommends keeping multiple versions installed for compatibility.

Conclusion

Windows Server 2025 makes it straightforward to run both .NET Framework and modern .NET applications in the same environment. .NET Framework 4.8.1 is included as a Windows Feature and can be enabled via PowerShell in seconds, while modern .NET runtimes and SDKs install as standalone packages through winget or the official install script. Using dotnet --list-sdks and dotnet --list-runtimes gives you a clear inventory of everything installed, and environment variables like DOTNET_ROOT and DOTNET_ROLL_FORWARD provide fine-grained control over runtime discovery and version selection. With proper configuration, a single Windows Server 2025 instance can host legacy .NET Framework 4.x applications in IIS alongside multiple generations of ASP.NET Core, console, and worker service applications without interference.