diff --git a/docs/usage/docker.md b/docs/usage/docker.md
index 28838e44b64e83c054a5a69e004839a768075fec..ca3991dd86f7c00c5ecdfdace1b2f6d952c36b61 100644
--- a/docs/usage/docker.md
+++ b/docs/usage/docker.md
@@ -256,7 +256,7 @@ Renovate can authenticate with AWS ECR using AWS access key id & secret as the u
 
 #### Google Container Registry / Google Artifact Registry
 
-##### Using Application Default Credentials / Workload Identity
+##### Using Application Default Credentials / Workload Identity (Self-Hosted only)
 
 Just configure [ADC](https://cloud.google.com/docs/authentication/provide-credentials-adc) /
 [Workload Identity](https://cloud.google.com/kubernetes-engine/docs/how-to/workload-identity) as normal and _don't_
diff --git a/docs/usage/java.md b/docs/usage/java.md
index 8d1720771f73bbcf151857cec9f5bc4dc513b336..744a109c51fc0de04c1fc4ab9da0bd59d135aebc 100644
--- a/docs/usage/java.md
+++ b/docs/usage/java.md
@@ -131,3 +131,68 @@ module.exports = {
   ],
 };
 ```
+
+### Google Artifact Registry
+
+There are multiple ways to configure Renovate to access Artifact Registry.
+
+#### Using Application Default Credentials / Workload Identity (Self-Hosted only)
+
+Just configure [ADC](https://cloud.google.com/docs/authentication/provide-credentials-adc) /
+[Workload Identity](https://cloud.google.com/kubernetes-engine/docs/how-to/workload-identity) as normal and _don't_
+provide a username, password or token. Renovate will automatically retrieve the credentials using the
+google-auth-library.
+
+#### Using long-lived service account credentials
+
+To access the Google Artifact Registry, use the JSON service account with `Basic` authentication, and use the:
+
+- `_json_key_base64` as username
+- full Google Cloud Platform service account JSON as password
+
+To avoid JSON-in-JSON wrapping, which can cause problems, encode the JSON service account beforehand.
+
+1. Download your JSON service account and store it on your machine. Make sure that the service account has `read` (and only `read`) permissions to your artifacts
+2. Base64 encode the service account credentials by running `cat service-account.json | base64`
+3. Add the encoded service account to your configuration file
+
+   1. If you want to add it to your self-hosted configuration file:
+
+      ```json
+      {
+        "hostRules": [
+          {
+            "matchHost": "europe-maven.pkg.dev",
+            "username": "_json_key_base64",
+            "password": "<base64 service account>"
+          }
+        ]
+      }
+      ```
+
+   2. If you want to add it to your repository Renovate configuration file, [encrypt](./configuration-options.md#encrypted) it and then add it:
+
+      ```json
+      {
+        "hostRules": [
+          {
+            "matchHost": "europe-maven.pkg.dev",
+            "username": "_json_key_base64",
+            "encrypted": {
+              "password": "<encrypted base64 service account>"
+            }
+          }
+        ]
+      }
+      ```
+
+4. Add the following to the `packageRules` in your repository Renovate configuration file:
+
+   ```json
+   {
+     "matchManagers": ["maven", "gradle"],
+     "registryUrls": [
+       "https://europe-maven.pkg.dev/<my-gcp-project>/<my-repository>"
+     ]
+   }
+   ```
diff --git a/lib/modules/datasource/docker/common.ts b/lib/modules/datasource/docker/common.ts
index e6d7ba57fd5081e8dfd36e0a5197a179299727f8..e312ab7dab1d811e570f66475405eed048e82b14 100644
--- a/lib/modules/datasource/docker/common.ts
+++ b/lib/modules/datasource/docker/common.ts
@@ -26,8 +26,9 @@ import {
   trimTrailingSlash,
 } from '../../../util/url';
 import { api as dockerVersioning } from '../../versioning/docker';
+import { getGoogleAuthToken } from '../util';
 import { ecrRegex, getECRAuthToken } from './ecr';
-import { getGoogleAccessToken, googleRegex } from './google';
+import { googleRegex } from './google';
 import type { OciHelmConfig } from './schema';
 import type { RegistryRepository } from './types';
 
@@ -110,23 +111,14 @@ export async function getAuthHeaders(
         { registryHost, dockerRepository },
         `Using google auth for Docker registry`
       );
-      try {
-        const accessToken = await getGoogleAccessToken();
-        if (accessToken) {
-          const auth = Buffer.from(
-            `${'oauth2accesstoken'}:${accessToken}`
-          ).toString('base64');
-          opts.headers = { authorization: `Basic ${auth}` };
-        }
-      } catch (err) /* istanbul ignore next */ {
-        if (err.message?.includes('Could not load the default credentials')) {
-          logger.once.debug(
-            { registryHost, dockerRepository },
-            'Could not get Google access token, using no auth'
-          );
-        } else {
-          throw err;
-        }
+      const auth = await getGoogleAuthToken();
+      if (auth) {
+        opts.headers = { authorization: `Basic ${auth}` };
+      } else {
+        logger.once.debug(
+          { registryHost, dockerRepository },
+          'Could not get Google access token, using no auth'
+        );
       }
     } else if (opts.username && opts.password) {
       logger.trace(
diff --git a/lib/modules/datasource/docker/google.ts b/lib/modules/datasource/docker/google.ts
index e9531742ca7c3c225ed029f1eae6dc4e6d40a424..cec035f75865cfa07fa5909dbf816d190a8a2239 100644
--- a/lib/modules/datasource/docker/google.ts
+++ b/lib/modules/datasource/docker/google.ts
@@ -1,24 +1,5 @@
-import { GoogleAuth } from 'google-auth-library';
-import { logger } from '../../../logger';
 import { regEx } from '../../../util/regex';
-import { addSecretForSanitizing } from '../../../util/sanitize';
 
 export const googleRegex = regEx(
   /(((eu|us|asia)\.)?gcr\.io|[a-z0-9-]+-docker\.pkg\.dev)/
 );
-
-export async function getGoogleAccessToken(): Promise<string | null> {
-  const googleAuth: GoogleAuth = new GoogleAuth({
-    scopes: 'https://www.googleapis.com/auth/cloud-platform',
-  });
-  const accessToken = await googleAuth.getAccessToken();
-  if (accessToken) {
-    // sanitize token
-    addSecretForSanitizing(accessToken);
-    return accessToken;
-  }
-  logger.warn(
-    'Could not retrieve access token using google-auth-library getAccessToken'
-  );
-  return null;
-}
diff --git a/lib/modules/datasource/maven/index.spec.ts b/lib/modules/datasource/maven/index.spec.ts
index 0f8cc2fb7573ebac21ad28772491deef9dbf26a4..ab4741d35f3db848033459202a4f3bb71c33fd55 100644
--- a/lib/modules/datasource/maven/index.spec.ts
+++ b/lib/modules/datasource/maven/index.spec.ts
@@ -1,16 +1,25 @@
+import { GoogleAuth as _googleAuth } from 'google-auth-library';
 import { ReleaseResult, getPkgReleases } from '..';
 import { Fixtures } from '../../../../test/fixtures';
 import * as httpMock from '../../../../test/http-mock';
+import { mocked } from '../../../../test/util';
 import { EXTERNAL_HOST_ERROR } from '../../../constants/error-messages';
 import * as hostRules from '../../../util/host-rules';
 import { id as versioning } from '../../versioning/maven';
 import { MavenDatasource } from '.';
 
+const googleAuth = mocked(_googleAuth);
+jest.mock('google-auth-library');
+
 const datasource = MavenDatasource.id;
 
 const baseUrl = 'https://repo.maven.apache.org/maven2';
 const baseUrlCustom = 'https://custom.registry.renovatebot.com';
 
+const arRegistry = 'maven.pkg.dev/some-project/some-repository';
+const baseUrlAR = `artifactregistry://${arRegistry}`;
+const baseUrlARHttps = `https://${arRegistry}`;
+
 interface SnapshotOpts {
   version: string;
   jarStatus?: number;
@@ -432,6 +441,116 @@ describe('modules/datasource/maven/index', () => {
     expect(res).toMatchSnapshot();
   });
 
+  it('supports artifactregistry urls with auth', async () => {
+    const metadataPaths = [
+      '/org/example/package/maven-metadata.xml',
+      '/org/example/package/1.0.3-SNAPSHOT/maven-metadata.xml',
+      '/org/example/package/1.0.4-SNAPSHOT/maven-metadata.xml',
+      '/org/example/package/1.0.5-SNAPSHOT/maven-metadata.xml',
+    ];
+    const pomfilePath = '/org/example/package/2.0.0/package-2.0.0.pom';
+    hostRules.clear();
+
+    for (const path of metadataPaths) {
+      httpMock
+        .scope(baseUrlARHttps)
+        .get(path)
+        .matchHeader(
+          'authorization',
+          'Basic b2F1dGgyYWNjZXNzdG9rZW46c29tZS10b2tlbg=='
+        )
+        .reply(200, Fixtures.get('metadata.xml'));
+    }
+
+    httpMock
+      .scope(baseUrlARHttps)
+      .get(pomfilePath)
+      .matchHeader(
+        'authorization',
+        'Basic b2F1dGgyYWNjZXNzdG9rZW46c29tZS10b2tlbg=='
+      )
+      .reply(200, Fixtures.get('pom.xml'));
+
+    googleAuth.mockImplementation(
+      jest.fn().mockImplementation(() => ({
+        getAccessToken: jest.fn().mockResolvedValue('some-token'),
+      }))
+    );
+
+    const res = await get('org.example:package', baseUrlAR);
+
+    expect(res).toEqual({
+      display: 'org.example:package',
+      group: 'org.example',
+      homepage: 'https://package.example.org/about',
+      name: 'package',
+      registryUrl:
+        'artifactregistry://maven.pkg.dev/some-project/some-repository',
+      releases: [
+        { version: '0.0.1' },
+        { version: '1.0.0' },
+        { version: '1.0.1' },
+        { version: '1.0.2' },
+        { version: '1.0.3-SNAPSHOT' },
+        { version: '1.0.4-SNAPSHOT' },
+        { version: '1.0.5-SNAPSHOT' },
+        { version: '2.0.0' },
+      ],
+    });
+    expect(googleAuth).toHaveBeenCalledTimes(5);
+  });
+
+  it('supports artifactregistry urls without auth', async () => {
+    const metadataPaths = [
+      '/org/example/package/maven-metadata.xml',
+      '/org/example/package/1.0.3-SNAPSHOT/maven-metadata.xml',
+      '/org/example/package/1.0.4-SNAPSHOT/maven-metadata.xml',
+      '/org/example/package/1.0.5-SNAPSHOT/maven-metadata.xml',
+    ];
+    const pomfilePath = '/org/example/package/2.0.0/package-2.0.0.pom';
+    hostRules.clear();
+
+    for (const path of metadataPaths) {
+      httpMock
+        .scope(baseUrlARHttps)
+        .get(path)
+        .reply(200, Fixtures.get('metadata.xml'));
+    }
+
+    httpMock
+      .scope(baseUrlARHttps)
+      .get(pomfilePath)
+      .reply(200, Fixtures.get('pom.xml'));
+
+    googleAuth.mockImplementation(
+      jest.fn().mockImplementation(() => ({
+        getAccessToken: jest.fn().mockResolvedValue(undefined),
+      }))
+    );
+
+    const res = await get('org.example:package', baseUrlAR);
+
+    expect(res).toEqual({
+      display: 'org.example:package',
+      group: 'org.example',
+      homepage: 'https://package.example.org/about',
+      name: 'package',
+      registryUrl:
+        'artifactregistry://maven.pkg.dev/some-project/some-repository',
+      releases: [
+        { version: '0.0.1' },
+        { version: '1.0.0' },
+        { version: '1.0.1' },
+        { version: '1.0.2' },
+        { version: '1.0.3-SNAPSHOT' },
+        { version: '1.0.4-SNAPSHOT' },
+        { version: '1.0.5-SNAPSHOT' },
+        { version: '2.0.0' },
+      ],
+    });
+    expect(googleAuth).toHaveBeenCalledTimes(5);
+  });
+
   describe('fetching parent info', () => {
     const parentPackage = {
       dep: 'org.example:parent',
diff --git a/lib/modules/datasource/maven/util.ts b/lib/modules/datasource/maven/util.ts
index a41a5b1e3bd420fb5884d224f08aba624ac243dc..9dee2d60fde052709ed836dbb3439091eb68a827 100644
--- a/lib/modules/datasource/maven/util.ts
+++ b/lib/modules/datasource/maven/util.ts
@@ -6,13 +6,18 @@ import { HOST_DISABLED } from '../../../constants/error-messages';
 import { logger } from '../../../logger';
 import { ExternalHostError } from '../../../types/errors/external-host-error';
 import type { Http } from '../../../util/http';
-import type { HttpResponse } from '../../../util/http/types';
+import type {
+  HttpOptions,
+  HttpRequestOptions,
+  HttpResponse,
+} from '../../../util/http/types';
 import { regEx } from '../../../util/regex';
 import { getS3Client, parseS3Url } from '../../../util/s3';
 import { streamToString } from '../../../util/streams';
 import { parseUrl } from '../../../util/url';
 import { normalizeDate } from '../metadata';
 import type { ReleaseResult } from '../types';
+import { getGoogleAuthToken } from '../util';
 import { MAVEN_REPO } from './common';
 import type {
   HttpResourceCheckResult,
@@ -63,11 +68,12 @@ function isUnsupportedHostError(err: { name: string }): boolean {
 
 export async function downloadHttpProtocol(
   http: Http,
-  pkgUrl: URL | string
+  pkgUrl: URL | string,
+  opts: HttpOptions & HttpRequestOptions<string> = {}
 ): Promise<Partial<HttpResponse>> {
   let raw: HttpResponse;
   try {
-    raw = await http.get(pkgUrl.toString());
+    raw = await http.get(pkgUrl.toString(), opts);
     return raw;
   } catch (err) {
     const failedUrl = pkgUrl.toString();
@@ -139,6 +145,30 @@ export async function downloadS3Protocol(pkgUrl: URL): Promise<string | null> {
   return null;
 }
 
+export async function downloadArtifactRegistryProtocol(
+  http: Http,
+  pkgUrl: URL
+): Promise<Partial<HttpResponse>> {
+  const opts: HttpOptions = {};
+  const host = pkgUrl.host;
+  const path = pkgUrl.pathname;
+
+  logger.trace({ host, path }, `Using google auth for Maven repository`);
+  const auth = await getGoogleAuthToken();
+  if (auth) {
+    opts.headers = { authorization: `Basic ${auth}` };
+  } else {
+    logger.once.debug(
+      { host, path },
+      'Could not get Google access token, using no auth'
+    );
+  }
+
+  const url = pkgUrl.toString().replace('artifactregistry:', 'https:');
+
+  return downloadHttpProtocol(http, url, opts);
+}
+
 async function checkHttpResource(
   http: Http,
   pkgUrl: URL
@@ -259,6 +289,13 @@ export async function downloadMavenXml(
     case 's3:':
       rawContent = (await downloadS3Protocol(pkgUrl)) ?? undefined;
       break;
+    case 'artifactregistry:':
+      ({
+        authorization,
+        body: rawContent,
+        statusCode,
+      } = await downloadArtifactRegistryProtocol(http, pkgUrl));
+      break;
     default:
       logger.debug(`Unsupported Maven protocol url:${pkgUrl.toString()}`);
       return {};
diff --git a/lib/modules/datasource/util.ts b/lib/modules/datasource/util.ts
index 7293769a2b33e1a1165fe2f49070d8df70dfd4be..10e68673e974e8fb17d9803913bd75e9a211d0ff 100644
--- a/lib/modules/datasource/util.ts
+++ b/lib/modules/datasource/util.ts
@@ -1,5 +1,8 @@
 import is from '@sindresorhus/is';
+import { GoogleAuth } from 'google-auth-library';
+import { logger } from '../../logger';
 import type { HttpResponse } from '../../util/http/types';
+import { addSecretForSanitizing } from '../../util/sanitize';
 
 const JFROG_ARTIFACTORY_RES_HEADER = 'x-jfrog-version';
 
@@ -8,3 +11,28 @@ export function isArtifactoryServer<T = unknown>(
 ): boolean {
   return is.string(res?.headers[JFROG_ARTIFACTORY_RES_HEADER]);
 }
+
+export async function getGoogleAuthToken(): Promise<string | null> {
+  try {
+    const googleAuth: GoogleAuth = new GoogleAuth({
+      scopes: 'https://www.googleapis.com/auth/cloud-platform',
+    });
+    const accessToken = await googleAuth.getAccessToken();
+    if (accessToken) {
+      // sanitize token
+      addSecretForSanitizing(accessToken);
+      return Buffer.from(`oauth2accesstoken:${accessToken}`).toString('base64');
+    } else {
+      logger.warn(
+        'Could not retrieve access token using google-auth-library getAccessToken'
+      );
+    }
+  } catch (err) {
+    if (err.message?.includes('Could not load the default credentials')) {
+      return null;
+    } else {
+      throw err;
+    }
+  }
+  return null;
+}
diff --git a/lib/modules/datasource/utils.spec.ts b/lib/modules/datasource/utils.spec.ts
index 20d8161e84c5310683685ac4ffaf2120050697f7..395dbcc9120b5f6c280e5cfbdec5d489c9df90ea 100644
--- a/lib/modules/datasource/utils.spec.ts
+++ b/lib/modules/datasource/utils.spec.ts
@@ -1,5 +1,10 @@
+import { GoogleAuth as _googleAuth } from 'google-auth-library';
+import { mocked } from '../../../test/util';
 import type { HttpResponse } from '../../util/http/types';
-import { isArtifactoryServer } from './util';
+import { getGoogleAuthToken, isArtifactoryServer } from './util';
+
+const googleAuth = mocked(_googleAuth);
+jest.mock('google-auth-library');
 
 describe('modules/datasource/utils', () => {
   it('is artifactory server invalid', () => {
@@ -19,4 +24,50 @@ describe('modules/datasource/utils', () => {
     };
     expect(isArtifactoryServer(response)).toBeTrue();
   });
+
+  it('retrieves a Google Access token', async () => {
+    googleAuth.mockImplementationOnce(
+      jest.fn().mockImplementationOnce(() => ({
+        getAccessToken: jest.fn().mockResolvedValue('some-token'),
+      }))
+    );
+
+    const res = await getGoogleAuthToken();
+    expect(res).toBe('b2F1dGgyYWNjZXNzdG9rZW46c29tZS10b2tlbg==');
+  });
+
+  it('no Google Access token results in null', async () => {
+    googleAuth.mockImplementationOnce(
+      jest.fn().mockImplementationOnce(() => ({
+        getAccessToken: jest.fn().mockReturnValue(''),
+      }))
+    );
+
+    const res = await getGoogleAuthToken();
+    expect(res).toBeNull();
+  });
+
+  it('Google Access token error throws an exception', async () => {
+    const err = 'some-error';
+    googleAuth.mockImplementationOnce(
+      jest.fn().mockImplementationOnce(() => ({
+        getAccessToken: jest.fn().mockRejectedValue(new Error(err)),
+      }))
+    );
+
+    await expect(getGoogleAuthToken()).rejects.toThrow('some-error');
+  });
+
+  it('Google Access token could not load default credentials', async () => {
+    googleAuth.mockImplementationOnce(
+      jest.fn().mockImplementationOnce(() => ({
+        getAccessToken: jest.fn().mockRejectedValue({
+          message: 'Could not load the default credentials',
+        }),
+      }))
+    );
+
+    const res = await getGoogleAuthToken();
+    expect(res).toBeNull();
+  });
 });