diff --git a/lib/datasource/api.ts b/lib/datasource/api.ts
index 4f9fee1d1efbc6e8006867eb1e06c9e012af12d6..36caaae79a91e03dfa28c8ff2e240f2a1eca8eb7 100644
--- a/lib/datasource/api.ts
+++ b/lib/datasource/api.ts
@@ -19,7 +19,7 @@ import * as jenkinsPlugins from './jenkins-plugins';
 import * as maven from './maven';
 import * as npm from './npm';
 import * as nuget from './nuget';
-import * as orb from './orb';
+import { OrbDatasource } from './orb';
 import * as packagist from './packagist';
 import * as pod from './pod';
 import * as pypi from './pypi';
@@ -56,7 +56,7 @@ api.set('jenkins-plugins', jenkinsPlugins);
 api.set('maven', maven);
 api.set('npm', npm);
 api.set('nuget', nuget);
-api.set('orb', orb);
+api.set('orb', new OrbDatasource());
 api.set('packagist', packagist);
 api.set('pod', pod);
 api.set('pypi', pypi);
diff --git a/lib/datasource/orb/index.spec.ts b/lib/datasource/orb/index.spec.ts
index b3d3940b3e98faf216836c1a3200781b78a8792a..4d8feff26609699ea87c44edc60d9e3dab9b8519 100644
--- a/lib/datasource/orb/index.spec.ts
+++ b/lib/datasource/orb/index.spec.ts
@@ -1,7 +1,7 @@
 import { getPkgReleases } from '..';
 import * as httpMock from '../../../test/http-mock';
 import { getName } from '../../../test/util';
-import { id as datasource } from '.';
+import { OrbDatasource } from '.';
 
 const orbData = {
   data: {
@@ -26,6 +26,8 @@ const orbData = {
 
 const baseUrl = 'https://circleci.com';
 
+const datasource = OrbDatasource.id;
+
 describe(getName(), () => {
   describe('getReleases', () => {
     beforeEach(() => {
diff --git a/lib/datasource/orb/index.ts b/lib/datasource/orb/index.ts
index 46bd7738570f0e258c7b8be293bc9bda0c33a302..83098ba392322b101242058c779fa9a8556aecef 100644
--- a/lib/datasource/orb/index.ts
+++ b/lib/datasource/orb/index.ts
@@ -1,64 +1,57 @@
 import { logger } from '../../logger';
-import * as packageCache from '../../util/cache/package';
-import { Http } from '../../util/http';
+import { cache } from '../../util/cache/package/decorator';
+import { Datasource } from '../datasource';
 import type { GetReleasesConfig, ReleaseResult } from '../types';
 import type { OrbRelease } from './types';
 
-export const id = 'orb';
-export const defaultRegistryUrls = ['https://circleci.com/'];
-export const customRegistrySupport = false;
+export class OrbDatasource extends Datasource {
+  static readonly id = 'orb';
 
-const http = new Http(id);
-
-/**
- * orb.getReleases
- *
- * This function will fetch an orb from CircleCI and return all semver versions.
- */
-export async function getReleases({
-  lookupName,
-  registryUrl,
-}: GetReleasesConfig): Promise<ReleaseResult | null> {
-  logger.debug({ lookupName }, 'orb.getReleases()');
-  const cacheNamespace = 'orb';
-  const cacheKey = lookupName;
-  const cachedResult = await packageCache.get<ReleaseResult>(
-    cacheNamespace,
-    cacheKey
-  );
-  // istanbul ignore if
-  if (cachedResult) {
-    return cachedResult;
-  }
-  const url = `${registryUrl}graphql-unstable`;
-  const body = {
-    query: `{orb(name:"${lookupName}"){name, homeUrl, versions {version, createdAt}}}`,
-    variables: {},
-  };
-  const res: OrbRelease = (
-    await http.postJson<{ data: { orb: OrbRelease } }>(url, {
-      body,
-    })
-  ).body.data.orb;
-  if (!res) {
-    logger.debug({ lookupName }, 'Failed to look up orb');
-    return null;
+  constructor() {
+    super(OrbDatasource.id);
   }
-  // Simplify response before caching and returning
-  const dep: ReleaseResult = {
-    releases: null,
-  };
-  if (res.homeUrl?.length) {
-    dep.homepage = res.homeUrl;
+
+  customRegistrySupport = false;
+
+  defaultRegistryUrls = ['https://circleci.com/'];
+
+  @cache({
+    namespace: `datasource-${OrbDatasource.id}`,
+    key: ({ lookupName }: GetReleasesConfig) => lookupName,
+  })
+  async getReleases({
+    lookupName,
+    registryUrl,
+  }: GetReleasesConfig): Promise<ReleaseResult | null> {
+    const url = `${registryUrl}graphql-unstable`;
+    const body = {
+      query: `{orb(name:"${lookupName}"){name, homeUrl, versions {version, createdAt}}}`,
+      variables: {},
+    };
+    const res: OrbRelease = (
+      await this.http.postJson<{ data: { orb: OrbRelease } }>(url, {
+        body,
+      })
+    ).body.data.orb;
+    if (!res) {
+      logger.debug({ lookupName }, 'Failed to look up orb');
+      return null;
+    }
+    // Simplify response before caching and returning
+    const dep: ReleaseResult = {
+      releases: null,
+    };
+    if (res.homeUrl?.length) {
+      dep.homepage = res.homeUrl;
+    }
+    dep.homepage =
+      dep.homepage || `https://circleci.com/developer/orbs/orb/${lookupName}`;
+    dep.releases = res.versions.map(({ version, createdAt }) => ({
+      version,
+      releaseTimestamp: createdAt || null,
+    }));
+
+    logger.trace({ dep }, 'dep');
+    return dep;
   }
-  dep.homepage =
-    dep.homepage || `https://circleci.com/developer/orbs/orb/${lookupName}`;
-  dep.releases = res.versions.map(({ version, createdAt }) => ({
-    version,
-    releaseTimestamp: createdAt || null,
-  }));
-  logger.trace({ dep }, 'dep');
-  const cacheMinutes = 15;
-  await packageCache.set(cacheNamespace, cacheKey, dep, cacheMinutes);
-  return dep;
 }
diff --git a/lib/manager/circleci/extract.ts b/lib/manager/circleci/extract.ts
index f71a48a3b7a36ec546edc5c6ed4d239268a4a809..0ffe5ce1ea93b12db3a046b576870873b2cd240e 100644
--- a/lib/manager/circleci/extract.ts
+++ b/lib/manager/circleci/extract.ts
@@ -1,4 +1,4 @@
-import * as datasourceOrb from '../../datasource/orb';
+import { OrbDatasource } from '../../datasource/orb';
 import { logger } from '../../logger';
 import * as npmVersioning from '../../versioning/npm';
 import { getDep } from '../dockerfile/extract';
@@ -36,7 +36,7 @@ export function extractPackageFile(content: string): PackageFile | null {
               depType: 'orb',
               depName,
               currentValue,
-              datasource: datasourceOrb.id,
+              datasource: OrbDatasource.id,
               lookupName: orbName,
               commitMessageTopic: '{{{depName}}} orb',
               versioning: npmVersioning.id,
diff --git a/lib/util/package-rules.spec.ts b/lib/util/package-rules.spec.ts
index ded6c2fe17e3c25db491f7a582cfa1ab88e4f80d..5bd2d52ae0656bfc28a89f6a27f98306546cccc3 100644
--- a/lib/util/package-rules.spec.ts
+++ b/lib/util/package-rules.spec.ts
@@ -7,7 +7,7 @@ import {
 } from '../constants/languages';
 
 import * as datasourceDocker from '../datasource/docker';
-import * as datasourceOrb from '../datasource/orb';
+import { OrbDatasource } from '../datasource/orb';
 import { applyPackageRules } from './package-rules';
 
 type TestConfig = PackageRuleInputConfig & {
@@ -318,14 +318,14 @@ describe('applyPackageRules()', () => {
     const config: TestConfig = {
       packageRules: [
         {
-          matchDatasources: [datasourceOrb.id, datasourceDocker.id],
+          matchDatasources: [OrbDatasource.id, datasourceDocker.id],
           x: 1,
         },
       ],
     };
     const dep = {
       depType: 'dependencies',
-      datasource: datasourceOrb.id,
+      datasource: OrbDatasource.id,
       baseBranch: 'master',
     };
     const res = applyPackageRules({ ...config, ...dep });
@@ -342,7 +342,7 @@ describe('applyPackageRules()', () => {
     };
     const dep = {
       depType: 'dependencies',
-      datasource: datasourceOrb.id,
+      datasource: OrbDatasource.id,
       baseBranch: 'master',
     };
     const res = applyPackageRules({ ...config, ...dep });
@@ -352,7 +352,7 @@ describe('applyPackageRules()', () => {
     const config: TestConfig = {
       packageRules: [
         {
-          matchDatasources: [datasourceOrb.id],
+          matchDatasources: [OrbDatasource.id],
           x: 1,
         },
       ],