diff --git a/lib/manager/npm/post-update/types.ts b/lib/manager/npm/post-update/types.ts
index 035ac3d39d47c23597a4f0f664d2c4a5f8deacef..8b4605f2274cd4a463ed41c93d5ae791cff8093b 100644
--- a/lib/manager/npm/post-update/types.ts
+++ b/lib/manager/npm/post-update/types.ts
@@ -19,6 +19,7 @@ export interface ArtifactError {
 export interface UpdatedArtifacts {
   name: string;
   contents: string | Buffer;
+  executable?: boolean;
 }
 
 export interface WriteExistingFilesResult {
diff --git a/lib/util/git/index.spec.ts b/lib/util/git/index.spec.ts
index d7412b372082832a59a5b28670bf2a0f0f4f94fb..42bae467a5b059b35200ad11e126ae9e0ad659d8 100644
--- a/lib/util/git/index.spec.ts
+++ b/lib/util/git/index.spec.ts
@@ -412,6 +412,24 @@ describe('util/git/index', () => {
         expect.objectContaining({ '--no-verify': null })
       );
     });
+
+    it('creates file with the executable bit', async () => {
+      const file = {
+        name: 'some-executable',
+        contents: 'some new-contents',
+        executable: true,
+      };
+      const commit = await git.commitFiles({
+        branchName: 'renovate/past_branch',
+        files: [file],
+        message: 'Create something',
+      });
+      expect(commit).not.toBeNull();
+
+      const repo = Git(tmpDir.path);
+      const result = await repo.raw(['ls-tree', 'HEAD', 'some-executable']);
+      expect(result).toStartWith('100755');
+    });
   });
 
   describe('getCommitMessages()', () => {
diff --git a/lib/util/git/index.ts b/lib/util/git/index.ts
index 3c7610ecb4562b349aaf34c411abbddd3dcc74c3..ebcbcf198124331932ce01e8e0916cfaa94a2d98 100644
--- a/lib/util/git/index.ts
+++ b/lib/util/git/index.ts
@@ -653,6 +653,11 @@ export interface File {
    * file contents
    */
   contents: string | Buffer;
+
+  /**
+   * the executable bit
+   */
+  executable?: boolean;
 }
 
 export type CommitFilesConfig = {
@@ -709,10 +714,17 @@ export async function commitFiles({
           } else {
             contents = file.contents;
           }
-          await fs.outputFile(join(localDir, fileName), contents);
+          // some file systems including Windows don't support the mode
+          // so the index should be manually updated after adding the file
+          await fs.outputFile(join(localDir, fileName), contents, {
+            mode: file.executable ? 0o777 : 0o666,
+          });
         }
         try {
           await git.add(fileName);
+          if (file.executable) {
+            await git.raw(['update-index', '--chmod=+x', fileName]);
+          }
           addedModifiedFiles.push(fileName);
         } catch (err) /* istanbul ignore next */ {
           if (