diff --git a/docs/usage/getting-started/private-packages.md b/docs/usage/getting-started/private-packages.md
index 0dfa4e59d7afd85f2deb7f05250266fe52d730cf..451cb54b4ecb64846ed2c31f77b2f8d34ac24f43 100644
--- a/docs/usage/getting-started/private-packages.md
+++ b/docs/usage/getting-started/private-packages.md
@@ -88,6 +88,62 @@ Here is an example of some host rules:
 Renovate applies theses `hostRules` to every HTTP(s) request which is sent, so they are largely independent of any platform or datasource logic.
 With `hostRules` in place, private package lookups should all work.
 
+### GitHub (and Enterprise) repo scoped credentials
+
+If you need to use different credentials for a specific GitHub repo, then you can configure `hostRules` like one of the following:
+
+```json
+{
+  "hostRules": [
+    {
+      "matchHost": "https://api.github.com/repos/org/repo",
+      "token": "abc123"
+    },
+    {
+      "matchHost": "https://github.domain.com/api/v3/repos/org/repo",
+      "token": "abc123"
+    }
+  ]
+}
+```
+
+Renovate will use those credentials for all requests to `org/repo`.
+
+#### Example for gomod
+
+Here's an example for `gomod` with private github.com repos.
+Assume this config is used on the `github.com/some-other-org` repo:
+
+```json
+{
+  "$schema": "https://docs.renovatebot.com/renovate-schema.json",
+  "dependencyDashboard": true,
+  "hostRules": [
+    {
+      "matchHost": "https://gitlab.com",
+      "token": "glpat-token_for_different_git_platform",
+      "hostType": "gitlab"
+    },
+    {
+      "matchHost": "https://github.com/some-org",
+      "token": "ghp_token_for_different_org",
+      "hostType": "go"
+    },
+    {
+      "matchHost": "https://api.github.com/repos/some-org",
+      "token": "ghp_token_for_different_org",
+      "hostType": "github"
+    }
+  ],
+  "customEnvVariables": {
+    "GOPRIVATE": "github.com/some-org,github.com/some-other-org,gitlab.com/some-org",
+    "GONOSUMDB": "github.com/some-org,github.com/some-other-org,gitlab.com/some-org",
+    "GONOPROXY": "github.com/some-org,github.com/some-other-org,gitlab.com/some-org"
+  },
+  "postUpdateOptions": ["gomodTidy"]
+}
+```
+
 ## Looking up Release Notes
 
 When Renovate creates Pull Requests, its default behavior is to locate and embed release notes/changelogs of packages.
diff --git a/lib/modules/datasource/github-releases/cache/cache-base.ts b/lib/modules/datasource/github-releases/cache/cache-base.ts
index c9cb5081f4df5d4fe809110846ffb30560ef63f2..998c3164d83d9c9345a958812149f3009f8dd507 100644
--- a/lib/modules/datasource/github-releases/cache/cache-base.ts
+++ b/lib/modules/datasource/github-releases/cache/cache-base.ts
@@ -4,6 +4,7 @@ import * as packageCache from '../../../../util/cache/package';
 import type {
   GithubGraphqlResponse,
   GithubHttp,
+  GithubHttpOptions,
 } from '../../../../util/http/github';
 import type { GetReleasesConfig } from '../../types';
 import { getApiBaseUrl } from '../common';
@@ -164,12 +165,14 @@ export abstract class AbstractGithubDatasourceCache<
 
   private async query(
     baseUrl: string,
-    variables: GithubQueryParams
+    variables: GithubQueryParams,
+    options: GithubHttpOptions
   ): Promise<QueryResponse<FetchedItem> | Error> {
     try {
       const graphqlRes = await this.http.postJson<
         GithubGraphqlResponse<QueryResponse<FetchedItem>>
       >('/graphql', {
+        ...options,
         baseUrl,
         body: { query: this.graphqlQuery, variables },
       });
@@ -264,7 +267,9 @@ export abstract class AbstractGithubDatasourceCache<
           : this.maxPrefetchPages;
         let stopIteration = false;
         while (pagesRemained > 0 && !stopIteration) {
-          const res = await this.query(baseUrl, variables);
+          const res = await this.query(baseUrl, variables, {
+            repository: packageName,
+          });
           if (res instanceof Error) {
             if (
               res.message.startsWith(
diff --git a/lib/util/http/github.spec.ts b/lib/util/http/github.spec.ts
index 1e57db28ecab3e960552b42335ac0ea7f8ea4c37..6096fc01d18d441decaeca187d1afe757350e400 100644
--- a/lib/util/http/github.spec.ts
+++ b/lib/util/http/github.spec.ts
@@ -138,6 +138,83 @@ describe('util/http/github', () => {
       expect(res.body.the_field).toEqual(['a', 'b', 'c', 'd']);
     });
 
+    it('paginates with auth and repo', async () => {
+      const url = '/some-url?per_page=2';
+      hostRules.add({
+        hostType: 'github',
+        token: 'test',
+        matchHost: 'github.com',
+      });
+      hostRules.add({
+        hostType: 'github',
+        token: 'abc',
+        matchHost: 'https://api.github.com/repos/some/repo',
+      });
+      httpMock
+        .scope(githubApiHost, {
+          reqheaders: {
+            authorization: 'token abc',
+            accept: 'application/vnd.github.v3+json',
+          },
+        })
+        .get(url)
+        .reply(200, ['a', 'b'], {
+          link: `<${url}&page=2>; rel="next", <${url}&page=3>; rel="last"`,
+        })
+        .get(`${url}&page=2`)
+        .reply(200, ['c', 'd'], {
+          link: `<${url}&page=3>; rel="next", <${url}&page=3>; rel="last"`,
+        })
+        .get(`${url}&page=3`)
+        .reply(200, ['e']);
+      const res = await githubApi.getJson(url, {
+        paginate: true,
+        repository: 'some/repo',
+      });
+      expect(res.body).toEqual(['a', 'b', 'c', 'd', 'e']);
+    });
+
+    it('paginates with auth and repo on GHE', async () => {
+      const url = '/api/v3/some-url?per_page=2';
+      hostRules.add({
+        hostType: 'github',
+        token: 'test',
+        matchHost: 'github.domain.com',
+      });
+      hostRules.add({
+        hostType: 'github',
+        token: 'abc',
+        matchHost: 'https://github.domain.com/api/v3/repos/some/repo',
+      });
+      httpMock
+        .scope('https://github.domain.com', {
+          reqheaders: {
+            authorization: 'token abc',
+            accept:
+              'application/vnd.github.antiope-preview+json, application/vnd.github.v3+json',
+          },
+        })
+        .get(url)
+        .reply(200, ['a', 'b'], {
+          link: `<${url}&page=2>; rel="next", <${url}&page=3>; rel="last"`,
+        })
+        .get(`${url}&page=2`)
+        .reply(200, ['c', 'd'], {
+          link: `<${url}&page=3>; rel="next", <${url}&page=3>; rel="last"`,
+        })
+        .get(`${url}&page=3`)
+        .reply(200, ['e']);
+      const res = await githubApi.getJson(url, {
+        paginate: true,
+        repository: 'some/repo',
+        baseUrl: 'https://github.domain.com',
+        headers: {
+          accept: 'application/vnd.github.antiope-preview+json',
+        },
+      });
+      expect(res.body).toEqual(['a', 'b', 'c', 'd', 'e']);
+    });
+
     it('attempts to paginate', async () => {
       const url = '/some-url';
       httpMock
diff --git a/lib/util/http/github.ts b/lib/util/http/github.ts
index 45021b32d48aa73af2df45b3cb3b5e58040f5c83..1350088968a18aa210b360c902436084a1dbad2b 100644
--- a/lib/util/http/github.ts
+++ b/lib/util/http/github.ts
@@ -14,7 +14,8 @@ import { getCache } from '../cache/repository';
 import { maskToken } from '../mask';
 import { range } from '../range';
 import { regEx } from '../regex';
-import { parseLinkHeader } from '../url';
+import { joinUrlParts, parseLinkHeader, resolveBaseUrl } from '../url';
+import { findMatchingRules } from './host-rules';
 import type { GotLegacyError } from './legacy';
 import type {
   GraphqlOptions,
@@ -35,6 +36,7 @@ export interface GithubHttpOptions extends HttpOptions {
   paginate?: boolean | string;
   paginationField?: string;
   pageLimit?: number;
+  repository?: string;
 }
 
 interface GithubGraphqlRepoData<T = unknown> {
@@ -170,9 +172,12 @@ function constructAcceptString(input?: any): string {
   const defaultAccept = 'application/vnd.github.v3+json';
   const acceptStrings =
     typeof input === 'string' ? input.split(regEx(/\s*,\s*/)) : [];
+
+  // TODO: regression of #6736
   if (
-    !acceptStrings.some((x) => x.startsWith('application/vnd.github.')) ||
-    acceptStrings.length < 2
+    !acceptStrings.some((x) => x === defaultAccept) &&
+    (!acceptStrings.some((x) => x.startsWith('application/vnd.github.')) ||
+      acceptStrings.length < 2)
   ) {
     acceptStrings.push(defaultAccept);
   }
@@ -273,12 +278,33 @@ export class GithubHttp extends Http<GithubHttpOptions, GithubHttpOptions> {
     options?: InternalHttpOptions & GithubHttpOptions,
     okToRetry = true
   ): Promise<HttpResponse<T>> {
-    const opts = {
+    const opts: GithubHttpOptions = {
       baseUrl,
       ...options,
       throwHttpErrors: true,
     };
 
+    if (!opts.token) {
+      const authUrl = new URL(resolveBaseUrl(opts.baseUrl!, url));
+
+      if (opts.repository) {
+        // set authUrl to https://api.github.com/repos/org/repo or https://gihub.domain.com/api/v3/repos/org/repo
+        authUrl.hash = '';
+        authUrl.search = '';
+        authUrl.pathname = joinUrlParts(
+          authUrl.pathname.startsWith('/api/v3') ? '/api/v3' : '',
+          'repos',
+          `${opts.repository}`
+        );
+      }
+
+      const { token } = findMatchingRules(
+        { hostType: this.hostType },
+        authUrl.toString()
+      );
+      opts.token = token;
+    }
+
     const accept = constructAcceptString(opts.headers?.accept);
 
     opts.headers = {
@@ -300,7 +326,7 @@ export class GithubHttp extends Http<GithubHttpOptions, GithubHttpOptions> {
           }
           const queue = [...range(2, lastPage)].map(
             (pageNumber) => (): Promise<HttpResponse<T>> => {
-              const nextUrl = new URL(linkHeader.next.url, baseUrl);
+              const nextUrl = new URL(linkHeader.next.url, opts.baseUrl);
               nextUrl.searchParams.set('page', String(pageNumber));
               return this.request<T>(
                 nextUrl,
diff --git a/lib/util/http/host-rules.ts b/lib/util/http/host-rules.ts
index 92d23a7df83ec7ce7134a26c1b0572958afe7656..2e829499732671d2a88281bf87565daeab893807 100644
--- a/lib/util/http/host-rules.ts
+++ b/lib/util/http/host-rules.ts
@@ -10,7 +10,7 @@ import type { HostRule } from '../../types';
 import * as hostRules from '../host-rules';
 import type { GotOptions } from './types';
 
-function findMatchingRules(options: GotOptions, url: string): HostRule {
+export function findMatchingRules(options: GotOptions, url: string): HostRule {
   const { hostType } = options;
   let res = hostRules.find({ hostType, url });
 
diff --git a/lib/util/http/index.ts b/lib/util/http/index.ts
index ce83378947e75d024ef9ad03189e8ff31f59b659..21bb432567af5196a8ed90fd3614a82f58c009ca 100644
--- a/lib/util/http/index.ts
+++ b/lib/util/http/index.ts
@@ -95,7 +95,7 @@ async function gotRoutine<T>(
 export class Http<GetOptions = HttpOptions, PostOptions = HttpPostOptions> {
   private options?: GotOptions;
 
-  constructor(private hostType: string, options: HttpOptions = {}) {
+  constructor(protected hostType: string, options: HttpOptions = {}) {
     this.options = merge<GotOptions>(options, { context: { hostType } });
   }