diff --git a/lib/modules/manager/nuget/artifacts.spec.ts b/lib/modules/manager/nuget/artifacts.spec.ts
index 4dfbbc4a04de05f9ece7e791d8185ff6adbf0a94..33a7465d63028b4973cecae107f4d10b588dcc0e 100644
--- a/lib/modules/manager/nuget/artifacts.spec.ts
+++ b/lib/modules/manager/nuget/artifacts.spec.ts
@@ -15,7 +15,7 @@ jest.mock('../../../util/host-rules', () => mockDeep());
 jest.mock('../../../util/git');
 jest.mock('./util');
 
-const { getDefaultRegistries } = mocked(util);
+const { getDefaultRegistries, findGlobalJson } = mocked(util);
 
 process.env.CONTAINERBASE = 'true';
 
@@ -230,12 +230,14 @@ describe('modules/manager/nuget/artifacts', () => {
     fs.getLocalFiles.mockResolvedValueOnce({
       'packages.lock.json': 'New packages.lock.json',
     });
+
+    findGlobalJson.mockResolvedValueOnce({ sdk: { version: '7.0.100' } });
     expect(
       await nuget.updateArtifacts({
         packageFileName: 'project.csproj',
         updatedDeps: [{ depName: 'dep' }],
         newPackageFileContent: '{}',
-        config: { ...config, constraints: { dotnet: '7.0.100' } },
+        config,
       }),
     ).toEqual([
       {
diff --git a/lib/modules/manager/nuget/artifacts.ts b/lib/modules/manager/nuget/artifacts.ts
index c60e04262e6363756e1c5ede3661631983999c36..88dcdd92e89471c9986bd0b641059d87f8926720 100644
--- a/lib/modules/manager/nuget/artifacts.ts
+++ b/lib/modules/manager/nuget/artifacts.ts
@@ -25,7 +25,11 @@ import {
   NUGET_CENTRAL_FILE,
   getDependentPackageFiles,
 } from './package-tree';
-import { getConfiguredRegistries, getDefaultRegistries } from './util';
+import {
+  findGlobalJson,
+  getConfiguredRegistries,
+  getDefaultRegistries,
+} from './util';
 
 async function createCachedNuGetConfigFile(
   nugetCacheDir: string,
@@ -55,6 +59,9 @@ async function runDotnetRestore(
     packageFileName,
   );
 
+  const dotnetVersion =
+    config.constraints?.dotnet ??
+    (await findGlobalJson(packageFileName))?.sdk?.version;
   const execOptions: ExecOptions = {
     docker: {},
     userConfiguredEnv: config.env,
@@ -62,9 +69,7 @@ async function runDotnetRestore(
       NUGET_PACKAGES: join(nugetCacheDir, 'packages'),
       MSBUILDDISABLENODEREUSE: '1',
     },
-    toolConstraints: [
-      { toolName: 'dotnet', constraint: config.constraints?.dotnet },
-    ],
+    toolConstraints: [{ toolName: 'dotnet', constraint: dotnetVersion }],
   };
 
   const cmds = [
diff --git a/lib/modules/manager/nuget/extract/global-manifest.ts b/lib/modules/manager/nuget/extract/global-manifest.ts
index ae9c6e9f99c0427ee028650996e2b7b6527e6c9e..d7e36b7f66c8ead034b9d36808ab67a86151e4f7 100644
--- a/lib/modules/manager/nuget/extract/global-manifest.ts
+++ b/lib/modules/manager/nuget/extract/global-manifest.ts
@@ -2,11 +2,8 @@ import { logger } from '../../../../logger';
 import { DotnetVersionDatasource } from '../../../datasource/dotnet-version';
 import { NugetDatasource } from '../../../datasource/nuget';
 import type { PackageDependency, PackageFileContent } from '../../types';
-import type {
-  MsbuildGlobalManifest,
-  NugetPackageDependency,
-  Registry,
-} from '../types';
+import { GlobalJson } from '../schema';
+import type { NugetPackageDependency, Registry } from '../types';
 import { applyRegistries } from '../util';
 
 export function extractMsbuildGlobalManifest(
@@ -15,10 +12,10 @@ export function extractMsbuildGlobalManifest(
   registries: Registry[] | undefined,
 ): PackageFileContent | null {
   const deps: PackageDependency[] = [];
-  let manifest: MsbuildGlobalManifest;
+  let manifest: GlobalJson;
   let extractedConstraints: Record<string, string> | undefined;
   try {
-    manifest = JSON.parse(content);
+    manifest = GlobalJson.parse(content);
   } catch {
     logger.debug({ packageFile }, `Invalid JSON`);
     return null;
diff --git a/lib/modules/manager/nuget/schema.ts b/lib/modules/manager/nuget/schema.ts
new file mode 100644
index 0000000000000000000000000000000000000000..c71dabc94a1fc177f3d3cec3c5d0a9f9f0238161
--- /dev/null
+++ b/lib/modules/manager/nuget/schema.ts
@@ -0,0 +1,63 @@
+import { z } from 'zod';
+import { Jsonc } from '../../../util/schema-utils';
+
+/**
+ * The roll-forward policy to use when selecting an SDK version, either as a fallback when a specific SDK version is missing or as a directive to use a later version. A version must be specified with a rollForward value, unless you're setting it to latestMajor. The default roll forward behavior is determined by the matching rules.
+ *
+ * https://learn.microsoft.com/de-de/dotnet/core/tools/global-json#rollforward
+ */
+const RollForwardSchema = z.enum([
+  'patch',
+  'feature',
+  'minor',
+  'major',
+  'latestPatch',
+  'latestFeature',
+  'latestMinor',
+  'latestMajor',
+  'disable',
+]);
+export type RollForward = z.infer<typeof RollForwardSchema>;
+
+/**
+ * global.json schema
+ *
+ * https://learn.microsoft.com/en-us/dotnet/core/tools/global-json#allowprerelease
+ */
+export const GlobalJsonSchema = z.object({
+  /**
+   * Specifies information about the .NET SDK to select.
+   */
+  sdk: z
+    .object({
+      /**
+       * The version of the .NET SDK to use.
+       *
+       * https://learn.microsoft.com/de-de/dotnet/core/tools/global-json#version
+       */
+      version: z.string().optional(),
+      /**
+       * The roll-forward policy to use when selecting an SDK version, either as a fallback when a specific SDK version is missing or as a directive to use a later version. A version must be specified with a rollForward value, unless you're setting it to latestMajor. The default roll forward behavior is determined by the matching rules.
+       *
+       * https://learn.microsoft.com/de-de/dotnet/core/tools/global-json#rollforward
+       */
+      rollForward: RollForwardSchema.optional(),
+      /**
+       * Indicates whether the SDK resolver should consider prerelease versions when selecting the SDK version to use.
+       *
+       * https://learn.microsoft.com/de-de/dotnet/core/tools/global-json#allowprerelease
+       */
+      allowPrerelease: z.boolean().optional(),
+    })
+    .optional(),
+
+  /**
+   * Lets you control the project SDK version in one place rather than in each individual project. For more information, see How project SDKs are resolved.
+   *
+   * https://learn.microsoft.com/de-de/dotnet/core/tools/global-json#msbuild-sdks
+   */
+  'msbuild-sdks': z.record(z.string()).optional(),
+});
+
+export const GlobalJson = Jsonc.pipe(GlobalJsonSchema);
+export type GlobalJson = z.infer<typeof GlobalJson>;
diff --git a/lib/modules/manager/nuget/types.ts b/lib/modules/manager/nuget/types.ts
index 0eef41beaf9885b33a31cc043a45eb9bd0de8ef9..9d34b776f14fb81474a2afd292f83269318f685c 100644
--- a/lib/modules/manager/nuget/types.ts
+++ b/lib/modules/manager/nuget/types.ts
@@ -17,17 +17,6 @@ export interface Registry {
   readonly name?: string;
   sourceMappedPackagePatterns?: string[];
 }
-
-export interface MsbuildGlobalManifest {
-  readonly sdk?: MsbuildSdk;
-  readonly 'msbuild-sdks'?: Record<string, string>;
-}
-
-export interface MsbuildSdk {
-  readonly version: string;
-  readonly rollForward: string;
-}
-
 export interface ProjectFile {
   readonly isLeaf: boolean;
   readonly name: string;
diff --git a/lib/modules/manager/nuget/util.spec.ts b/lib/modules/manager/nuget/util.spec.ts
index 0956e2014c0383034a7c7e66bfd477d5fb34e47d..a1a01dec1040d1e1fbae3577a4a2372549f53691 100644
--- a/lib/modules/manager/nuget/util.spec.ts
+++ b/lib/modules/manager/nuget/util.spec.ts
@@ -3,7 +3,12 @@ import { XmlDocument } from 'xmldoc';
 import { fs } from '../../../../test/util';
 import type { Registry } from './types';
 import { bumpPackageVersion } from './update';
-import { applyRegistries, findVersion, getConfiguredRegistries } from './util';
+import {
+  applyRegistries,
+  findGlobalJson,
+  findVersion,
+  getConfiguredRegistries,
+} from './util';
 
 jest.mock('../../../util/fs');
 
@@ -340,4 +345,34 @@ describe('modules/manager/nuget/util', () => {
       });
     });
   });
+
+  describe('findGlobalJson', () => {
+    it('not found', async () => {
+      fs.findLocalSiblingOrParent.mockResolvedValueOnce(null);
+      const globalJson = await findGlobalJson('project.csproj');
+      expect(globalJson).toBeNull();
+    });
+
+    it('no content', async () => {
+      fs.findLocalSiblingOrParent.mockResolvedValueOnce('global.json');
+      const globalJson = await findGlobalJson('project.csproj');
+      expect(globalJson).toBeNull();
+    });
+
+    it('fails to parse', async () => {
+      fs.findLocalSiblingOrParent.mockResolvedValueOnce('global.json');
+      fs.readLocalFile.mockResolvedValueOnce('{');
+      const globalJson = await findGlobalJson('project.csproj');
+      expect(globalJson).toBeNull();
+    });
+
+    it('parses', async () => {
+      fs.findLocalSiblingOrParent.mockResolvedValueOnce('global.json');
+      fs.readLocalFile.mockResolvedValueOnce(
+        '{   /* This is comment */ "sdk": { "version": "5.0.100" }, "some": true }',
+      );
+      const globalJson = await findGlobalJson('project.csproj');
+      expect(globalJson).toEqual({ sdk: { version: '5.0.100' } });
+    });
+  });
 });
diff --git a/lib/modules/manager/nuget/util.ts b/lib/modules/manager/nuget/util.ts
index dd210357d3a863be90624801e0a2a2cd0bd53866..3f04f4318e758bc0a7fbf94f13360555080cd245 100644
--- a/lib/modules/manager/nuget/util.ts
+++ b/lib/modules/manager/nuget/util.ts
@@ -2,10 +2,15 @@ import upath from 'upath';
 import type { XmlElement } from 'xmldoc';
 import { XmlDocument } from 'xmldoc';
 import { logger } from '../../../logger';
-import { findUpLocal, readLocalFile } from '../../../util/fs';
+import {
+  findLocalSiblingOrParent,
+  findUpLocal,
+  readLocalFile,
+} from '../../../util/fs';
 import { minimatch } from '../../../util/minimatch';
 import { regEx } from '../../../util/regex';
 import { nugetOrg } from '../../datasource/nuget';
+import { GlobalJson } from './schema';
 import type { NugetPackageDependency, Registry } from './types';
 
 export async function readFileAsXmlDocument(
@@ -207,3 +212,31 @@ function sortPatterns(
 
   return a[0].localeCompare(b[0]) * -1;
 }
+
+export async function findGlobalJson(
+  packageFile: string,
+): Promise<GlobalJson | null> {
+  const globalJsonPath = await findLocalSiblingOrParent(
+    packageFile,
+    'global.json',
+  );
+  if (!globalJsonPath) {
+    return null;
+  }
+
+  const content = await readLocalFile(globalJsonPath, 'utf8');
+  if (!content) {
+    logger.debug({ packageFile, globalJsonPath }, 'Failed to read global.json');
+    return null;
+  }
+
+  const result = await GlobalJson.safeParseAsync(content);
+  if (!result.success) {
+    logger.debug(
+      { packageFile, globalJsonPath, err: result.error },
+      'Failed to parse global.json',
+    );
+    return null;
+  }
+  return result.data;
+}