diff --git a/lib/modules/manager/cocoapods/__snapshots__/artifacts.spec.ts.snap b/lib/modules/manager/cocoapods/__snapshots__/artifacts.spec.ts.snap
index 87e52fcfdd8c75c32a8dc4744aa68a655a00481f..2c41431eddb8bffea4c67325d6c4791a4c142994 100644
--- a/lib/modules/manager/cocoapods/__snapshots__/artifacts.spec.ts.snap
+++ b/lib/modules/manager/cocoapods/__snapshots__/artifacts.spec.ts.snap
@@ -3,19 +3,13 @@
 exports[`modules/manager/cocoapods/artifacts dynamically selects Docker image tag 1`] = `
 Array [
   Object {
-    "cmd": "docker pull renovate/cocoapods:1.2.4",
+    "cmd": "docker pull renovate/ruby:2.7.4",
     "options": Object {
       "encoding": "utf-8",
     },
   },
   Object {
-    "cmd": "docker ps --filter name=renovate_cocoapods -aq",
-    "options": Object {
-      "encoding": "utf-8",
-    },
-  },
-  Object {
-    "cmd": "docker run --rm --name=renovate_cocoapods --label=renovate_child -v \\"/tmp/github/some/repo\\":\\"/tmp/github/some/repo\\" -v \\"/tmp/cache\\":\\"/tmp/cache\\" -w \\"/tmp/github/some/repo\\" renovate/cocoapods:1.2.4 bash -l -c \\"pod install\\"",
+    "cmd": "docker run --rm --name=renovate_ruby --label=renovate_child -v \\"/tmp/github/some/repo\\":\\"/tmp/github/some/repo\\" -v \\"/tmp/cache\\":\\"/tmp/cache\\" -w \\"/tmp/github/some/repo\\" renovate/ruby:2.7.4 bash -l -c \\"install-tool cocoapods 1.2.4 && pod install\\"",
     "options": Object {
       "cwd": "/tmp/github/some/repo",
       "encoding": "utf-8",
@@ -38,19 +32,13 @@ Array [
 exports[`modules/manager/cocoapods/artifacts falls back to the \`latest\` Docker image tag 1`] = `
 Array [
   Object {
-    "cmd": "docker pull renovate/cocoapods:latest",
+    "cmd": "docker pull renovate/ruby:latest",
     "options": Object {
       "encoding": "utf-8",
     },
   },
   Object {
-    "cmd": "docker ps --filter name=renovate_cocoapods -aq",
-    "options": Object {
-      "encoding": "utf-8",
-    },
-  },
-  Object {
-    "cmd": "docker run --rm --name=renovate_cocoapods --label=renovate_child -v \\"/tmp/github/some/repo\\":\\"/tmp/github/some/repo\\" -v \\"/tmp/cache\\":\\"/tmp/cache\\" -w \\"/tmp/github/some/repo\\" renovate/cocoapods:latest bash -l -c \\"pod install\\"",
+    "cmd": "docker run --rm --name=renovate_ruby --label=renovate_child -v \\"/tmp/github/some/repo\\":\\"/tmp/github/some/repo\\" -v \\"/tmp/cache\\":\\"/tmp/cache\\" -w \\"/tmp/github/some/repo\\" renovate/ruby:latest bash -l -c \\"install-tool cocoapods 1.2.4 && pod install\\"",
     "options": Object {
       "cwd": "/tmp/github/some/repo",
       "encoding": "utf-8",
@@ -139,19 +127,13 @@ Array [
 exports[`modules/manager/cocoapods/artifacts returns updated Podfile 2`] = `
 Array [
   Object {
-    "cmd": "docker pull renovate/cocoapods",
-    "options": Object {
-      "encoding": "utf-8",
-    },
-  },
-  Object {
-    "cmd": "docker ps --filter name=renovate_cocoapods -aq",
+    "cmd": "docker pull renovate/ruby:2.7.4",
     "options": Object {
       "encoding": "utf-8",
     },
   },
   Object {
-    "cmd": "docker run --rm --name=renovate_cocoapods --label=renovate_child -v \\"/tmp/github/some/repo\\":\\"/tmp/github/some/repo\\" -v \\"/tmp/cache\\":\\"/tmp/cache\\" -w \\"/tmp/github/some/repo\\" renovate/cocoapods bash -l -c \\"gem install cocoapods-acknowledgements && pod install\\"",
+    "cmd": "docker run --rm --name=renovate_ruby --label=renovate_child -v \\"/tmp/github/some/repo\\":\\"/tmp/github/some/repo\\" -v \\"/tmp/cache\\":\\"/tmp/cache\\" -w \\"/tmp/github/some/repo\\" renovate/ruby:2.7.4 bash -l -c \\"install-tool cocoapods 3.1.0 && gem install cocoapods-acknowledgements && pod install\\"",
     "options": Object {
       "cwd": "/tmp/github/some/repo",
       "encoding": "utf-8",
@@ -206,13 +188,13 @@ Array [
 exports[`modules/manager/cocoapods/artifacts returns updated Podfile and Pods files 2`] = `
 Array [
   Object {
-    "cmd": "docker ps --filter name=renovate_cocoapods -aq",
+    "cmd": "docker pull renovate/ruby:2.7.4",
     "options": Object {
       "encoding": "utf-8",
     },
   },
   Object {
-    "cmd": "docker run --rm --name=renovate_cocoapods --label=renovate_child -v \\"/tmp/github/some/repo\\":\\"/tmp/github/some/repo\\" -v \\"/tmp/cache\\":\\"/tmp/cache\\" -w \\"/tmp/github/some/repo\\" renovate/cocoapods bash -l -c \\"pod install\\"",
+    "cmd": "docker run --rm --name=renovate_ruby --label=renovate_child -v \\"/tmp/github/some/repo\\":\\"/tmp/github/some/repo\\" -v \\"/tmp/cache\\":\\"/tmp/cache\\" -w \\"/tmp/github/some/repo\\" renovate/ruby:2.7.4 bash -l -c \\"install-tool cocoapods 3.1.0 && pod install\\"",
     "options": Object {
       "cwd": "/tmp/github/some/repo",
       "encoding": "utf-8",
diff --git a/lib/modules/manager/cocoapods/artifacts.spec.ts b/lib/modules/manager/cocoapods/artifacts.spec.ts
index 04cffde3c73bb815eb8ebc251670ee272daec2a8..66c3a640e23940370ebc5075e1e34d37a45ff814 100644
--- a/lib/modules/manager/cocoapods/artifacts.spec.ts
+++ b/lib/modules/manager/cocoapods/artifacts.spec.ts
@@ -3,6 +3,7 @@ import { envMock, exec, mockExecAll } from '../../../../test/exec-util';
 import { env, fs, git, mocked } from '../../../../test/util';
 import { GlobalConfig } from '../../../config/global';
 import type { RepoGlobalConfig } from '../../../config/types';
+import * as docker from '../../../util/exec/docker';
 import type { StatusResult } from '../../../util/git/types';
 import * as _datasource from '../../datasource';
 import type { UpdateArtifactsConfig } from '../types';
@@ -30,17 +31,17 @@ describe('modules/manager/cocoapods/artifacts', () => {
   beforeEach(() => {
     jest.resetAllMocks();
     env.getChildProcessEnv.mockReturnValue(envMock.basic);
+    jest.spyOn(docker, 'removeDockerContainer').mockResolvedValue();
+    // can't be mocked
+    docker.resetPrefetchedImages();
 
     GlobalConfig.set(adminConfig);
 
     datasource.getPkgReleases.mockResolvedValue({
       releases: [
-        { version: '1.2.0' },
-        { version: '1.2.1' },
-        { version: '1.2.2' },
-        { version: '1.2.3' },
-        { version: '1.2.4' },
-        { version: '1.2.5' },
+        { version: '2.7.4' },
+        { version: '3.0.0' },
+        { version: '3.1.0' },
       ],
     });
   });
@@ -110,7 +111,7 @@ describe('modules/manager/cocoapods/artifacts', () => {
     fs.findLocalSiblingOrParent.mockResolvedValueOnce('Podfile.lock');
     fs.readLocalFile.mockResolvedValueOnce('Current Podfile');
     git.getRepoStatus.mockResolvedValueOnce({
-      modified: [],
+      modified: [] as string[],
     } as StatusResult);
     fs.findLocalSiblingOrParent.mockResolvedValueOnce('Podfile.lock');
     fs.readLocalFile.mockResolvedValueOnce('Current Podfile');
@@ -241,9 +242,20 @@ describe('modules/manager/cocoapods/artifacts', () => {
       config,
     });
     expect(execSnapshots).toMatchSnapshot([
-      { cmd: 'docker pull renovate/cocoapods:1.2.4' },
-      {},
-      {},
+      { cmd: 'docker pull renovate/ruby:2.7.4' },
+      {
+        cmd:
+          'docker run --rm --name=renovate_ruby --label=renovate_child ' +
+          '-v "/tmp/github/some/repo":"/tmp/github/some/repo" ' +
+          '-v "/tmp/cache":"/tmp/cache" ' +
+          '-w "/tmp/github/some/repo" ' +
+          'renovate/ruby:2.7.4' +
+          ' bash -l -c "' +
+          'install-tool cocoapods 1.2.4' +
+          ' && ' +
+          'pod install' +
+          '"',
+      },
     ]);
   });
 
@@ -272,9 +284,20 @@ describe('modules/manager/cocoapods/artifacts', () => {
       config,
     });
     expect(execSnapshots).toMatchSnapshot([
-      { cmd: 'docker pull renovate/cocoapods:latest' },
-      {},
-      {},
+      { cmd: 'docker pull renovate/ruby:latest' },
+      {
+        cmd:
+          'docker run --rm --name=renovate_ruby --label=renovate_child ' +
+          '-v "/tmp/github/some/repo":"/tmp/github/some/repo" ' +
+          '-v "/tmp/cache":"/tmp/cache" ' +
+          '-w "/tmp/github/some/repo" ' +
+          'renovate/ruby:latest' +
+          ' bash -l -c "' +
+          'install-tool cocoapods 1.2.4' +
+          ' && ' +
+          'pod install' +
+          '"',
+      },
     ]);
   });
 });
diff --git a/lib/modules/manager/cocoapods/artifacts.ts b/lib/modules/manager/cocoapods/artifacts.ts
index e36240262f9da62d1a6f74017822b25bd210cb29..21fb629f128ba6722a4d7c7d6cc0a1d7c74ecb7a 100644
--- a/lib/modules/manager/cocoapods/artifacts.ts
+++ b/lib/modules/manager/cocoapods/artifacts.ts
@@ -67,7 +67,7 @@ export async function updateArtifacts({
   const match = regEx(/^COCOAPODS: (?<cocoapodsVersion>.*)$/m).exec(
     existingLockFileContent
   );
-  const tagConstraint = match?.groups?.cocoapodsVersion ?? null;
+  const cocoapods = match?.groups?.cocoapodsVersion ?? null;
 
   const cmd = [...getPluginCommands(newPackageFileContent), 'pod install'];
   const execOptions: ExecOptions = {
@@ -76,10 +76,16 @@ export async function updateArtifacts({
       CP_HOME_DIR: await ensureCacheDir('cocoapods'),
     },
     docker: {
-      image: 'cocoapods',
+      image: 'ruby',
       tagScheme: 'ruby',
-      tagConstraint,
+      tagConstraint: '< 3.0', // currently using v2 on docker image
     },
+    toolConstraints: [
+      {
+        toolName: 'cocoapods',
+        constraint: cocoapods,
+      },
+    ],
   };
 
   try {
diff --git a/lib/modules/manager/cocoapods/extract.spec.ts b/lib/modules/manager/cocoapods/extract.spec.ts
index c420a14d3ff0ea08bcdedaa6537f04b7b3dd44e4..9388fa2131d03cbfc7320d0f629f658e7517357e 100644
--- a/lib/modules/manager/cocoapods/extract.spec.ts
+++ b/lib/modules/manager/cocoapods/extract.spec.ts
@@ -12,8 +12,8 @@ describe('modules/manager/cocoapods/extract', () => {
   describe('extractPackageFile()', () => {
     it('extracts from simple file', async () => {
       GlobalConfig.set(adminConfig);
-      const { deps } = await extractPackageFile(simplePodfile, 'Podfile');
-      expect(deps).toMatchSnapshot([
+      const res = await extractPackageFile(simplePodfile, 'Podfile');
+      expect(res?.deps).toMatchSnapshot([
         { depName: 'a' },
         { depName: 'a/sub' },
         { depName: 'b', currentValue: '1.2.3' },
@@ -41,8 +41,8 @@ describe('modules/manager/cocoapods/extract', () => {
 
     it('extracts from complex file', async () => {
       GlobalConfig.set(adminConfig);
-      const { deps } = await extractPackageFile(complexPodfile, 'Podfile');
-      expect(deps).toMatchSnapshot([
+      const res = await extractPackageFile(complexPodfile, 'Podfile');
+      expect(res?.deps).toMatchSnapshot([
         { depName: 'IQKeyboardManager', currentValue: '~> 6.5.0' },
         { depName: 'CYLTabBarController', currentValue: '~> 1.28.3' },
         { depName: 'PureLayout', currentValue: '~> 3.1.4' },
diff --git a/lib/util/exec/buildpack.ts b/lib/util/exec/buildpack.ts
index 8c36269f8f164e56a5ef2060c3ad8f4f2ac5e5ef..2d79ff792cc09d373df22693e106c06cb5f9dbeb 100644
--- a/lib/util/exec/buildpack.ts
+++ b/lib/util/exec/buildpack.ts
@@ -6,6 +6,7 @@ import * as allVersioning from '../../modules/versioning';
 import { id as composerVersioningId } from '../../modules/versioning/composer';
 import { id as npmVersioningId } from '../../modules/versioning/npm';
 import { id as pep440VersioningId } from '../../modules/versioning/pep440';
+import { id as rubyVersioningId } from '../../modules/versioning/ruby';
 import { id as semverVersioningId } from '../../modules/versioning/semver';
 import type { Opt, ToolConfig, ToolConstraint } from './types';
 
@@ -15,6 +16,11 @@ const allToolConfig: Record<string, ToolConfig> = {
     depName: 'bundler',
     versioning: 'ruby',
   },
+  cocoapods: {
+    datasource: 'rubygems',
+    depName: 'cocoapods',
+    versioning: rubyVersioningId,
+  },
   composer: {
     datasource: 'github-releases',
     depName: 'composer/composer',