.NET Core applications contain a file named <something>.runtimeconfig.json. This file can be used to control a variety of options. Most developers need not be concerned with it because the SDK generates the file, but I think it’s worth understanding. The file can be used to control settings which are not surfaced in Visual Studio, such as automatically running your app on higher .NET Core versions, tuning thread pools and garbage collection, and more.

This post is part of a series:

Purpose of the file

The runtimeconfig.json file is technically optional, but for practical reasons, every real-world app has it. The file can be hand-edited. Unlike the .deps.json file, it is meant to be human-readable. The purpose of the file is to define required shared frameworks (for framework-dependency deployments only), as well as other runtime options, as outlined below.

A simple example

A typical runtimeconfig.json file will look something like this.

{
  "runtimeOptions": {
    "tfm": "netcoreapp2.1",
    "framework": {
      "name": "Microsoft.NETCore.App",
      "version": "2.1.0"
    }
  }
}

I’ve written a complete schema for this file. See https://gist.github.com/natemcmaster/0bdee16450f8ec1823f2c11af880ceeb.

runtimeconfig.template.json

Some options cannot be set in your project file (.csproj). You have two options to work around this. Hand-edit runtimeconfig.json as a post-build action, or use a file named runtimeconfig.template.json. I recommend using the template if you want settings to persist.

On build, the SDK will augment the template with additional data from your .csproj file. Follow these steps to use a template:

  1. Create a new project (dotnet new console -n MyApp)
  2. Create a file named “runtimeconfig.template.json” into the project directory (next to your .csproj file).
  3. Set the contents of the file to this:
     {
       "rollForwardOnNoCandidateFx": 2
     }
    
  4. Run dotnet build

Voila! That’s it. Look at bin/Debug/netcoreapp2.1/MyApp.runtimeconfig.json to make sure it worked.

Visual Studio intellisense

I’ve written a JSON schema, which you can use in your Visual Studio editor. Add this line to your runtimeconfig.template.json file.

{
  "$schema": "https://gist.githubusercontent.com/natemcmaster/0bdee16450f8ec1823f2c11af880ceeb/raw/runtimeconfig.template.schema.json"
}

Runtime options

Frameworks, versions, and roll-forward

.NET Core shared frameworks support installing side-by-side versions, and therefore, dotnet has to pick one version when starting an application. The following options are used to set which shared frameworks and which versions of those frameworks are loaded.

Note: the default settings generated by the SDK are usually sufficient, but they can be altered to workaround regressions in .NET Core patches or the unfortunately common error when .NET Core fails to launch:

It was not possible to find any compatible framework version. The specified framework ‘Microsoft.NETCore.App’, version ‘X.Y.Z’ was not found.

Shared framework(s)

This specifies the shared framework(s) the application depends on by name. The version is treated as a minimum version. The only way to override the minimum (without changing the file) is to use dotnet exec --fx-version.

For .NET Core < 3.0, only one framework can be specified.

JSON:

{
  "runtimeOptions": {
    "framework": {
      "name": "Microsoft.AspNetCore.App",
      "version": "2.2.0"
    }
  }
}

.csproj:

<ItemGroup>
  <PackageReference Include="Microsoft.AspNetCore.App" Version="2.2.0" />
</ItemGroup>

For .NET Core >= 3.0, multiple shared frameworks can be used and are no longer referenced as packages.

Note: 3.0 is still in preview, may change.

JSON:

{
  "runtimeOptions": {
    "frameworks": [
      {
        "name": "Microsoft.AspNetCore.App",
        "version": "3.0.0"
      },
      {
        "name": "Microsoft.WindowsDesktop.App",
        "version": "3.0.0"
      }
    ]
  }
}

.csproj:

<ItemGroup>
  <FrameworkReference Include="Microsoft.AspNetCore.App" />
  <FrameworkReference Include="Microsoft.WindowsDesktop.App" />
</ItemGroup>

Automatically run on higher versions

This option is new in .NET Core 3.0.

By default, .NET Core will try to find the highest patch version of the shared framework which has the same major and minor version as your app specifies. But if it can’t find that, it may roll-forward to newer versions. This option controls the roll-forward policy.

JSON:

{
  "runtimeOptions": {
    "rollForward": "Major"
  }
}

.csproj:

<PropertyGroup>
  <RollForward>Major</RollForward>
</PropertyGroup>

The spec for this setting can be found at https://github.com/dotnet/designs/blob/master/accepted/2019/runtime-binding.md. About this setting, it says:

RollForward can have the following values:

  • LatestPatch – Roll forward to the highest patch version. This disables minor version roll forward.
  • Minor – Roll forward to the lowest higher minor version, if requested minor version is missing. If the requested minor version is present, then the LatestPatch policy is used.
  • Major – Roll forward to lowest higher major version, and lowest minor version, if requested major version is missing. If the requested major version is present, then the Minor policy is used.
  • LatestMinor – Roll forward to highest minor version, even if requested minor version is present.
  • LatestMajor – Roll forward to highest major and highest minor version, even if requested major is present.
  • Disable – Do not roll forward. Only bind to specified version. This policy is not recommended for general use since it disable the ability to roll-forward to the latest patches. It is only recommended for testing.

Minor is the default setting. See Configuration Precedence for more information.

In all cases except Disable the highest available patch version is selected.

Note: LatestMinor and LatestMajor are intended for component hosting scenarios, for both managed and native hosts (for example, managed COM components).

Automatically run on higher patch versions (before .NET Core 3.0)

This policy is being deprecated in .NET Core 3.0 in favor of the simpler “rollForward” option, as described above.

By default, .NET Core runs on the highest patch version of shared frameworks installed on the machine. This can be disabled using ‘applyPatches’.

JSON:

{
  "runtimeOptions": {
    "applyPatches": false
  }
}

.csproj: currently not available as an SDK option. See above.

Note: I couldn’t write about this without a word of caution. I would personally only use this in production when it’s 3 AM, the site is down, the phone is ringing, and the company is bleeding $$$ every minute. Otherwise, it’s better to get the latest security patches – for obvious reasons.

Automatically run on higher major or minor versions (before .NET Core 3.0)

This policy is being deprecated in .NET Core 3.0 in favor of the simpler “rollForward” option, as described above.

By default, .NET Core will try to find the highest patch version of the shared framework which has the same major and minor version as your app specifies. But if it can’t find that, it may roll-forward to newer versions. This option controls the roll-forward policy.

JSON:

{
  "runtimeOptions": {
    "rollForwardOnNoCandidateFx": 1
  }
}

.csproj: currently not available as an SDK option. See above.

This can be set to 0, 1, or 2. See the design document for more details.

For example, given framework.version == 2.1.0, this is how .NET Core uses this setting to decided what is a ‘compatible’ version of the framework.

rollForwardOnNoCandidateFx Compatible framework versions
0 >=2.1.0, < 2.2.0
1 (default) >=2.1.0, < 3.0.0
2 >=2.1.0

Target framework moniker

This one is an implementation detail of the runtime package store.

JSON:

{
  "runtimeOptions": {
    "tfm": "netcoreapp2.1"
  }
}

.csproj:

<PropertyGroup>
  <TargetFramework>netcoreapp2.1</TargetFramework>
</PropertyGroup>

Assembly probing paths

This specifies additional folders used by the host to find assemblies listed in the .deps.json file. See Part 1 of this series for details on how this works.

JSON:

{
  "runtimeOptions": {
    "additionalProbingPaths": [
      "C:\\Users\\nmcmaster\\.nuget\\packages\\"
    ]
  }
}

.csproj:

<ItemGroup>
  <AdditionalProbingPath Include="$(USERPROFILE)\.nuget\packages" />
</ItemGroup>

Note: this .csproj item will only end up in the runtimeconfig.dev.json file, which is only used during development, not production. Use the template file to set values which are required to be in the regular, production version of runtimeconfig.json.

Runtime settings

“configProperties” is a list of key-value pairs given to the runtime. These can be used in almost any way imaginable, but there is a short list of well-defined and commonly used settings.

JSON:

{
  "runtimeOptions": {
    "configProperties": {
      "key": "value"
    }
  }
}

Well-known runtime settings

Setting name Type Description
System.GC.Server boolean Enable server garbage collection.
System.GC.Concurrent boolean Enable concurrent garbage collection.
System.GC.RetainVM boolean Put segments that should be deleted on a standby list for future use instead of releasing them back to the OS.
System.Runtime.TieredCompilation boolean Enable tiered compilation.
System.Threading.ThreadPool.MinThreads integer Override MinThreads for the ThreadPool worker pool.
System.Threading.ThreadPool.MaxThreads integer Override MaxThreads for the ThreadPool worker pool.
System.Globalization.Invariant boolean Enabling invariant mode disables globalization behavior.

Here are some documents explaining more about these:

These settings can be configured in your .csproj file. The best way to find more is to look at the Microsoft.NET.Sdk.targets file itself.

<PropertyGroup>
  <ConcurrentGarbageCollection>true</ConcurrentGarbageCollection>
  <ServerGarbageCollection>true</ServerGarbageCollection>
  <RetainVMGarbageCollection>true</RetainVMGarbageCollection>
  <ThreadPoolMinThreads>1</ThreadPoolMinThreads>
  <ThreadPoolMaxThreads>100</ThreadPoolMaxThreads>
  <!-- Supported as of .NET Core SDK 3.0 Preview 1 -->
  <TieredCompilation>true</TieredCompilation>
  <InvariantGlobalization>true</InvariantGlobalization>
</PropertyGroup>

Additional runtime settings

.NET Core allows you to specify your own settings. These values can be retrieved using System.AppContext.GetData.

Note: this is not a suitable alternative to configuration builders.

JSON:

{
  "runtimeOptions": {
    "configProperties": {
      "ArbitraryNumberSetting": 2,
      "ArbitraryStringSetting": "red",
      "ArbitraryBoolSetting": true
    }
  }
}

.csproj:

<ItemGroup>
  <RuntimeHostConfigurationOption Include="ArbitraryNumberSetting" Value="2" />
  <RuntimeHostConfigurationOption Include="ArbitraryStringSetting" Value="red" />
  <RuntimeHostConfigurationOption Include="ArbitraryBoolSetting" Value="true" />
</ItemGroup>

In C#,

// "red"
var color = System.AppContext.GetData("ArbitraryStringSetting") as string;

More info

See Part 1 for more details about this file and how to use it. I also recommend searching through the Markdown files in https://github.com/dotnet for more details on how these various settings are used.