diff --git a/lib/manager/gradle-wrapper/artifacts-real.spec.ts b/lib/manager/gradle-wrapper/artifacts-real.spec.ts
index fa522df65013902a8a67682069bf1a56e8afc10a..acb1e1debbd9eedcd8e0280bf9a058b202d91168 100644
--- a/lib/manager/gradle-wrapper/artifacts-real.spec.ts
+++ b/lib/manager/gradle-wrapper/artifacts-real.spec.ts
@@ -8,7 +8,7 @@ import type { RepoGlobalConfig } from '../../config/types';
 import type { StatusResult } from '../../util/git';
 import { ifSystemSupportsGradle } from '../gradle/deep/__testutil__/gradle';
 import type { UpdateArtifactsConfig } from '../types';
-import * as dcUpdate from '.';
+import * as gradleWrapper from '.';
 
 jest.mock('../../util/git');
 
@@ -60,7 +60,7 @@ describe('manager/gradle-wrapper/artifacts-real', () => {
         ],
       } as StatusResult);
 
-      const res = await dcUpdate.updateArtifacts({
+      const res = await gradleWrapper.updateArtifacts({
         packageFileName: 'gradle/wrapper/gradle-wrapper.properties',
         updatedDeps: [],
         newPackageFileContent: await readString(
@@ -100,7 +100,7 @@ describe('manager/gradle-wrapper/artifacts-real', () => {
         })
       );
 
-      const result = await dcUpdate.updateArtifacts({
+      const result = await gradleWrapper.updateArtifacts({
         packageFileName: 'gradle/wrapper/gradle-wrapper.properties',
         updatedDeps: [],
         newPackageFileContent: ``,
@@ -118,7 +118,7 @@ describe('manager/gradle-wrapper/artifacts-real', () => {
         modified: [],
       } as StatusResult);
 
-      const res = await dcUpdate.updateArtifacts({
+      const res = await gradleWrapper.updateArtifacts({
         packageFileName: 'gradle/wrapper/gradle-wrapper.properties',
         updatedDeps: [],
         newPackageFileContent: await readString(
@@ -142,7 +142,7 @@ describe('manager/gradle-wrapper/artifacts-real', () => {
         throw new Error('failed');
       });
 
-      const res = await dcUpdate.updateArtifacts({
+      const res = await gradleWrapper.updateArtifacts({
         packageFileName: 'gradle/wrapper/gradle-wrapper.properties',
         updatedDeps: [],
         newPackageFileContent: await readString(
@@ -169,7 +169,7 @@ describe('manager/gradle-wrapper/artifacts-real', () => {
       };
 
       setGlobalConfig(wrongCmdConfig);
-      const res = await dcUpdate.updateArtifacts({
+      const res = await gradleWrapper.updateArtifacts({
         packageFileName: 'gradle/wrapper/gradle-wrapper.properties',
         updatedDeps: [],
         newPackageFileContent: await readString(
@@ -192,7 +192,7 @@ describe('manager/gradle-wrapper/artifacts-real', () => {
 
     it('gradlew not found', async () => {
       setGlobalConfig({ localDir: 'some-dir' });
-      const res = await dcUpdate.updateArtifacts({
+      const res = await gradleWrapper.updateArtifacts({
         packageFileName: 'gradle-wrapper.properties',
         updatedDeps: [],
         newPackageFileContent: undefined,
@@ -219,7 +219,7 @@ describe('manager/gradle-wrapper/artifacts-real', () => {
 
       const newContent = await readString(`./gradle-wrapper-sum.properties`);
 
-      const result = await dcUpdate.updateArtifacts({
+      const result = await gradleWrapper.updateArtifacts({
         packageFileName: 'gradle/wrapper/gradle-wrapper.properties',
         updatedDeps: [],
         newPackageFileContent: newContent.replace(
@@ -263,7 +263,7 @@ describe('manager/gradle-wrapper/artifacts-real', () => {
         .get('/distributions/gradle-6.3-bin.zip.sha256')
         .reply(404);
 
-      const result = await dcUpdate.updateArtifacts({
+      const result = await gradleWrapper.updateArtifacts({
         packageFileName: 'gradle/wrapper/gradle-wrapper.properties',
         updatedDeps: [],
         newPackageFileContent: `distributionSha256Sum=336b6898b491f6334502d8074a6b8c2d73ed83b92123106bd4bf837f04111043\ndistributionUrl=https\\://services.gradle.org/distributions/gradle-6.3-bin.zip`,
diff --git a/lib/manager/gradle-wrapper/artifacts.spec.ts b/lib/manager/gradle-wrapper/artifacts.spec.ts
index 5ce870f9dcc8341bbcc49479d0c4168ed0cfdb20..bcd2199fa5f7313e8c0cfadcea930d178220f15d 100644
--- a/lib/manager/gradle-wrapper/artifacts.spec.ts
+++ b/lib/manager/gradle-wrapper/artifacts.spec.ts
@@ -1,8 +1,6 @@
-/* eslint jest/no-standalone-expect: 0 */
-import { exec as _exec } from 'child_process';
-import { readFile } from 'fs-extra';
+import { readFile, stat } from 'fs-extra';
 import { resolve } from 'upath';
-import { envMock, mockExecAll } from '../../../test/exec-util';
+import { envMock, exec, mockExecAll } from '../../../test/exec-util';
 import * as httpMock from '../../../test/http-mock';
 import {
   addReplacingSerializer,
@@ -16,14 +14,13 @@ import type { RepoGlobalConfig } from '../../config/types';
 import { resetPrefetchedImages } from '../../util/exec/docker';
 import type { StatusResult } from '../../util/git';
 import type { UpdateArtifactsConfig } from '../types';
-import * as dcUpdate from '.';
+import * as gradleWrapper from '.';
 
 jest.mock('child_process');
 jest.mock('../../util/fs');
 jest.mock('../../util/git');
 jest.mock('../../util/exec/env');
 
-const exec: jest.Mock<typeof _exec> = _exec as any;
 const fixtures = resolve(__dirname, './__fixtures__');
 
 const adminConfig: RepoGlobalConfig = {
@@ -57,6 +54,7 @@ describe('manager/gradle-wrapper/artifacts', () => {
     resetPrefetchedImages();
 
     fs.readLocalFile.mockResolvedValue('test');
+    fs.stat.mockImplementation((p) => stat(p));
   });
 
   afterEach(() => {
@@ -74,7 +72,7 @@ describe('manager/gradle-wrapper/artifacts', () => {
 
     const execSnapshots = mockExecAll(exec);
 
-    const res = await dcUpdate.updateArtifacts({
+    const res = await gradleWrapper.updateArtifacts({
       packageFileName: 'gradle/wrapper/gradle-wrapper.properties',
       updatedDeps: [],
       newPackageFileContent: await readString(
@@ -100,7 +98,7 @@ describe('manager/gradle-wrapper/artifacts', () => {
 
   it('gradlew not found', async () => {
     setGlobalConfig({ ...adminConfig, localDir: 'some-dir' });
-    const res = await dcUpdate.updateArtifacts({
+    const res = await gradleWrapper.updateArtifacts({
       packageFileName: 'gradle-wrapper.properties',
       updatedDeps: [],
       newPackageFileContent: undefined,
@@ -117,7 +115,7 @@ describe('manager/gradle-wrapper/artifacts', () => {
         modified: [],
       })
     );
-    const res = await dcUpdate.updateArtifacts({
+    const res = await gradleWrapper.updateArtifacts({
       packageFileName: 'gradle-wrapper.properties',
       updatedDeps: [],
       newPackageFileContent: '',
@@ -145,7 +143,7 @@ describe('manager/gradle-wrapper/artifacts', () => {
 
     const execSnapshots = mockExecAll(exec);
 
-    const result = await dcUpdate.updateArtifacts({
+    const result = await gradleWrapper.updateArtifacts({
       packageFileName: 'gradle-wrapper.properties',
       updatedDeps: [],
       newPackageFileContent: `distributionSha256Sum=336b6898b491f6334502d8074a6b8c2d73ed83b92123106bd4bf837f04111043\ndistributionUrl=https\\://services.gradle.org/distributions/gradle-6.3-bin.zip`,
@@ -179,7 +177,7 @@ describe('manager/gradle-wrapper/artifacts', () => {
       .get('/distributions/gradle-6.3-bin.zip.sha256')
       .reply(404);
 
-    const result = await dcUpdate.updateArtifacts({
+    const result = await gradleWrapper.updateArtifacts({
       packageFileName: 'gradle-wrapper.properties',
       updatedDeps: [],
       newPackageFileContent: `distributionSha256Sum=336b6898b491f6334502d8074a6b8c2d73ed83b92123106bd4bf837f04111043\ndistributionUrl=https\\://services.gradle.org/distributions/gradle-6.3-bin.zip`,
diff --git a/lib/manager/gradle-wrapper/artifacts.ts b/lib/manager/gradle-wrapper/artifacts.ts
index 9e551d7ec4e83a455aeb1b022797a8c041f9eabf..d594c16ad8e2783d6c136a32602f1aadfa7c5c69 100644
--- a/lib/manager/gradle-wrapper/artifacts.ts
+++ b/lib/manager/gradle-wrapper/artifacts.ts
@@ -1,18 +1,19 @@
-import { stat } from 'fs-extra';
 import { resolve } from 'upath';
 import { getGlobalConfig } from '../../config/global';
 import { TEMPORARY_ERROR } from '../../constants/error-messages';
 import { logger } from '../../logger';
 import { ExecOptions, exec } from '../../util/exec';
-import { readLocalFile, writeLocalFile } from '../../util/fs';
+import { readLocalFile, stat, writeLocalFile } from '../../util/fs';
 import { StatusResult, getRepoStatus } from '../../util/git';
 import { Http } from '../../util/http';
+import type { UpdateArtifact, UpdateArtifactsResult } from '../types';
 import {
   extraEnv,
+  getJavaContraint,
+  getJavaVersioning,
   gradleWrapperFileName,
   prepareGradleCommand,
-} from '../gradle/deep/utils';
-import type { UpdateArtifact, UpdateArtifactsResult } from '../types';
+} from './utils';
 
 const http = new Http('gradle-wrapper');
 
@@ -57,7 +58,7 @@ export async function updateArtifacts({
   try {
     const { localDir: projectDir } = getGlobalConfig();
     logger.debug({ updatedDeps }, 'gradle-wrapper.updateArtifacts()');
-    const gradlew = gradleWrapperFileName(config);
+    const gradlew = gradleWrapperFileName();
     const gradlewPath = resolve(projectDir, `./${gradlew}`);
     let cmd = await prepareGradleCommand(
       gradlew,
@@ -87,7 +88,10 @@ export async function updateArtifacts({
     logger.debug(`Updating gradle wrapper: "${cmd}"`);
     const execOptions: ExecOptions = {
       docker: {
-        image: 'gradle',
+        image: 'java',
+        tagConstraint:
+          config.constraints?.java ?? getJavaContraint(config.currentValue),
+        tagScheme: getJavaVersioning(),
       },
       extraEnv,
     };
diff --git a/lib/manager/gradle-wrapper/extract.ts b/lib/manager/gradle-wrapper/extract.ts
index 8bc097918750262242d4d3a3c92f4ea5a6be4cad..88383865230e980b598257707de157839ef313fd 100644
--- a/lib/manager/gradle-wrapper/extract.ts
+++ b/lib/manager/gradle-wrapper/extract.ts
@@ -1,30 +1,20 @@
 import { GradleVersionDatasource } from '../../datasource/gradle-version';
 import { logger } from '../../logger';
-import { regEx } from '../../util/regex';
-import * as gradleVersioning from '../../versioning/gradle';
+import { id as versioning } from '../../versioning/gradle';
 import type { PackageDependency, PackageFile } from '../types';
-
-// https://regex101.com/r/1GaQ2X/1
-const DISTRIBUTION_URL_REGEX = regEx(
-  '^(?:distributionUrl\\s*=\\s*)\\S*-(?<version>\\d+\\.\\d+(?:\\.\\d+)?(?:-\\w+)*)-(?<type>bin|all)\\.zip\\s*$'
-);
+import { extractGradleVersion } from './utils';
 
 export function extractPackageFile(fileContent: string): PackageFile | null {
-  logger.debug('gradle-wrapper.extractPackageFile()');
-  const lines = fileContent.split('\n');
-
-  for (const line of lines) {
-    const distributionUrlMatch = DISTRIBUTION_URL_REGEX.exec(line);
-    if (distributionUrlMatch) {
-      const dependency: PackageDependency = {
-        depName: 'gradle',
-        currentValue: distributionUrlMatch.groups.version,
-        datasource: GradleVersionDatasource.id,
-        versioning: gradleVersioning.id,
-      };
-      logger.debug(dependency, 'Gradle Wrapper');
-      return { deps: [dependency] };
-    }
+  logger.trace('gradle-wrapper.extractPackageFile()');
+  const currentValue = extractGradleVersion(fileContent);
+  if (currentValue) {
+    const dependency: PackageDependency = {
+      depName: 'gradle',
+      currentValue,
+      datasource: GradleVersionDatasource.id,
+      versioning,
+    };
+    return { deps: [dependency] };
   }
   return null;
 }
diff --git a/lib/manager/gradle-wrapper/util.spec.ts b/lib/manager/gradle-wrapper/util.spec.ts
new file mode 100644
index 0000000000000000000000000000000000000000..9a65c9c4b9c5d2ec6d384eefa11d02fc716af76f
--- /dev/null
+++ b/lib/manager/gradle-wrapper/util.spec.ts
@@ -0,0 +1,36 @@
+import { setGlobalConfig } from '../../config/global';
+import { extractGradleVersion, getJavaContraint } from './utils';
+
+describe('manager/gradle-wrapper/util', () => {
+  describe('getJavaContraint()', () => {
+    it('return null for global mode', () => {
+      expect(getJavaContraint(undefined)).toBeNull();
+    });
+
+    it('return ^11.0.0 for docker mode and undefined gradle', () => {
+      setGlobalConfig({ binarySource: 'docker' });
+      expect(getJavaContraint(undefined)).toEqual('^11.0.0');
+    });
+
+    it('return ^8.0.0 for docker gradle < 5', () => {
+      setGlobalConfig({ binarySource: 'docker' });
+      expect(getJavaContraint('4.9')).toEqual('^8.0.0');
+    });
+
+    it('return ^11.0.0 for docker gradle >=5 && <7', () => {
+      setGlobalConfig({ binarySource: 'docker' });
+      expect(getJavaContraint('6.0')).toEqual('^11.0.0');
+    });
+
+    it('return ^16.0.0 for docker gradle >= 7', () => {
+      setGlobalConfig({ binarySource: 'docker' });
+      expect(getJavaContraint('7.0.1')).toEqual('^16.0.0');
+    });
+  });
+
+  describe('extractGradleVersion()', () => {
+    it('works for undefined', () => {
+      expect(extractGradleVersion(undefined)).toBeNull();
+    });
+  });
+});
diff --git a/lib/manager/gradle-wrapper/utils.ts b/lib/manager/gradle-wrapper/utils.ts
new file mode 100644
index 0000000000000000000000000000000000000000..f328ed53e9a6f6230bfab20a5bd549c4008a8746
--- /dev/null
+++ b/lib/manager/gradle-wrapper/utils.ts
@@ -0,0 +1,91 @@
+import type { Stats } from 'fs';
+import os from 'os';
+import upath from 'upath';
+import { getGlobalConfig } from '../../config/global';
+import { chmod } from '../../util/fs';
+import { regEx } from '../../util/regex';
+import gradleVersioning from '../../versioning/gradle';
+import { id as npmVersioning } from '../../versioning/npm';
+
+export const extraEnv = {
+  GRADLE_OPTS:
+    '-Dorg.gradle.parallel=true -Dorg.gradle.configureondemand=true -Dorg.gradle.daemon=false -Dorg.gradle.caching=false',
+};
+
+export function gradleWrapperFileName(): string {
+  if (
+    os.platform() === 'win32' &&
+    getGlobalConfig()?.binarySource !== 'docker'
+  ) {
+    return 'gradlew.bat';
+  }
+  return './gradlew';
+}
+
+export async function prepareGradleCommand(
+  gradlewName: string,
+  cwd: string,
+  gradlew: Stats | null,
+  args: string | null
+): Promise<string> {
+  /* eslint-disable no-bitwise */
+  // istanbul ignore if
+  if (gradlew?.isFile() === true) {
+    // if the file is not executable by others
+    if ((gradlew.mode & 0o1) === 0) {
+      // add the execution permission to the owner, group and others
+      await chmod(upath.join(cwd, gradlewName), gradlew.mode | 0o111);
+    }
+    if (args === null) {
+      return gradlewName;
+    }
+    return `${gradlewName} ${args}`;
+  }
+  /* eslint-enable no-bitwise */
+  return null;
+}
+
+/**
+ * Find compatible java version for gradle.
+ * see https://docs.gradle.org/current/userguide/compatibility.html
+ * @param gradleVersion current gradle version
+ * @returns A Java semver range
+ */
+export function getJavaContraint(gradleVersion: string): string | null {
+  if (getGlobalConfig()?.binarySource !== 'docker') {
+    // ignore
+    return null;
+  }
+
+  const major = gradleVersioning.getMajor(gradleVersion);
+  if (major >= 7) {
+    return '^16.0.0';
+  }
+  // first public gradle version was 2.0
+  if (major > 0 && major < 5) {
+    return '^8.0.0';
+  }
+  return '^11.0.0';
+}
+
+export function getJavaVersioning(): string {
+  return npmVersioning;
+}
+
+// https://regex101.com/r/1GaQ2X/1
+const DISTRIBUTION_URL_REGEX = regEx(
+  '^(?:distributionUrl\\s*=\\s*)\\S*-(?<version>\\d+\\.\\d+(?:\\.\\d+)?(?:-\\w+)*)-(?<type>bin|all)\\.zip\\s*$'
+);
+
+export function extractGradleVersion(fileContent: string): string | null {
+  const lines = fileContent?.split('\n') ?? [];
+
+  for (const line of lines) {
+    const distributionUrlMatch = DISTRIBUTION_URL_REGEX.exec(line);
+    if (distributionUrlMatch) {
+      return distributionUrlMatch.groups.version;
+    }
+  }
+
+  return null;
+}
diff --git a/lib/manager/gradle/deep/__snapshots__/index.spec.ts.snap b/lib/manager/gradle/deep/__snapshots__/index.spec.ts.snap
index b5a158ef4ad61cd3c51e1ac39959ab6b6d4a40c0..175a47f608ffe26e9d502fbb960f0e4d50273e56 100644
--- a/lib/manager/gradle/deep/__snapshots__/index.spec.ts.snap
+++ b/lib/manager/gradle/deep/__snapshots__/index.spec.ts.snap
@@ -870,13 +870,13 @@ Array [
 exports[`manager/gradle/deep/index extractPackageFile should use docker even if gradlew is available 2`] = `
 Array [
   Object {
-    "cmd": "docker pull renovate/gradle",
+    "cmd": "docker pull renovate/java:11.0.12",
     "options": Object {
       "encoding": "utf-8",
     },
   },
   Object {
-    "cmd": "docker ps --filter name=renovate_gradle -aq",
+    "cmd": "docker ps --filter name=renovate_java -aq",
     "options": Object {
       "encoding": "utf-8",
     },
@@ -888,7 +888,7 @@ Array [
     },
   },
   Object {
-    "cmd": "docker run --rm --name=renovate_gradle --label=renovate_child -v \\"/foo/bar\\":\\"/foo/bar\\" -e GRADLE_OPTS -w \\"/foo/bar\\" renovate/gradle bash -l -c \\"<gradlew> --init-script renovate-plugin.gradle renovate\\"",
+    "cmd": "docker run --rm --name=renovate_java --label=renovate_child -v \\"/foo/bar\\":\\"/foo/bar\\" -e GRADLE_OPTS -w \\"/foo/bar\\" renovate/java:11.0.12 bash -l -c \\"<gradlew> --init-script renovate-plugin.gradle renovate\\"",
     "options": Object {
       "cwd": "/foo/bar",
       "encoding": "utf-8",
@@ -1005,13 +1005,13 @@ Array [
 exports[`manager/gradle/deep/index extractPackageFile should use docker even if gradlew.bat is available on Windows 2`] = `
 Array [
   Object {
-    "cmd": "docker pull renovate/gradle",
+    "cmd": "docker pull renovate/java:11.0.12",
     "options": Object {
       "encoding": "utf-8",
     },
   },
   Object {
-    "cmd": "docker ps --filter name=renovate_gradle -aq",
+    "cmd": "docker ps --filter name=renovate_java -aq",
     "options": Object {
       "encoding": "utf-8",
     },
@@ -1023,7 +1023,7 @@ Array [
     },
   },
   Object {
-    "cmd": "docker run --rm --name=renovate_gradle --label=renovate_child -v \\"/foo/bar\\":\\"/foo/bar\\" -e GRADLE_OPTS -w \\"/foo/bar\\" renovate/gradle bash -l -c \\"<gradlew> --init-script renovate-plugin.gradle renovate\\"",
+    "cmd": "docker run --rm --name=renovate_java --label=renovate_child -v \\"/foo/bar\\":\\"/foo/bar\\" -e GRADLE_OPTS -w \\"/foo/bar\\" renovate/java:11.0.12 bash -l -c \\"<gradlew> --init-script renovate-plugin.gradle renovate\\"",
     "options": Object {
       "cwd": "/foo/bar",
       "encoding": "utf-8",
@@ -1140,13 +1140,13 @@ Array [
 exports[`manager/gradle/deep/index extractPackageFile should use docker if required 2`] = `
 Array [
   Object {
-    "cmd": "docker pull renovate/gradle",
+    "cmd": "docker pull renovate/java:11.0.12",
     "options": Object {
       "encoding": "utf-8",
     },
   },
   Object {
-    "cmd": "docker ps --filter name=renovate_gradle -aq",
+    "cmd": "docker ps --filter name=renovate_java -aq",
     "options": Object {
       "encoding": "utf-8",
     },
@@ -1158,7 +1158,7 @@ Array [
     },
   },
   Object {
-    "cmd": "docker run --rm --name=renovate_gradle --label=renovate_child -v \\"/foo/bar\\":\\"/foo/bar\\" -e GRADLE_OPTS -w \\"/foo/bar\\" renovate/gradle bash -l -c \\"gradle --init-script renovate-plugin.gradle renovate\\"",
+    "cmd": "docker run --rm --name=renovate_java --label=renovate_child -v \\"/foo/bar\\":\\"/foo/bar\\" -e GRADLE_OPTS -w \\"/foo/bar\\" renovate/java:11.0.12 bash -l -c \\"install-tool gradle latest && gradle --init-script renovate-plugin.gradle renovate\\"",
     "options": Object {
       "cwd": "/foo/bar",
       "encoding": "utf-8",
diff --git a/lib/manager/gradle/deep/gradle-updates-report.spec.ts b/lib/manager/gradle/deep/gradle-updates-report.spec.ts
index 45a58fd4f7b42b5ee00be1715f8dbab5360a2d12..fc804f22cafb0b0960f9b3c631a00b965b9877e8 100644
--- a/lib/manager/gradle/deep/gradle-updates-report.spec.ts
+++ b/lib/manager/gradle/deep/gradle-updates-report.spec.ts
@@ -1,13 +1,14 @@
 import * as fs from 'fs-extra';
 import tmp, { DirectoryResult } from 'tmp-promise';
 import * as upath from 'upath';
+import { setGlobalConfig } from '../../../config/global';
 import { exec } from '../../../util/exec';
+import { extraEnv } from '../../gradle-wrapper/utils';
 import { ifSystemSupportsGradle } from './__testutil__/gradle';
 import {
   GRADLE_DEPENDENCY_REPORT_FILENAME,
   createRenovateGradlePlugin,
 } from './gradle-updates-report';
-import { extraEnv } from './utils';
 import { GRADLE_DEPENDENCY_REPORT_OPTIONS } from '.';
 
 const fixtures = 'lib/manager/gradle/deep/__fixtures__';
@@ -21,27 +22,44 @@ describe('manager/gradle/deep/gradle-updates-report', () => {
 
         beforeEach(async () => {
           workingDir = await tmp.dir({ unsafeCleanup: true });
+          setGlobalConfig({ localDir: workingDir.path });
         });
 
+        afterEach(() => workingDir.cleanup());
+
         it(`generates a report for Gradle version ${gradleVersion}`, async () => {
           await fs.copy(`${fixtures}/minimal-project`, workingDir.path);
           await fs.copy(
             `${fixtures}/gradle-wrappers/${gradleVersion}`,
             workingDir.path
           );
-          await createRenovateGradlePlugin(workingDir.path);
+          await createRenovateGradlePlugin();
 
           const gradlew = upath.join(workingDir.path, 'gradlew');
           await exec(`${gradlew} ${GRADLE_DEPENDENCY_REPORT_OPTIONS}`, {
             cwd: workingDir.path,
             extraEnv,
           });
-          // FIXME: explicit assert condition
           expect(
             fs.readJSONSync(
               `${workingDir.path}/${GRADLE_DEPENDENCY_REPORT_FILENAME}`
             )
-          ).toMatchSnapshot();
+          ).toMatchSnapshot([
+            {
+              dependencies: [
+                {
+                  group: 'org.apache.commons',
+                  name: 'commons-collections4',
+                  version: '4.4',
+                },
+              ],
+              project: 'minimal-test',
+              repositories: [
+                'https://jcenter.bintray.com/',
+                'https://plugins.gradle.org/m2',
+              ],
+            },
+          ]);
         }, 120000);
       }
     );
diff --git a/lib/manager/gradle/deep/gradle-updates-report.ts b/lib/manager/gradle/deep/gradle-updates-report.ts
index cbaf6c4020fdc0fc781eae7b7e9041396caf3b16..917caf1e675ffa350342c0daa1fc99ff75b1a2b8 100644
--- a/lib/manager/gradle/deep/gradle-updates-report.ts
+++ b/lib/manager/gradle/deep/gradle-updates-report.ts
@@ -1,7 +1,11 @@
-import { exists, readFile, writeFile } from 'fs-extra';
 import { join } from 'upath';
 import * as datasourceSbtPackage from '../../../datasource/sbt-package';
 import { logger } from '../../../logger';
+import {
+  localPathExists,
+  readLocalFile,
+  writeLocalFile,
+} from '../../../util/fs';
 import type {
   BuildDependency,
   GradleDependencyWithRepos,
@@ -11,7 +15,7 @@ import type {
 export const GRADLE_DEPENDENCY_REPORT_FILENAME = 'gradle-renovate-report.json';
 
 export async function createRenovateGradlePlugin(
-  localDir: string
+  gradleRoot = '.'
 ): Promise<void> {
   const content = `
 import groovy.json.JsonOutput
@@ -47,11 +51,11 @@ gradle.buildFinished {
    def json = JsonOutput.toJson(output)
    outputFile.write json
 }`;
-  const gradleInitFile = join(localDir, 'renovate-plugin.gradle');
+  const gradleInitFile = join(gradleRoot, 'renovate-plugin.gradle');
   logger.debug(
     'Creating renovate-plugin.gradle file with renovate gradle plugin'
   );
-  await writeFile(gradleInitFile, content);
+  await writeLocalFile(gradleInitFile, content);
 }
 
 async function readGradleReport(localDir: string): Promise<GradleProject[]> {
@@ -59,11 +63,11 @@ async function readGradleReport(localDir: string): Promise<GradleProject[]> {
     localDir,
     GRADLE_DEPENDENCY_REPORT_FILENAME
   );
-  if (!(await exists(renovateReportFilename))) {
+  if (!(await localPathExists(renovateReportFilename))) {
     return [];
   }
 
-  const contents = await readFile(renovateReportFilename, 'utf8');
+  const contents = await readLocalFile(renovateReportFilename, 'utf8');
   try {
     return JSON.parse(contents);
   } catch (err) {
diff --git a/lib/manager/gradle/deep/index.spec.ts b/lib/manager/gradle/deep/index.spec.ts
index 14d6defd9b01a061d0118036fa527c1a5125606b..36f4ee06d5b11e4c9187d06143e25be1f9958f32 100644
--- a/lib/manager/gradle/deep/index.spec.ts
+++ b/lib/manager/gradle/deep/index.spec.ts
@@ -1,29 +1,32 @@
-import { exec as _exec } from 'child_process';
 import type { Stats } from 'fs';
 import os from 'os';
-import _fs from 'fs-extra';
 import { join } from 'upath';
 import { extractAllPackageFiles, updateDependency } from '..';
-import { envMock, mockExecAll } from '../../../../test/exec-util';
+import { envMock, exec, mockExecAll } from '../../../../test/exec-util';
 import {
   addReplacingSerializer,
+  env,
+  fs,
   loadFixture,
-  mocked,
 } from '../../../../test/util';
 import { setGlobalConfig } from '../../../config/global';
 import type { RepoGlobalConfig } from '../../../config/types';
+import {
+  ReleaseResult,
+  getPkgReleases as _getPkgReleases,
+} from '../../../datasource';
 import * as docker from '../../../util/exec/docker';
-import * as _env from '../../../util/exec/env';
 import type { ExtractConfig } from '../../types';
 
 jest.mock('child_process');
-const exec: jest.Mock<typeof _exec> = _exec as never;
-
-jest.mock('fs-extra');
-const fs = mocked(_fs);
-
 jest.mock('../../../util/exec/env');
-const env = mocked(_env);
+jest.mock('../../../util/fs');
+jest.mock('../../../datasource');
+
+const getPkgReleases: jest.MockInstance<
+  ReturnType<typeof _getPkgReleases>,
+  jest.ArgsType<typeof _getPkgReleases>
+> = _getPkgReleases as never;
 
 const adminConfig: RepoGlobalConfig = {
   localDir: join('/foo/bar'),
@@ -52,22 +55,34 @@ dependency "bar:bar:This.Is.Valid.Version.Good.Luck"
 dependency "baz:baz:\${bazVersion}"
 `;
 
+const graddleWrapperPropertiesData = loadFixture(
+  '/gradle-wrappers/6/gradle/wrapper/gradle-wrapper.properties'
+);
+
 addReplacingSerializer('gradlew.bat', '<gradlew>');
 addReplacingSerializer('./gradlew', '<gradlew>');
 
+const javaReleases: ReleaseResult = {
+  releases: [
+    { version: '8.0.302' },
+    { version: '11.0.12' },
+    { version: '16.0.2' },
+  ],
+};
+
 describe('manager/gradle/deep/index', () => {
   const updatesReport = loadFixture('updatesReport.json');
 
   function setupMocks({
-    baseDir = '/foo/bar',
     wrapperFilename = `gradlew`,
+    wrapperPropertiesFilename = 'gradle/wrapper/gradle-wrapper.properties',
     pluginFilename = 'renovate-plugin.gradle',
     report = updatesReport,
     reportFilename = 'gradle-renovate-report.json',
     packageFilename = 'build.gradle',
     output = gradleOutput,
   } = {}) {
-    fs.stat.mockImplementationOnce((dirname) => {
+    fs.stat.mockImplementationOnce((_dirname) => {
       if (wrapperFilename) {
         return Promise.resolve({
           isFile: () => true,
@@ -75,14 +90,34 @@ describe('manager/gradle/deep/index', () => {
       }
       return Promise.reject();
     });
-    fs.writeFile.mockImplementationOnce((_filename, _content) => {});
-    fs.exists.mockImplementationOnce((_filename) => Promise.resolve(!!report));
-    fs.readFile.mockImplementationOnce((filename) =>
-      report ? Promise.resolve(report as never) : Promise.reject()
-    );
-    fs.readFile.mockImplementationOnce((filename) =>
-      Promise.resolve(buildGradle as never)
-    );
+    fs.writeLocalFile.mockImplementation((f, _content) => {
+      if (f?.endsWith(pluginFilename)) {
+        return Promise.resolve();
+      }
+      return Promise.reject();
+    });
+    fs.localPathExists.mockImplementation((f) => {
+      if (f?.endsWith(reportFilename)) {
+        return Promise.resolve(!!report);
+      }
+      if (f?.endsWith(wrapperPropertiesFilename)) {
+        return Promise.resolve(true);
+      }
+      return Promise.resolve(false);
+    });
+    fs.readLocalFile.mockImplementation((f) => {
+      if (f?.endsWith(reportFilename)) {
+        return report ? Promise.resolve(report) : Promise.reject();
+      }
+      if (f?.endsWith(packageFilename)) {
+        return Promise.resolve(buildGradle);
+      }
+      if (f?.endsWith(wrapperPropertiesFilename)) {
+        return Promise.resolve(graddleWrapperPropertiesData);
+      }
+      return Promise.resolve('');
+    });
+
     return mockExecAll(exec, output);
   }
 
@@ -107,18 +142,30 @@ describe('manager/gradle/deep/index', () => {
       const dependencies = await extractAllPackageFiles(config, [
         'build.gradle',
       ]);
-      // FIXME: explicit assert condition
-      expect(dependencies).toMatchSnapshot();
+      expect(dependencies).toHaveLength(1);
+      expect(dependencies[0]?.deps).toHaveLength(8);
+      expect(dependencies).toMatchSnapshot([
+        {
+          datasource: 'maven',
+          packageFile: 'build.gradle',
+        },
+      ]);
       expect(execSnapshots).toMatchSnapshot();
     });
 
     it('should return gradle.kts dependencies', async () => {
-      const execSnapshots = setupMocks();
+      const execSnapshots = setupMocks({ packageFilename: 'build.gradle.kts' });
       const dependencies = await extractAllPackageFiles(config, [
         'build.gradle.kts',
       ]);
-      // FIXME: explicit assert condition
-      expect(dependencies).toMatchSnapshot();
+      expect(dependencies).toHaveLength(1);
+      expect(dependencies[0]?.deps).toHaveLength(8);
+      expect(dependencies).toMatchSnapshot([
+        {
+          datasource: 'maven',
+          packageFile: 'build.gradle.kts',
+        },
+      ]);
       expect(execSnapshots).toMatchSnapshot();
     });
 
@@ -158,8 +205,14 @@ describe('manager/gradle/deep/index', () => {
       const dependencies = await extractAllPackageFiles(config, [
         'build.gradle',
       ]);
-      // FIXME: explicit assert condition
-      expect(dependencies).toMatchSnapshot();
+      expect(dependencies).toHaveLength(1);
+      expect(dependencies[0]?.deps).toHaveLength(3);
+      expect(dependencies).toMatchSnapshot([
+        {
+          datasource: 'maven',
+          packageFile: 'build.gradle',
+        },
+      ]);
       expect(execSnapshots).toMatchSnapshot();
     });
 
@@ -168,8 +221,14 @@ describe('manager/gradle/deep/index', () => {
       const dependencies = await extractAllPackageFiles(config, [
         'build.gradle',
       ]);
-      // FIXME: explicit assert condition
-      expect(dependencies).toMatchSnapshot();
+      expect(dependencies).toHaveLength(1);
+      expect(dependencies[0]?.deps).toHaveLength(8);
+      expect(dependencies).toMatchSnapshot([
+        {
+          datasource: 'maven',
+          packageFile: 'build.gradle',
+        },
+      ]);
       expect(execSnapshots).toMatchSnapshot();
     });
 
@@ -179,23 +238,42 @@ describe('manager/gradle/deep/index', () => {
       const dependencies = await extractAllPackageFiles(config, [
         'build.gradle',
       ]);
-      // FIXME: explicit assert condition
-      expect(dependencies).toMatchSnapshot();
+      expect(dependencies).toHaveLength(1);
+      expect(dependencies[0]?.deps).toHaveLength(8);
+      expect(dependencies).toMatchSnapshot([
+        {
+          datasource: 'maven',
+          packageFile: 'build.gradle',
+        },
+      ]);
       expect(execSnapshots).toMatchSnapshot();
     });
 
     it('should execute gradle if gradlew is not available', async () => {
-      const execSnapshots = setupMocks({ wrapperFilename: null });
+      const execSnapshots = setupMocks({
+        wrapperFilename: null,
+        wrapperPropertiesFilename: null,
+      });
       const dependencies = await extractAllPackageFiles(config, [
         'build.gradle',
       ]);
-      // FIXME: explicit assert condition
-      expect(dependencies).toMatchSnapshot();
+      expect(dependencies).toHaveLength(1);
+      expect(dependencies[0]?.deps).toHaveLength(8);
+      expect(dependencies).toMatchSnapshot([
+        {
+          datasource: 'maven',
+          packageFile: 'build.gradle',
+        },
+      ]);
       expect(execSnapshots).toMatchSnapshot();
     });
 
     it('should return null and gradle should not be executed if no root build.gradle', async () => {
-      const execSnapshots = setupMocks({ wrapperFilename: null, report: null });
+      const execSnapshots = setupMocks({
+        wrapperFilename: null,
+        report: null,
+        wrapperPropertiesFilename: null,
+      });
       const packageFiles = ['foo/build.gradle'];
       expect(await extractAllPackageFiles(config, packageFiles)).toBeNull();
       expect(execSnapshots).toBeEmpty();
@@ -203,8 +281,9 @@ describe('manager/gradle/deep/index', () => {
 
     it('should return gradle dependencies for build.gradle in subdirectories if there is gradlew in the same directory', async () => {
       const execSnapshots = setupMocks({
-        baseDir: '/foo/bar/',
         wrapperFilename: 'baz/qux/gradlew',
+        wrapperPropertiesFilename:
+          'baz/qux/gradle/wrapper/gradle-wrapper.properties',
         packageFilename: 'baz/qux/build.gradle',
         reportFilename: 'baz/qux/gradle-renovate-report.json',
         pluginFilename: 'baz/qux/renovate-plugin.gradle',
@@ -213,30 +292,55 @@ describe('manager/gradle/deep/index', () => {
       const dependencies = await extractAllPackageFiles(config, [
         'baz/qux/build.gradle',
       ]);
-      // FIXME: explicit assert condition
-      expect(dependencies).toMatchSnapshot();
+      expect(dependencies).toHaveLength(1);
+      expect(dependencies[0]?.deps).toHaveLength(8);
+      expect(dependencies).toMatchSnapshot([
+        {
+          datasource: 'maven',
+          packageFile: 'baz/qux/build.gradle',
+        },
+      ]);
       expect(execSnapshots).toMatchSnapshot();
     });
 
     it('should use docker if required', async () => {
       setGlobalConfig(dockerAdminConfig);
-      const execSnapshots = setupMocks({ wrapperFilename: null });
+      const execSnapshots = setupMocks({
+        wrapperFilename: null,
+        wrapperPropertiesFilename: null,
+      });
+      getPkgReleases.mockResolvedValueOnce(javaReleases);
       const dependencies = await extractAllPackageFiles(config, [
         'build.gradle',
       ]);
-      // FIXME: explicit assert condition
-      expect(dependencies).toMatchSnapshot();
+      expect(dependencies).toHaveLength(1);
+      expect(dependencies[0]?.deps).toHaveLength(8);
+      expect(dependencies).toMatchSnapshot([
+        {
+          datasource: 'maven',
+          packageFile: 'build.gradle',
+        },
+      ]);
+      expect(execSnapshots[0].cmd).toEqual('docker pull renovate/java:11.0.12');
       expect(execSnapshots).toMatchSnapshot();
     });
 
     it('should use docker even if gradlew is available', async () => {
       setGlobalConfig(dockerAdminConfig);
       const execSnapshots = setupMocks();
+      getPkgReleases.mockResolvedValueOnce(javaReleases);
       const dependencies = await extractAllPackageFiles(config, [
         'build.gradle',
       ]);
-      // FIXME: explicit assert condition
-      expect(dependencies).toMatchSnapshot();
+      expect(dependencies).toHaveLength(1);
+      expect(dependencies[0]?.deps).toHaveLength(8);
+      expect(dependencies).toMatchSnapshot([
+        {
+          datasource: 'maven',
+          packageFile: 'build.gradle',
+        },
+      ]);
+      expect(execSnapshots[0].cmd).toEqual('docker pull renovate/java:11.0.12');
       expect(execSnapshots).toMatchSnapshot();
     });
 
@@ -244,11 +348,19 @@ describe('manager/gradle/deep/index', () => {
       setGlobalConfig(dockerAdminConfig);
       jest.spyOn(os, 'platform').mockReturnValueOnce('win32');
       const execSnapshots = setupMocks({ wrapperFilename: 'gradlew.bat' });
+      getPkgReleases.mockResolvedValueOnce(javaReleases);
       const dependencies = await extractAllPackageFiles(config, [
         'build.gradle',
       ]);
-      // FIXME: explicit assert condition
-      expect(dependencies).toMatchSnapshot();
+      expect(dependencies).toHaveLength(1);
+      expect(dependencies[0]?.deps).toHaveLength(8);
+      expect(dependencies).toMatchSnapshot([
+        {
+          datasource: 'maven',
+          packageFile: 'build.gradle',
+        },
+      ]);
+      expect(execSnapshots[0].cmd).toEqual('docker pull renovate/java:11.0.12');
       expect(execSnapshots).toMatchSnapshot();
     });
   });
diff --git a/lib/manager/gradle/deep/index.ts b/lib/manager/gradle/deep/index.ts
index 66c7135652baf79e0a881157a7c8bb51b4026c91..bab24401d3ec656c073691889d08d596cd5522b3 100644
--- a/lib/manager/gradle/deep/index.ts
+++ b/lib/manager/gradle/deep/index.ts
@@ -1,5 +1,4 @@
 import type { Stats } from 'fs';
-import { stat } from 'fs-extra';
 import upath from 'upath';
 import { getGlobalConfig } from '../../../config/global';
 import { TEMPORARY_ERROR } from '../../../constants/error-messages';
@@ -7,7 +6,13 @@ import * as datasourceMaven from '../../../datasource/maven';
 import { logger } from '../../../logger';
 import { ExternalHostError } from '../../../types/errors/external-host-error';
 import { ExecOptions, exec } from '../../../util/exec';
-import { readLocalFile } from '../../../util/fs';
+import { readLocalFile, stat } from '../../../util/fs';
+import {
+  extraEnv,
+  getJavaVersioning,
+  gradleWrapperFileName,
+  prepareGradleCommand,
+} from '../../gradle-wrapper/utils';
 import type {
   ExtractConfig,
   PackageFile,
@@ -24,7 +29,7 @@ import {
   extractDependenciesFromUpdatesReport,
 } from './gradle-updates-report';
 import type { GradleDependency } from './types';
-import { extraEnv, gradleWrapperFileName, prepareGradleCommand } from './utils';
+import { getDockerConstraint, getDockerPreCommands } from './utils';
 
 export const GRADLE_DEPENDENCY_REPORT_OPTIONS =
   '--init-script renovate-plugin.gradle renovate';
@@ -46,16 +51,17 @@ async function prepareGradleCommandFallback(
 export async function executeGradle(
   config: ExtractConfig,
   cwd: string,
-  gradlew: Stats | null
+  gradlew: Stats | null,
+  gradleRoot = '.'
 ): Promise<void> {
   let stdout: string;
   let stderr: string;
-  let timeout;
+  let timeout: number;
   if (config.gradle?.timeout) {
     timeout = config.gradle.timeout * 1000;
   }
   const cmd = await prepareGradleCommandFallback(
-    gradleWrapperFileName(config),
+    gradleWrapperFileName(),
     cwd,
     gradlew,
     GRADLE_DEPENDENCY_REPORT_OPTIONS
@@ -64,7 +70,11 @@ export async function executeGradle(
     timeout,
     cwd,
     docker: {
-      image: 'gradle',
+      image: 'java',
+      tagConstraint:
+        config.constraints?.java ?? (await getDockerConstraint(gradleRoot)),
+      tagScheme: getJavaVersioning(),
+      preCommands: await getDockerPreCommands(gradleRoot),
     },
     extraEnv,
   };
@@ -94,7 +104,7 @@ export async function extractAllPackageFiles(
   const { localDir } = getGlobalConfig();
   for (const packageFile of packageFiles) {
     const dirname = upath.dirname(packageFile);
-    const gradlewPath = upath.join(dirname, gradleWrapperFileName(config));
+    const gradlewPath = upath.join(dirname, gradleWrapperFileName());
     gradlew = await stat(upath.join(localDir, gradlewPath)).catch(() => null);
 
     if (['build.gradle', 'build.gradle.kts'].includes(packageFile)) {
@@ -114,14 +124,15 @@ export async function extractAllPackageFiles(
   }
   logger.debug('Extracting dependencies from all gradle files');
 
-  const cwd = upath.join(localDir, upath.dirname(rootBuildGradle));
+  const gradleRoot = upath.dirname(rootBuildGradle);
+  const cwd = upath.join(localDir, gradleRoot);
 
-  await createRenovateGradlePlugin(cwd);
-  await executeGradle(config, cwd, gradlew);
+  await createRenovateGradlePlugin(gradleRoot);
+  await executeGradle(config, cwd, gradlew, gradleRoot);
 
   init();
 
-  const dependencies = await extractDependenciesFromUpdatesReport(cwd);
+  const dependencies = await extractDependenciesFromUpdatesReport(gradleRoot);
   if (dependencies.length === 0) {
     return [];
   }
diff --git a/lib/manager/gradle/deep/utils.ts b/lib/manager/gradle/deep/utils.ts
index 48afb4d804f3a5a77ee188733570e382d9edf62b..4b8aa9e1181eec1c8b322ad5c78038733f4d0294 100644
--- a/lib/manager/gradle/deep/utils.ts
+++ b/lib/manager/gradle/deep/utils.ts
@@ -1,44 +1,42 @@
-import type { Stats } from 'fs';
-import os from 'os';
-import { chmod } from 'fs-extra';
-import upath from 'upath';
+import { join } from 'upath';
 import { getGlobalConfig } from '../../../config/global';
-import type { ExtractConfig } from '../../types';
-
-export const extraEnv = {
-  GRADLE_OPTS:
-    '-Dorg.gradle.parallel=true -Dorg.gradle.configureondemand=true -Dorg.gradle.daemon=false -Dorg.gradle.caching=false',
-};
-
-export function gradleWrapperFileName(config: ExtractConfig): string {
-  if (
-    os.platform() === 'win32' &&
-    getGlobalConfig()?.binarySource !== 'docker'
-  ) {
-    return 'gradlew.bat';
+import { localPathExists, readLocalFile } from '../../../util/fs';
+import {
+  extractGradleVersion,
+  getJavaContraint,
+} from '../../gradle-wrapper/utils';
+
+const GradleWrapperProperties = 'gradle/wrapper/gradle-wrapper.properties';
+
+export async function getDockerConstraint(
+  gradleRoot: string
+): Promise<string | null> {
+  if (getGlobalConfig()?.binarySource !== 'docker') {
+    // ignore
+    return null;
   }
-  return './gradlew';
+
+  const fileContent = await readLocalFile(
+    join(gradleRoot, GradleWrapperProperties),
+    'utf8'
+  );
+
+  const version = extractGradleVersion(fileContent);
+
+  return getJavaContraint(version);
 }
 
-export async function prepareGradleCommand(
-  gradlewName: string,
-  cwd: string,
-  gradlew: Stats | null,
-  args: string | null
-): Promise<string> {
-  /* eslint-disable no-bitwise */
-  // istanbul ignore if
-  if (gradlew?.isFile() === true) {
-    // if the file is not executable by others
-    if ((gradlew.mode & 0o1) === 0) {
-      // add the execution permission to the owner, group and others
-      await chmod(upath.join(cwd, gradlewName), gradlew.mode | 0o111);
-    }
-    if (args === null) {
-      return gradlewName;
-    }
-    return `${gradlewName} ${args}`;
+export async function getDockerPreCommands(
+  gradleRoot: string
+): Promise<string[]> {
+  if (getGlobalConfig()?.binarySource !== 'docker') {
+    // ignore
+    return null;
+  }
+
+  if (await localPathExists(join(gradleRoot, GradleWrapperProperties))) {
+    return null;
   }
-  /* eslint-enable no-bitwise */
-  return null;
+
+  return ['install-tool gradle latest'];
 }
diff --git a/lib/manager/types.ts b/lib/manager/types.ts
index caed04ef60d980e1ae2abd608828ea5a4efc0ee2..000c830f0db307a00c5965eef2c5548c59ecb6a5 100644
--- a/lib/manager/types.ts
+++ b/lib/manager/types.ts
@@ -18,6 +18,7 @@ export interface ManagerData<T> {
 }
 
 export interface ExtractConfig {
+  constraints?: Record<string, string>;
   registryUrls?: string[];
   endpoint?: string;
   gradle?: { timeout?: number };