diff --git a/lib/versioning/loose/generic.ts b/lib/versioning/loose/generic.ts
index f916c684ebc0c519efd5a4b708745986345c5e67..55646f2b704d5267efa7c07f3ef0461674f550fc 100644
--- a/lib/versioning/loose/generic.ts
+++ b/lib/versioning/loose/generic.ts
@@ -1,3 +1,4 @@
+import is from '@sindresorhus/is';
 import type { NewValueConfig, VersioningApi } from '../types';
 
 export interface GenericVersion {
@@ -133,7 +134,51 @@ export abstract class GenericVersioningApi<
       : null;
   }
 
-  protected abstract _compare(version: string, other: string): number;
+  protected _compare(version: string, other: string): number {
+    const left = this._parse(version);
+    const right = this._parse(other);
+
+    // istanbul ignore if
+    if (!(left && right)) {
+      return 1;
+    }
+
+    // support variable length compare
+    const length = Math.max(left.release.length, right.release.length);
+    for (let i = 0; i < length; i += 1) {
+      // 2.1 and 2.1.0 are equivalent
+      const part1 = left.release[i] ?? 0;
+      const part2 = right.release[i] ?? 0;
+      if (part1 !== part2) {
+        return part1 - part2;
+      }
+    }
+
+    if (
+      is.nonEmptyString(left.prerelease) &&
+      is.nonEmptyString(right.prerelease)
+    ) {
+      const pre = left.prerelease.localeCompare(right.prerelease);
+
+      if (pre !== 0) {
+        return pre;
+      }
+    } else if (is.nonEmptyString(left.prerelease)) {
+      return -1;
+    } else if (is.nonEmptyString(right.prerelease)) {
+      return 1;
+    }
+
+    return this._compareOther(left, right);
+  }
+
+  /*
+   * virtual
+   */
+  // eslint-disable-next-line class-methods-use-this
+  protected _compareOther(_left: T, _right: T): number {
+    return 0;
+  }
 
   protected abstract _parse(version: string): T | null;
 
diff --git a/lib/versioning/regex/index.spec.ts b/lib/versioning/regex/index.spec.ts
index 8e5c93a97c7dde0797509606ed0d4d93308c2728..09c57ac1d3360a6be5f43811f2944d378f641583 100644
--- a/lib/versioning/regex/index.spec.ts
+++ b/lib/versioning/regex/index.spec.ts
@@ -396,4 +396,33 @@ describe('regex', () => {
       expect(regex.matches('1.2.4a1-foo', '1.2.3a1-bar')).toBe(false);
     });
   });
+
+  describe('Supported 4th number as build', () => {
+    it('supports Bitnami docker versioning', () => {
+      const re = get(
+        'regex:^(?<major>\\d+)\\.(?<minor>\\d+)\\.(?<patch>\\d+)(:?-(?<compatibility>.*-r)(?<build>\\d+))?$'
+      );
+
+      expect(re.isValid('12.7.0-debian-10-r69')).toBe(true);
+      expect(re.isValid('12.7.0-debian-10-r100')).toBe(true);
+
+      expect(
+        re.isCompatible('12.7.0-debian-10-r69', '12.7.0-debian-10-r100')
+      ).toBe(true);
+
+      expect(
+        re.isGreaterThan('12.7.0-debian-10-r69', '12.7.0-debian-10-r100')
+      ).toBe(false);
+      expect(
+        re.isGreaterThan('12.7.0-debian-10-r169', '12.7.0-debian-10-r100')
+      ).toBe(true);
+
+      expect(re.matches('12.7.0-debian-9-r69', '12.7.0-debian-10-r69')).toBe(
+        true
+      );
+      expect(re.matches('12.7.0-debian-9-r69', '12.7.0-debian-10-r68')).toBe(
+        true
+      );
+    });
+  });
 });
diff --git a/lib/versioning/regex/index.ts b/lib/versioning/regex/index.ts
index 9257a41d0a09851338a7d3f68dc5dcdfa67fc3f9..43252b36ae101f758e5987e50f08091c47edbb8a 100644
--- a/lib/versioning/regex/index.ts
+++ b/lib/versioning/regex/index.ts
@@ -1,4 +1,5 @@
-import { compare, ltr, maxSatisfying, minSatisfying, satisfies } from 'semver';
+import is from '@sindresorhus/is';
+import { ltr, maxSatisfying, minSatisfying, satisfies } from 'semver';
 import { CONFIG_VALIDATION } from '../../constants/error-messages';
 import { regEx } from '../../util/regex';
 import { GenericVersion, GenericVersioningApi } from '../loose/generic';
@@ -10,8 +11,6 @@ export const urls = [];
 export const supportsRanges = false;
 
 export interface RegExpVersion extends GenericVersion {
-  /** prereleases are treated in the standard semver manner, if present */
-  prerelease: string;
   /**
    * compatibility, if present, are treated as a compatibility layer: we will
    * never try to update to a version with a different compatibility.
@@ -22,7 +21,7 @@ export interface RegExpVersion extends GenericVersion {
 // convenience method for passing a Version object into any semver.* method.
 function asSemver(version: RegExpVersion): string {
   let vstring = `${version.release[0]}.${version.release[1]}.${version.release[2]}`;
-  if (typeof version.prerelease !== 'undefined') {
+  if (is.nonEmptyString(version.prerelease)) {
     vstring += `-${version.prerelease}`;
   }
   return vstring;
@@ -38,6 +37,8 @@ export class RegExpVersioningApi extends GenericVersioningApi<RegExpVersion> {
   //   RegExp('^(?<major>\\d+)\\.(?<minor>\\d+)\\.(?<patch>\\d+)(-(?<compatibility>.*))?$')
   // * matches the versioning approach used by the Python images on DockerHub:
   //   RegExp('^(?<major>\\d+)\\.(?<minor>\\d+)\\.(?<patch>\\d+)(?<prerelease>[^.-]+)?(-(?<compatibility>.*))?$');
+  // * matches the versioning approach used by the Bitnami images on DockerHub:
+  //   RegExp('^(?<major>\\d+)\\.(?<minor>\\d+)\\.(?<patch>\\d+)(:?-(?<compatibility>.*-r)(?<build>\\d+))?$');
   private _config: RegExp = null;
 
   constructor(new_config: string) {
@@ -66,13 +67,6 @@ export class RegExpVersioningApi extends GenericVersioningApi<RegExpVersion> {
     this._config = regEx(new_config);
   }
 
-  protected _compare(version: string, other: string): number {
-    return compare(
-      asSemver(this._parse(version)),
-      asSemver(this._parse(other))
-    );
-  }
-
   // convenience method for passing a string into a Version given current config.
   protected _parse(version: string): RegExpVersion | null {
     const match = this._config.exec(version);
@@ -81,12 +75,18 @@ export class RegExpVersioningApi extends GenericVersioningApi<RegExpVersion> {
     }
 
     const groups = match.groups;
+    const release = [
+      typeof groups.major === 'undefined' ? 0 : Number(groups.major),
+      typeof groups.minor === 'undefined' ? 0 : Number(groups.minor),
+      typeof groups.patch === 'undefined' ? 0 : Number(groups.patch),
+    ];
+
+    if (groups.build) {
+      release.push(Number(groups.build));
+    }
+
     return {
-      release: [
-        typeof groups.major === 'undefined' ? 0 : Number(groups.major),
-        typeof groups.minor === 'undefined' ? 0 : Number(groups.minor),
-        typeof groups.patch === 'undefined' ? 0 : Number(groups.patch),
-      ],
+      release,
       prerelease: groups.prerelease,
       compatibility: groups.compatibility,
     };
@@ -98,10 +98,6 @@ export class RegExpVersioningApi extends GenericVersioningApi<RegExpVersion> {
     );
   }
 
-  isStable(version: string): boolean {
-    return typeof this._parse(version).prerelease === 'undefined';
-  }
-
   isLessThanRange(version: string, range: string): boolean {
     return ltr(asSemver(this._parse(version)), asSemver(this._parse(range)));
   }
diff --git a/lib/versioning/regex/readme.md b/lib/versioning/regex/readme.md
index 6ec07e07c42d286570304d4c6b2b4ce037e2f350..92122b42eab1f5f24b374f2ba68b348bf9e85104 100644
--- a/lib/versioning/regex/readme.md
+++ b/lib/versioning/regex/readme.md
@@ -34,3 +34,17 @@ Here is another example, this time for handling `python` Docker images, which us
   ]
 }
 ```
+
+Here is another example, this time for handling Bitnami Docker images, which use build indicators as well as version suffixes for compatibility:
+
+```json
+{
+  "packageRules": [
+    {
+      "matchDatasources": ["docker"],
+      "matchPackagePrefixes": ["bitnami/"],
+      "versioning": "regex:^(?<major>\\d+)\\.(?<minor>\\d+)\\.(?<patch>\\d+)(:?-(?<compatibility>.*-r)(?<build>\\d+))?$"
+    }
+  ]
+}
+```