Describe the bug
The official .NET SDK image sets DOTNET_ROLL_FORWARD=Major globally as a PowerShell workaround. This affects unrelated .NET workloads such as dotnet test and can cause older TFM tests, e.g. net8.0, to execute on the .NET 10 runtime without the target runtime being installed.
This can make CI compatibility tests look successful even though the actual target runtime was not tested.
Which .NET image(s) are you using?
mcr.microsoft.com/dotnet/sdk:10.0
Steps to reproduce
-
Use a .NET SDK container image that contains only the latest runtime, for example a .NET 10 SDK image.
-
Check the installed runtimes and environment:
dotnet --list-runtimes
printenv | grep DOTNET_ROLL_FORWARD
Observed in the SDK container:
DOTNET_ROLL_FORWARD=Major
Microsoft.NETCore.App 10.x is installed
Microsoft.NETCore.App 8.x is not installed
- Create or use a test project targeting an older TFM, for example:
<TargetFrameworks>net8.0;net10.0</TargetFrameworks>
- Run the tests inside the SDK container:
dotnet test --configuration Release
- The
net8.0 test assembly can execute successfully even though the .NET 8 runtime is not installed, because it rolls forward to the .NET 10 runtime.
Example output:
Running tests from .../bin/Release/net8.0/...Tests.dll (net10.0|x64)
- If
DOTNET_ROLL_FORWARD is unset or changed to Minor, the same net8.0 test execution fails with the expected missing runtime error when .NET 8 is not installed.
Other information
Description
The official .NET SDK container image sets DOTNET_ROLL_FORWARD=Major globally. I understand this appears to be intentional as a PowerShell workaround, but because the variable is global it affects all .NET applications executed inside the SDK container, not only PowerShell.
This can be surprising in CI pipelines. For multi-targeted NuGet/package libraries, tests targeting older TFMs may pass even though the corresponding runtime is not installed. For example, a net8.0 test assembly can run on the .NET 10 runtime inside a .NET 10 SDK image.
From a compatibility-testing perspective, this is misleading. The pipeline result looks like net8.0 was tested successfully, but actually the net8.0 assembly was executed on .NET 10.
What other issues did you find before opening this one?
I found the PowerShell-related workaround that appears to be the reason for setting DOTNET_ROLL_FORWARD=Major in the SDK image. The issue here is not PowerShell itself, but the global side effect on unrelated workloads such as dotnet test.
What error messages do you see?
When running the same test outside the SDK container, or with DOTNET_ROLL_FORWARD unset/changed to Minor, the expected error is:
You must install or update .NET to run this application.
Framework: 'Microsoft.NETCore.App', version '8.0.0'
The following frameworks were found:
10.0.x
Inside the SDK container, the test can instead pass because of:
DOTNET_ROLL_FORWARD=Major
and test output can show something like:
Running tests from .../bin/Release/net8.0/...Tests.dll (net10.0|x64)
When does this issue occur? Does it occur consistently?
It occurs consistently when using an SDK container image where:
DOTNET_ROLL_FORWARD=Major is set globally,
- only the newer runtime is installed,
- and an older target framework test/application is executed.
Do you know of any workarounds?
Yes. For strict compatibility testing, override the variable during test execution:
export DOTNET_ROLL_FORWARD=Minor
dotnet test --configuration Release
Then install all runtimes that should actually be tested, for example .NET 8 runtime for net8.0 tests and .NET 10 runtime for net10.0 tests.
Using DOTNET_ROLL_FORWARD=Major can be acceptable as an explicit migration shortcut, but it should not be easy to mistake this for true runtime compatibility testing.
What is the container host OS and version?
This was observed in Azure DevOps using Linux job containers based on the .NET SDK image. The behavior is caused by the environment inside the SDK container, so the host OS is likely not the main factor.
Example environment from the container:
DOTNET_RUNNING_IN_CONTAINER=true
DOTNET_SDK_VERSION=10.0.300
DOTNET_VERSION=10.0.8
DOTNET_ROLL_FORWARD=Major
Suggested improvement
Please consider one of the following:
- Document this behavior prominently for SDK image users.
- Add guidance for CI/test scenarios that require strict target-runtime validation.
- Consider whether the PowerShell workaround can be scoped more narrowly instead of setting
DOTNET_ROLL_FORWARD=Major globally.
Output of docker version
Output of docker info
Describe the bug
The official .NET SDK image sets
DOTNET_ROLL_FORWARD=Majorglobally as a PowerShell workaround. This affects unrelated .NET workloads such as dotnet test and can cause older TFM tests, e.g. net8.0, to execute on the .NET 10 runtime without the target runtime being installed.This can make CI compatibility tests look successful even though the actual target runtime was not tested.
Which .NET image(s) are you using?
mcr.microsoft.com/dotnet/sdk:10.0
Steps to reproduce
Use a .NET SDK container image that contains only the latest runtime, for example a .NET 10 SDK image.
Check the installed runtimes and environment:
dotnet --list-runtimes printenv | grep DOTNET_ROLL_FORWARDObserved in the SDK container:
dotnet test --configuration Releasenet8.0test assembly can execute successfully even though the .NET 8 runtime is not installed, because it rolls forward to the .NET 10 runtime.Example output:
DOTNET_ROLL_FORWARDis unset or changed toMinor, the samenet8.0test execution fails with the expected missing runtime error when .NET 8 is not installed.Other information
Description
The official .NET SDK container image sets
DOTNET_ROLL_FORWARD=Majorglobally. I understand this appears to be intentional as a PowerShell workaround, but because the variable is global it affects all .NET applications executed inside the SDK container, not only PowerShell.This can be surprising in CI pipelines. For multi-targeted NuGet/package libraries, tests targeting older TFMs may pass even though the corresponding runtime is not installed. For example, a
net8.0test assembly can run on the .NET 10 runtime inside a .NET 10 SDK image.From a compatibility-testing perspective, this is misleading. The pipeline result looks like
net8.0was tested successfully, but actually thenet8.0assembly was executed on .NET 10.What other issues did you find before opening this one?
I found the PowerShell-related workaround that appears to be the reason for setting
DOTNET_ROLL_FORWARD=Majorin the SDK image. The issue here is not PowerShell itself, but the global side effect on unrelated workloads such asdotnet test.What error messages do you see?
When running the same test outside the SDK container, or with
DOTNET_ROLL_FORWARDunset/changed toMinor, the expected error is:Inside the SDK container, the test can instead pass because of:
and test output can show something like:
When does this issue occur? Does it occur consistently?
It occurs consistently when using an SDK container image where:
DOTNET_ROLL_FORWARD=Majoris set globally,Do you know of any workarounds?
Yes. For strict compatibility testing, override the variable during test execution:
Then install all runtimes that should actually be tested, for example .NET 8 runtime for
net8.0tests and .NET 10 runtime fornet10.0tests.Using
DOTNET_ROLL_FORWARD=Majorcan be acceptable as an explicit migration shortcut, but it should not be easy to mistake this for true runtime compatibility testing.What is the container host OS and version?
This was observed in Azure DevOps using Linux job containers based on the .NET SDK image. The behavior is caused by the environment inside the SDK container, so the host OS is likely not the main factor.
Example environment from the container:
Suggested improvement
Please consider one of the following:
DOTNET_ROLL_FORWARD=Majorglobally.Output of
docker versionOutput of
docker info