diff --git a/lib/datasource/git-refs/index.ts b/lib/datasource/git-refs/index.ts
index e814bb59e5d078e2a21e57fd03e6f124c00e12f4..0a5abffa94bf18c3146621bae7316f67c3cb318e 100644
--- a/lib/datasource/git-refs/index.ts
+++ b/lib/datasource/git-refs/index.ts
@@ -13,9 +13,10 @@ const cacheMinutes = 10;
 // git will prompt for known hosts or passwords, unless we activate BatchMode
 process.env.GIT_SSH_COMMAND = 'ssh -o BatchMode=yes';
 
-export async function getRawRefs({
-  lookupName,
-}: GetReleasesConfig): Promise<RawRefs[] | null> {
+export async function getRawRefs(
+  { lookupName }: GetReleasesConfig,
+  hostType: string
+): Promise<RawRefs[] | null> {
   const git = simpleGit();
   const cacheNamespace = 'git-raw-refs';
 
@@ -29,7 +30,9 @@ export async function getRawRefs({
   }
 
   // fetch remote tags
-  const lsRemote = await git.listRemote([getRemoteUrlWithToken(lookupName)]);
+  const lsRemote = await git.listRemote([
+    getRemoteUrlWithToken(lookupName, hostType),
+  ]);
   if (!lsRemote) {
     return null;
   }
@@ -70,7 +73,7 @@ export async function getRawRefs({
 export async function getReleases({
   lookupName,
 }: GetReleasesConfig): Promise<ReleaseResult | null> {
-  const rawRefs: RawRefs[] = await getRawRefs({ lookupName });
+  const rawRefs: RawRefs[] = await getRawRefs({ lookupName }, id);
 
   const refs = rawRefs
     .filter((ref) => ref.type === 'tags' || ref.type === 'heads')
@@ -97,7 +100,7 @@ export async function getDigest(
   { lookupName }: Partial<DigestConfig>,
   newValue?: string
 ): Promise<string | null> {
-  const rawRefs: RawRefs[] = await getRawRefs({ lookupName });
+  const rawRefs: RawRefs[] = await getRawRefs({ lookupName }, id);
   const findValue = newValue || 'HEAD';
   const ref = rawRefs.find((rawRef) => rawRef.value === findValue);
   if (ref) {
diff --git a/lib/datasource/git-tags/index.ts b/lib/datasource/git-tags/index.ts
index b0a3956de2f7e6b06509e4ee11e4d7c931890941..36ed8dee8fd031fbdc975abb03be2c653f877308 100644
--- a/lib/datasource/git-tags/index.ts
+++ b/lib/datasource/git-tags/index.ts
@@ -8,7 +8,7 @@ export const customRegistrySupport = false;
 export async function getReleases({
   lookupName,
 }: GetReleasesConfig): Promise<ReleaseResult | null> {
-  const rawRefs = await gitRefs.getRawRefs({ lookupName });
+  const rawRefs = await gitRefs.getRawRefs({ lookupName }, id);
 
   if (rawRefs === null) {
     return null;
@@ -36,7 +36,7 @@ export async function getDigest(
   { lookupName }: Partial<DigestConfig>,
   newValue?: string
 ): Promise<string | null> {
-  const rawRefs = await gitRefs.getRawRefs({ lookupName });
+  const rawRefs = await gitRefs.getRawRefs({ lookupName }, id);
   const findValue = newValue || 'HEAD';
   const ref = rawRefs.find((rawRef) => rawRef.value === findValue);
   if (ref) {
diff --git a/lib/util/git/url.spec.ts b/lib/util/git/url.spec.ts
new file mode 100644
index 0000000000000000000000000000000000000000..bc8dc6e8556099cc355801c8a7955ff290f53ffe
--- /dev/null
+++ b/lib/util/git/url.spec.ts
@@ -0,0 +1,96 @@
+import { getName, hostRules } from '../../../test/util';
+import { getHttpUrl, getRemoteUrlWithToken } from './url';
+
+jest.mock('../host-rules');
+
+describe(getName(), () => {
+  describe('getHttpUrl()', () => {
+    it('returns https url for git url', () => {
+      expect(getHttpUrl('git://foo.bar/')).toBe('https://foo.bar/');
+    });
+
+    it('returns https url for https url', () => {
+      expect(getHttpUrl('https://foo.bar/')).toBe('https://foo.bar/');
+    });
+
+    it('returns http url for http url', () => {
+      expect(getHttpUrl('http://foo.bar/')).toBe('http://foo.bar/');
+    });
+  });
+
+  describe('getRemoteUrlWithToken()', () => {
+    it('returns original url if no host rule is found', () => {
+      expect(getRemoteUrlWithToken('https://foo.bar/')).toBe(
+        'https://foo.bar/'
+      );
+    });
+
+    it('returns http url with token', () => {
+      hostRules.find.mockReturnValueOnce({ token: 'token' });
+      expect(getRemoteUrlWithToken('http://foo.bar/')).toBe(
+        'http://token@foo.bar/'
+      );
+    });
+
+    it('returns https url with token', () => {
+      hostRules.find.mockReturnValueOnce({ token: 'token' });
+      expect(getRemoteUrlWithToken('https://foo.bar/')).toBe(
+        'https://token@foo.bar/'
+      );
+    });
+
+    it('returns https url with token for non-http protocols', () => {
+      hostRules.find.mockReturnValueOnce({ token: 'token' });
+      expect(getRemoteUrlWithToken('ssh://foo.bar/')).toBe(
+        'https://token@foo.bar/'
+      );
+    });
+
+    it('returns https url with encoded token', () => {
+      hostRules.find.mockReturnValueOnce({ token: 't#ken' });
+      expect(getRemoteUrlWithToken('https://foo.bar/')).toBe(
+        'https://t%23ken@foo.bar/'
+      );
+    });
+
+    it('returns http url with username and password', () => {
+      hostRules.find.mockReturnValueOnce({
+        username: 'user',
+        password: 'pass',
+      });
+      expect(getRemoteUrlWithToken('http://foo.bar/')).toBe(
+        'http://user:pass@foo.bar/'
+      );
+    });
+
+    it('returns https url with username and password', () => {
+      hostRules.find.mockReturnValueOnce({
+        username: 'user',
+        password: 'pass',
+      });
+      expect(getRemoteUrlWithToken('https://foo.bar/')).toBe(
+        'https://user:pass@foo.bar/'
+      );
+    });
+
+    it('returns https url with username and password for non-http protocols', () => {
+      hostRules.find.mockReturnValueOnce({
+        username: 'user',
+        password: 'pass',
+      });
+      expect(getRemoteUrlWithToken('ssh://foo.bar/')).toBe(
+        'https://user:pass@foo.bar/'
+      );
+    });
+
+    it('returns https url with encoded username and password', () => {
+      hostRules.find.mockReturnValueOnce({
+        username: 'u$er',
+        password: 'p@ss',
+      });
+      expect(getRemoteUrlWithToken('https://foo.bar/')).toBe(
+        'https://u%24er:p%40ss@foo.bar/'
+      );
+    });
+  });
+});
diff --git a/lib/util/git/url.ts b/lib/util/git/url.ts
index fb671a3441cb09209d6432fcadd94511bf9813ec..881de8722b3c6fc1aec98a485e6e58a96c83f02e 100644
--- a/lib/util/git/url.ts
+++ b/lib/util/git/url.ts
@@ -4,18 +4,31 @@ import * as hostRules from '../host-rules';
 
 export function getHttpUrl(url: string, token?: string): string {
   const parsedUrl = GitUrlParse(url);
+
   parsedUrl.token = token;
-  return parsedUrl.toString('https');
+
+  const protocol = /^https?$/.exec(parsedUrl.protocol)
+    ? parsedUrl.protocol
+    : 'https';
+  return parsedUrl.toString(protocol);
 }
 
-export function getRemoteUrlWithToken(url: string): string {
-  let remote = url;
+export function getRemoteUrlWithToken(url: string, hostType?: string): string {
+  const hostRule = hostRules.find({ url, hostType });
 
-  const hostRule = hostRules.find({ url });
   if (hostRule?.token) {
     logger.debug(`Found hostRules token for url ${url}`);
-    remote = getHttpUrl(url, hostRule.token);
+
+    return getHttpUrl(url, encodeURIComponent(hostRule.token));
+  }
+
+  if (hostRule?.username && hostRule?.password) {
+    logger.debug(`Found hostRules username and password for url ${url}`);
+    const encodedUsername = encodeURIComponent(hostRule.username);
+    const encodedPassword = encodeURIComponent(hostRule.password);
+
+    return getHttpUrl(url, `${encodedUsername}:${encodedPassword}`);
   }
 
-  return remote;
+  return url;
 }