diff --git a/lib/manager/batect/__fixtures__/batect.yml b/lib/manager/batect/__fixtures__/batect.yml
new file mode 100644
index 0000000000000000000000000000000000000000..d8fb1ac8178c1d36571cc12d2d088ed8f8f469ea
--- /dev/null
+++ b/lib/manager/batect/__fixtures__/batect.yml
@@ -0,0 +1,15 @@
+containers:
+  container-1:
+    image: alpine:1.2.3
+
+  container-2:
+    image: alpine:1.2.3
+
+  container-3:
+    image: ubuntu:20.04
+
+  container-4:
+    build_directory: some_build_directory
+
+  container-5:
+    image: postgres:9.6.20@sha256:166179811e4c75f8a092367afed6091208c8ecf60b111c7e49f29af45ca05e08
diff --git a/lib/manager/batect/extract.spec.ts b/lib/manager/batect/extract.spec.ts
new file mode 100644
index 0000000000000000000000000000000000000000..abd36560a1b7f72d7e704903aed07f4de50dab2a
--- /dev/null
+++ b/lib/manager/batect/extract.spec.ts
@@ -0,0 +1,41 @@
+import { readFileSync } from 'fs';
+import { getDep } from '../dockerfile/extract';
+import { extractPackageFile } from './extract';
+
+const sampleFile = readFileSync(
+  'lib/manager/batect/__fixtures__/batect.yml',
+  'utf8'
+);
+
+describe('lib/manager/batect/extract', () => {
+  describe('extractPackageFile()', () => {
+    it('returns null for empty configuration file', () => {
+      expect(extractPackageFile('')).toBeNull();
+    });
+
+    it('returns null for non-object configuration file', () => {
+      expect(extractPackageFile('nothing here')).toBeNull();
+    });
+
+    it('returns null for malformed configuration file', () => {
+      expect(extractPackageFile('nothing here\n:::::::')).toBeNull();
+    });
+
+    it('returns null for configuration file without containers', () => {
+      expect(extractPackageFile('something_else: some_value')).toBeNull();
+    });
+
+    it('extracts all available images from a valid Batect configuration file', () => {
+      const res = extractPackageFile(sampleFile);
+
+      expect(res.deps).toEqual([
+        getDep('alpine:1.2.3'),
+        getDep('alpine:1.2.3'),
+        getDep('ubuntu:20.04'),
+        getDep(
+          'postgres:9.6.20@sha256:166179811e4c75f8a092367afed6091208c8ecf60b111c7e49f29af45ca05e08'
+        ),
+      ]);
+    });
+  });
+});
diff --git a/lib/manager/batect/extract.ts b/lib/manager/batect/extract.ts
new file mode 100644
index 0000000000000000000000000000000000000000..3846a9008bdb011420f70ebb0e732c11c1cfec06
--- /dev/null
+++ b/lib/manager/batect/extract.ts
@@ -0,0 +1,59 @@
+import { safeLoad } from 'js-yaml';
+
+import { logger } from '../../logger';
+import { PackageFile } from '../common';
+import { getDep } from '../dockerfile/extract';
+import { BatectConfig } from './types';
+
+function loadConfig(content: string): BatectConfig {
+  const config = safeLoad(content);
+
+  if (typeof config !== 'object') {
+    throw new Error(
+      `Configuration file does not contain a YAML object (it is ${typeof config}).`
+    );
+  }
+
+  return config as BatectConfig;
+}
+
+function extractImages(config: BatectConfig): string[] {
+  if (config.containers === undefined) {
+    return [];
+  }
+
+  return Object.values(config.containers)
+    .filter((container) => container.image !== undefined)
+    .map((container) => container.image);
+}
+
+export function extractPackageFile(
+  content: string,
+  fileName?: string
+): PackageFile | null {
+  logger.debug('batect.extractPackageFile()');
+
+  try {
+    const config = loadConfig(content);
+    const images = extractImages(config);
+    const deps = images.map((image) => getDep(image));
+
+    if (deps.length === 0) {
+      return null;
+    }
+
+    logger.trace(
+      { deps, fileName },
+      'Loaded images from Batect configuration file'
+    );
+
+    return { deps };
+  } catch (err) {
+    logger.warn(
+      { err, fileName },
+      'Extracting dependencies from Batect configuration file failed'
+    );
+
+    return null;
+  }
+}
diff --git a/lib/manager/batect/index.ts b/lib/manager/batect/index.ts
new file mode 100644
index 0000000000000000000000000000000000000000..78ace05e2846c453192b3fdc8824a2216b0a7ff7
--- /dev/null
+++ b/lib/manager/batect/index.ts
@@ -0,0 +1,7 @@
+import { extractPackageFile } from './extract';
+
+export { extractPackageFile };
+
+export const defaultConfig = {
+  fileMatch: ['(^|/)batect(-bundle)?\\.yml$'],
+};
diff --git a/lib/manager/batect/readme.md b/lib/manager/batect/readme.md
new file mode 100644
index 0000000000000000000000000000000000000000..980e2bd926bfd2685f2e85ffaa97286857051129
--- /dev/null
+++ b/lib/manager/batect/readme.md
@@ -0,0 +1,23 @@
+Extracts all Docker images from Batect configuration files.
+
+By default, the manager searches for files called `batect.yml` or `batect-bundle.yml`.
+
+If you keep your Batect configuration in other files, you'll need to tell Renovate where to find them.
+This includes files included into your main configuration file with `include`.
+
+You do this by creating a `"batect"` object in your `renovate.json` file.
+This object should contain a `fileMatch` array with regular expressions that match the configuration file names.
+
+For example:
+
+```json
+{
+  "batect": {
+    "fileMatch": [
+      "(^|/)batect(-bundle)?\\.yml$",
+      "(^|/)my-other-batect-file\\.yml$",
+      "^a-directory/[^/]*\\.yml$"
+    ]
+  }
+}
+```
diff --git a/lib/manager/batect/types.ts b/lib/manager/batect/types.ts
new file mode 100644
index 0000000000000000000000000000000000000000..b62adbc14fa56ebade41b3a6daa7b636d0677f7a
--- /dev/null
+++ b/lib/manager/batect/types.ts
@@ -0,0 +1,7 @@
+export interface BatectConfig {
+  containers?: Record<string, BatectContainer>;
+}
+
+export interface BatectContainer {
+  image?: string;
+}