diff --git a/docs/development/configuration.md b/docs/development/configuration.md
index d3bfca3b7f6edd4dafcdbedea4b5cbfe61a9aeb3..a98d05be8e6863bad5812f89047fd545bc220072 100644
--- a/docs/development/configuration.md
+++ b/docs/development/configuration.md
@@ -20,6 +20,10 @@ Options which have `"globalOnly": true` are reserved only for bot global configu
 You can override default configuration using a configuration file, with default name `config.js` in the working directory.
 If you need an alternate location or name, set it in the environment variable `RENOVATE_CONFIG_FILE`.
 
+**Note:** `RENOVATE_CONFIG_FILE` must be provided with an explicit file extension.
+For example `RENOVATE_CONFIG_FILE=myconfig.js` or `RENOVATE_CONFIG_FILE=myconfig.json` and not `RENOVATE_CONFIG_FILE=myconfig`.
+If none is provided, or the file type is invalid, Renovate will fail.
+
 Using a configuration file gives you very granular configuration options.
 For instance, you can override most settings at the global (file), repository, or package level.
 e.g. apply one set of labels for `backend/package.json` and a different set of labels for `frontend/package.json` in the same repository.
diff --git a/lib/config-validator.ts b/lib/config-validator.ts
index aac81196482a76ff9ccb0fe43662863814b054c0..383c06e148da36485554c3796e6443ca8a6abcb2 100644
--- a/lib/config-validator.ts
+++ b/lib/config-validator.ts
@@ -2,14 +2,16 @@
 // istanbul ignore file
 import { dequal } from 'dequal';
 import { readFile } from 'fs-extra';
-import JSON5 from 'json5';
 import { configFileNames } from './config/app-strings';
 import { massageConfig } from './config/massage';
 import { migrateConfig } from './config/migration';
 import type { RenovateConfig } from './config/types';
 import { validateConfig } from './config/validation';
 import { logger } from './logger';
-import { getConfig as getFileConfig } from './workers/global/config/parse/file';
+import {
+  getConfig as getFileConfig,
+  getParsedContent,
+} from './workers/global/config/parse/file';
 
 let returnVal = 0;
 
@@ -52,22 +54,17 @@ type PackageJson = {
     (name) => name !== 'package.json'
   )) {
     try {
-      const rawContent = await readFile(file, 'utf8');
-      logger.info(`Validating ${file}`);
+      const parsedContent = await getParsedContent(file);
       try {
-        let jsonContent: RenovateConfig;
-        if (file.endsWith('.json5')) {
-          jsonContent = JSON5.parse(rawContent);
-        } else {
-          jsonContent = JSON.parse(rawContent);
-        }
-        await validate(file, jsonContent);
+        logger.info(`Validating ${file}`);
+        await validate(file, parsedContent);
       } catch (err) {
         logger.info({ err }, `${file} is not valid Renovate config`);
         returnVal = 1;
       }
     } catch (err) {
       // file does not exist
+      continue;
     }
   }
   try {
diff --git a/lib/workers/global/config/parse/__fixtures__/config.json5 b/lib/workers/global/config/parse/__fixtures__/config.json5
new file mode 100644
index 0000000000000000000000000000000000000000..05d6407ca7b492cf94ccca5ffa863cddc6e455cb
--- /dev/null
+++ b/lib/workers/global/config/parse/__fixtures__/config.json5
@@ -0,0 +1,4 @@
+{
+    // comment
+    "token": "abcdefg",
+}
diff --git a/lib/workers/global/config/parse/__fixtures__/config.yaml b/lib/workers/global/config/parse/__fixtures__/config.yaml
new file mode 100644
index 0000000000000000000000000000000000000000..30bd5dbba13bb0be846f7481a6df139db75a8ac4
--- /dev/null
+++ b/lib/workers/global/config/parse/__fixtures__/config.yaml
@@ -0,0 +1,3 @@
+---
+# comment
+token: abcdefg
diff --git a/lib/workers/global/config/parse/file.spec.ts b/lib/workers/global/config/parse/file.spec.ts
index 6e629aa2448248cee7622a84b1d1c82af12bff93..cf497566a42a59308cf81bf249ddbcc1d71d5e07 100644
--- a/lib/workers/global/config/parse/file.spec.ts
+++ b/lib/workers/global/config/parse/file.spec.ts
@@ -1,6 +1,7 @@
 import fs from 'fs';
 import { DirectoryResult, dir } from 'tmp-promise';
 import upath from 'upath';
+import { logger } from '../../../../logger';
 import customConfig from './__fixtures__/file';
 import * as file from './file';
 
@@ -16,12 +17,17 @@ describe('workers/global/config/parse/file', () => {
   });
 
   describe('.getConfig()', () => {
-    it('parses custom config file', async () => {
-      const configFile = upath.resolve(__dirname, './__fixtures__/file.js');
+    it.each([
+      ['custom config file with extension', 'file.js'],
+      ['JSON5 config file', 'config.json5'],
+      ['YAML config file', 'config.yaml'],
+    ])('parses %s', async (fileType, filePath) => {
+      const configFile = upath.resolve(__dirname, './__fixtures__/', filePath);
       expect(
         await file.getConfig({ RENOVATE_CONFIG_FILE: configFile })
       ).toEqual(customConfig);
     });
+
     it('migrates', async () => {
       const configFile = upath.resolve(__dirname, './__fixtures__/file2.js');
       const res = await file.getConfig({ RENOVATE_CONFIG_FILE: configFile });
@@ -33,12 +39,10 @@ describe('workers/global/config/parse/file', () => {
       expect(await file.getConfig({})).toBeDefined();
     });
 
-    it('fatal error and exit if error in parsing config.js', async () => {
-      const mockProcessExit = jest
-        .spyOn(process, 'exit')
-        .mockImplementation(() => undefined as never);
-      const configFile = upath.resolve(tmp.path, './file3.js');
-      const fileContent = `module.exports = {
+    it.each([
+      [
+        'config.js',
+        `module.exports = {
         "platform": "github",
         "token":"abcdef",
         "logFileLevel": "warn",
@@ -48,13 +52,23 @@ describe('workers/global/config/parse/file', () => {
           "extends": ["config:base"],
         },
         "repositories": [ "test/test" ],
-      };`;
-      fs.writeFileSync(configFile, fileContent, { encoding: 'utf8' });
-      await file.getConfig({ RENOVATE_CONFIG_FILE: configFile });
-      expect(mockProcessExit).toHaveBeenCalledWith(1);
-
-      fs.unlinkSync(configFile);
-    });
+      };`,
+      ],
+      ['config.json5', `"invalid":`],
+      ['config.yaml', `invalid: -`],
+    ])(
+      'fatal error and exit if error in parsing %s',
+      async (fileName, fileContent) => {
+        const mockProcessExit = jest
+          .spyOn(process, 'exit')
+          .mockImplementationOnce(() => undefined as never);
+        const configFile = upath.resolve(tmp.path, fileName);
+        fs.writeFileSync(configFile, fileContent, { encoding: 'utf8' });
+        await file.getConfig({ RENOVATE_CONFIG_FILE: configFile });
+        expect(mockProcessExit).toHaveBeenCalledWith(1);
+        fs.unlinkSync(configFile);
+      }
+    );
 
     it('fatal error and exit if custom config file does not exist', async () => {
       const mockProcessExit = jest
@@ -66,5 +80,20 @@ describe('workers/global/config/parse/file', () => {
 
       expect(mockProcessExit).toHaveBeenCalledWith(1);
     });
+
+    it.each([
+      ['invalid config file type', './file.txt'],
+      ['missing config file type', './file'],
+    ])('fatal error and exit if %s', async (fileType, filePath) => {
+      const mockProcessExit = jest
+        .spyOn(process, 'exit')
+        .mockImplementationOnce(() => undefined as never);
+      const configFile = upath.resolve(tmp.path, filePath);
+      fs.writeFileSync(configFile, `{"token": "abc"}`, { encoding: 'utf8' });
+      await file.getConfig({ RENOVATE_CONFIG_FILE: configFile });
+      expect(mockProcessExit).toHaveBeenCalledWith(1);
+      expect(logger.fatal).toHaveBeenCalledWith('Unsupported file type');
+      fs.unlinkSync(configFile);
+    });
   });
 });
diff --git a/lib/workers/global/config/parse/file.ts b/lib/workers/global/config/parse/file.ts
index efdefb62b313c0aaca61197f2b1b17735f026e82..103d24d8692f4ec7f0095ebbeff5eabd4aa55565 100644
--- a/lib/workers/global/config/parse/file.ts
+++ b/lib/workers/global/config/parse/file.ts
@@ -1,23 +1,47 @@
+import { load } from 'js-yaml';
+import JSON5 from 'json5';
 import upath from 'upath';
 import { migrateConfig } from '../../../../config/migration';
-import type { AllConfig } from '../../../../config/types';
+import type { AllConfig, RenovateConfig } from '../../../../config/types';
 import { logger } from '../../../../logger';
+import { readFile } from '../../../../util/fs';
+
+export async function getParsedContent(file: string): Promise<RenovateConfig> {
+  switch (upath.extname(file)) {
+    case '.yaml':
+    case '.yml':
+      return load(await readFile(file, 'utf8'), {
+        json: true,
+      }) as RenovateConfig;
+    case '.json5':
+    case '.json':
+      return JSON5.parse(await readFile(file, 'utf8'));
+    case '.js': {
+      const tmpConfig = await import(file);
+      return tmpConfig.default ? tmpConfig.default : tmpConfig;
+    }
+    default:
+      throw new Error('Unsupported file type');
+  }
+}
 
 export async function getConfig(env: NodeJS.ProcessEnv): Promise<AllConfig> {
-  let configFile = env.RENOVATE_CONFIG_FILE || 'config';
+  let configFile = env.RENOVATE_CONFIG_FILE || 'config.js';
   if (!upath.isAbsolute(configFile)) {
     configFile = `${process.cwd()}/${configFile}`;
-    logger.debug('Checking for config file in ' + configFile);
   }
+  logger.debug('Checking for config file in ' + configFile);
   let config: AllConfig = {};
   try {
-    const tmpConfig = await import(configFile);
-    config = tmpConfig.default ? tmpConfig.default : tmpConfig;
+    config = await getParsedContent(configFile);
   } catch (err) {
     // istanbul ignore if
     if (err instanceof SyntaxError || err instanceof TypeError) {
       logger.fatal(`Could not parse config file \n ${err.stack}`);
       process.exit(1);
+    } else if (err.message === 'Unsupported file type') {
+      logger.fatal(err.message);
+      process.exit(1);
     } else if (env.RENOVATE_CONFIG_FILE) {
       logger.fatal('No custom config file found on disk');
       process.exit(1);