diff --git a/docs/usage/configuration-options.md b/docs/usage/configuration-options.md
index a8ea74ea3a5af0bcce5f6639242209463e3c0da7..9c2d3b05895e5e9fa4447a9f03d208c7143d7ee7 100644
--- a/docs/usage/configuration-options.md
+++ b/docs/usage/configuration-options.md
@@ -1286,6 +1286,18 @@ Use the syntax `!/ /` like the following:
 }
 ```
 
+### matchFiles
+
+Renovate will compare `matchFiles` for an exact match against the dependency's package file.
+
+For example the following would match `package.json` but not `package/frontend/package.json`:
+
+```
+  "matchFiles": ["package.json"],
+```
+
+Use `matchPaths` instead if you need more flexible matching.
+
 ### matchPackageNames
 
 Use this field if you want to have one or more exact name matches in your package rule.
diff --git a/lib/config/__snapshots__/validation.spec.ts.snap b/lib/config/__snapshots__/validation.spec.ts.snap
index d1fe1f0849b4a01362d15a733682e5302b8711be..24e09401e0c59613ceebd651bd00cdd6ebf67ec1 100644
--- a/lib/config/__snapshots__/validation.spec.ts.snap
+++ b/lib/config/__snapshots__/validation.spec.ts.snap
@@ -79,7 +79,7 @@ Array [
   },
   Object {
     "depName": "Configuration Error",
-    "message": "packageRules: Each packageRule must contain at least one selector (matchPaths, matchLanguages, matchBaseBranches, matchManagers, matchDatasources, matchDepTypes, matchPackageNames, matchPackagePatterns, excludePackageNames, excludePackagePatterns, matchCurrentVersion, matchSourceUrlPrefixes, matchUpdateTypes). If you wish for configuration to apply to all packages, it is not necessary to place it inside a packageRule at all.",
+    "message": "packageRules: Each packageRule must contain at least one selector (matchPackageRules, matchPaths, matchLanguages, matchBaseBranches, matchManagers, matchDatasources, matchDepTypes, matchPackageNames, matchPackagePatterns, excludePackageNames, excludePackagePatterns, matchCurrentVersion, matchSourceUrlPrefixes, matchUpdateTypes). If you wish for configuration to apply to all packages, it is not necessary to place it inside a packageRule at all.",
   },
   Object {
     "depName": "Configuration Error",
diff --git a/lib/config/common.ts b/lib/config/common.ts
index 2dc21f073b51c80c86b6e384cc641c0627d93dd1..6de663299a9292ed87c2e1dddc95f6183fdf26ed 100644
--- a/lib/config/common.ts
+++ b/lib/config/common.ts
@@ -220,6 +220,7 @@ export interface PackageRule
   extends RenovateSharedConfig,
     UpdateConfig,
     Record<string, any> {
+  matchFiles?: string[];
   matchPaths?: string[];
   matchLanguages?: string[];
   matchBaseBranches?: string[];
diff --git a/lib/config/definitions.ts b/lib/config/definitions.ts
index 901f9487ebfd7110de2709dcc2cfcab53f192671..2e119fd3d321d17fbae9ae45d13a91ba40585601 100644
--- a/lib/config/definitions.ts
+++ b/lib/config/definitions.ts
@@ -942,6 +942,17 @@ const options: RenovateOptions[] = [
     cli: false,
     env: false,
   },
+  {
+    name: 'matchFiles',
+    description:
+      'List of strings to do an exact match against package files with full path. Applicable inside packageRules only.',
+    type: 'array',
+    subType: 'string',
+    stage: 'repository',
+    parent: 'packageRules',
+    cli: false,
+    env: false,
+  },
   {
     name: 'matchPaths',
     description:
diff --git a/lib/config/validation.ts b/lib/config/validation.ts
index 660b73b75067baa54ab2d3ed8c61349034ac38c0..834d2be7e212b37fc867f1f01fdf01b8c19df39d 100644
--- a/lib/config/validation.ts
+++ b/lib/config/validation.ts
@@ -217,6 +217,7 @@ export async function validateConfig(
             }
 
             const selectors = [
+              'matchPackageRules',
               'matchPaths',
               'matchLanguages',
               'matchBaseBranches',
diff --git a/lib/util/package-rules.spec.ts b/lib/util/package-rules.spec.ts
index acc3b2150c6ac4e1877619464e59afbe0f2e096b..87e180806f2881a845244eaa6ed9ea3cbae39d75 100644
--- a/lib/util/package-rules.spec.ts
+++ b/lib/util/package-rules.spec.ts
@@ -605,6 +605,28 @@ describe('applyPackageRules()', () => {
     expect(res1.x).toBeUndefined();
     expect(res2.x).toBeDefined();
   });
+  it('matches packageFiles', () => {
+    const config: TestConfig = {
+      packageFile: 'examples/foo/package.json',
+      packageRules: [
+        {
+          matchFiles: ['package.json'],
+          x: 1,
+        },
+      ],
+    };
+    const res1 = applyPackageRules({
+      ...config,
+      depName: 'test',
+    });
+    expect(res1.x).toBeUndefined();
+    config.packageFile = 'package.json';
+    const res2 = applyPackageRules({
+      ...config,
+      depName: 'test',
+    });
+    expect(res2.x).toBeDefined();
+  });
   it('matches paths', () => {
     const config: TestConfig = {
       packageFile: 'examples/foo/package.json',
diff --git a/lib/util/package-rules.ts b/lib/util/package-rules.ts
index ba52d25b9971cd01530580f0a6b709faa63f7d83..c5e33291f50a22e6df1d2b205a81bbe62a06fafe 100644
--- a/lib/util/package-rules.ts
+++ b/lib/util/package-rules.ts
@@ -43,6 +43,7 @@ function matchesRule(inputConfig: Config, packageRule: PackageRule): boolean {
     datasource,
   } = inputConfig;
   // Setting empty arrays simplifies our logic later
+  const matchFiles = packageRule.matchFiles || [];
   const matchPaths = packageRule.matchPaths || [];
   const matchLanguages = packageRule.matchLanguages || [];
   const matchBaseBranches = packageRule.matchBaseBranches || [];
@@ -64,6 +65,13 @@ function matchesRule(inputConfig: Config, packageRule: PackageRule): boolean {
   ) {
     matchPackagePatterns = ['.*'];
   }
+  if (matchFiles.length) {
+    const isMatch = matchFiles.some((fileName) => packageFile === fileName);
+    if (!isMatch) {
+      return false;
+    }
+    positiveMatch = true;
+  }
   if (matchPaths.length) {
     const isMatch = matchPaths.some(
       (rulePath) =>