diff --git a/ee/app/assets/javascripts/ci/runner/admin_runners_dashboard/admin_runners_dashboard_app.vue b/ee/app/assets/javascripts/ci/runner/admin_runners_dashboard/admin_runners_dashboard_app.vue
index d36d329cce4ec43ce587efc79a77b2be98a37673..ba08a7864fb204128a72284e240177dc92a8c442 100644
--- a/ee/app/assets/javascripts/ci/runner/admin_runners_dashboard/admin_runners_dashboard_app.vue
+++ b/ee/app/assets/javascripts/ci/runner/admin_runners_dashboard/admin_runners_dashboard_app.vue
@@ -19,6 +19,11 @@ export default {
     RunnerActiveList,
     RunnerWaitTimes,
   },
+  inject: {
+    clickhouseCiAnalyticsAvailable: {
+      default: false,
+    },
+  },
   props: {
     adminRunnersPath: {
       type: String,
@@ -49,23 +54,22 @@ export default {
       {{ s__('Runners|Use the dashboard to view performance statistics of your runner fleet.') }}
     </p>
 
-    <div>
-      <div class="gl-sm-display-flex gl-gap-4 gl-justify-content-space-between">
-        <runner-dashboard-stat-online
-          class="runners-dashboard-third-gap-4 gl-flex-grow-1 gl-mb-4"
-        />
-        <runner-dashboard-stat-offline
-          class="runners-dashboard-third-gap-4 gl-flex-grow-1 gl-mb-4"
-        />
-        <runner-usage class="runners-dashboard-third-gap-4 gl-flex-grow-1 gl-mb-4" />
-      </div>
+    <div class="gl-sm-display-flex gl-column-gap-4 gl-justify-content-space-between">
+      <div class="gl-sm-display-flex gl-column-gap-4 gl-justify-content-space-between gl-w-full">
+        <div
+          class="runners-dashboard-two-thirds-gap-4 gl-display-flex gl-gap-4 gl-justify-content-space-between gl-mb-4 gl-flex-wrap"
+        >
+          <runner-dashboard-stat-online class="runners-dashboard-half-gap-4" />
+          <runner-dashboard-stat-offline class="runners-dashboard-half-gap-4" />
 
-      <div class="gl-md-display-flex gl-gap-4 gl-justify-content-space-between">
-        <runner-job-failures class="runners-dashboard-two-thirds-gap-4 gl-flex-grow-1 gl-mb-4" />
-        <runner-active-list class="runners-dashboard-third-gap-4 gl-flex-grow-1 gl-mb-4" />
-      </div>
+          <!-- we use job failures as fallback, when clickhouse is not available -->
+          <runner-usage v-if="clickhouseCiAnalyticsAvailable" class="gl-flex-basis-full" />
+          <runner-job-failures v-else class="gl-flex-basis-full" />
+        </div>
 
-      <runner-wait-times class="runners-dashboard-wait-times" />
+        <runner-active-list class="runners-dashboard-third-gap-4 gl-mb-4" />
+      </div>
     </div>
+    <runner-wait-times class="gl-mb-4" />
   </div>
 </template>
diff --git a/ee/app/assets/javascripts/ci/runner/components/runner_usage.vue b/ee/app/assets/javascripts/ci/runner/components/runner_usage.vue
index 21569555e84c0e84d4b813d0b89a6ea401f9f669..ac619cbf4477ab5d55a72f3736294e60f22fedf8 100644
--- a/ee/app/assets/javascripts/ci/runner/components/runner_usage.vue
+++ b/ee/app/assets/javascripts/ci/runner/components/runner_usage.vue
@@ -1,29 +1,59 @@
 <script>
-import { GlButton } from '@gitlab/ui';
+import { GlAvatar, GlButton, GlLink, GlTableLite } from '@gitlab/ui';
 import { createAlert } from '~/alert';
-import { s__ } from '~/locale';
+import { s__, formatNumber } from '~/locale';
 import { confirmAction } from '~/lib/utils/confirm_via_gl_modal/confirm_via_gl_modal';
 import { INSTANCE_TYPE } from '~/ci/runner/constants';
 import * as Sentry from '~/sentry/sentry_browser_wrapper';
+import { getIdFromGraphQLId } from '~/graphql_shared/utils';
 
+import RunnerUsageQuery from '../graphql/performance/runner_usage.query.graphql';
+import RunnerUsageByProjectQuery from '../graphql/performance/runner_usage_by_project.query.graphql';
 import RunnerUsageExportMutation from '../graphql/performance/runner_usage_export.mutation.graphql';
 
+const thClass = ['gl-font-sm!', 'gl-text-secondary!'];
+
 export default {
   name: 'RunnerUsage',
   components: {
+    GlAvatar,
     GlButton,
-  },
-  inject: {
-    clickhouseCiAnalyticsAvailable: {
-      default: false,
-    },
+    GlLink,
+    GlTableLite,
   },
   data() {
     return {
       loading: false,
     };
   },
+  apollo: {
+    topProjects: {
+      query: RunnerUsageByProjectQuery,
+      update(data) {
+        return data.runnerUsageByProject;
+      },
+    },
+    topRunners: {
+      query: RunnerUsageQuery,
+      update(data) {
+        return data.runnerUsage;
+      },
+    },
+  },
   methods: {
+    formatNumber,
+    runnerName(runner) {
+      const { id: graphqlId, shortSha, description } = runner;
+      const id = getIdFromGraphQLId(graphqlId);
+
+      if (description) {
+        return `#${id} (${shortSha}) - ${description}`;
+      }
+      return `#${id} (${shortSha})`;
+    },
+    findClosestTd(el) {
+      return el.closest('td');
+    },
     async onClick() {
       const confirmed = await confirmAction(
         s__(
@@ -76,16 +106,79 @@ export default {
       }
     },
   },
+  topRunnersFields: [
+    {
+      key: 'runner',
+      label: s__('Runners|Most used instance runners'),
+      thClass: [...thClass, 'gl-width-full'],
+    },
+    {
+      key: 'ciMinutesUsed',
+      label: s__('Runners|Usage (min)'),
+      thClass: [...thClass, 'gl-text-right'],
+      tdClass: 'gl-text-right',
+    },
+  ],
+  topProjectsFields: [
+    {
+      key: 'project',
+      label: s__('Runners|Top projects consuming runners'),
+      thClass: [...thClass, 'gl-width-full'],
+    },
+    {
+      key: 'ciMinutesUsed',
+      label: s__('Runners|Usage (min)'),
+      thClass: [...thClass, 'gl-text-right'],
+      tdClass: 'gl-text-right',
+    },
+  ],
 };
 </script>
 <template>
-  <div v-if="clickhouseCiAnalyticsAvailable" class="gl-border gl-rounded-base gl-p-5">
-    <div class="gl-display-flex gl-align-items-center">
-      <h2 class="gl-font-lg gl-m-0 gl-flex-grow-1">{{ s__('Runners|Runner Usage') }}</h2>
-
+  <div class="gl-border gl-rounded-base gl-p-5">
+    <div class="gl-display-flex gl-align-items-center gl-mb-4">
+      <h2 class="gl-font-lg gl-flex-grow-1 gl-m-0">
+        {{ s__('Runners|Runner Usage (previous month)') }}
+      </h2>
       <gl-button :loading="loading" size="small" @click="onClick">
         {{ s__('Runners|Export as CSV') }}
       </gl-button>
     </div>
+
+    <div
+      class="gl-md-display-flex gl-justify-content-space-between gl-align-items-flex-start gl-gap-4"
+    >
+      <gl-table-lite
+        :fields="$options.topProjectsFields"
+        :items="topProjects"
+        class="runners-top-result-table runners-dashboard-half-gap-4"
+        data-testid="top-projects-table"
+      >
+        <template #cell(project)="{ value }">
+          <gl-avatar
+            :label="value.name"
+            :src="value.avatarUrl"
+            shape="rect"
+            :size="16"
+            :entity-name="value.name"
+          />
+          <gl-link :href="value.webUrl" class="gl-text-body!">{{ value.name }}</gl-link>
+        </template>
+
+        <template #cell(ciMinutesUsed)="{ value }">{{ formatNumber(value) }}</template>
+      </gl-table-lite>
+
+      <gl-table-lite
+        :fields="$options.topRunnersFields"
+        :items="topRunners"
+        class="runners-top-result-table runners-dashboard-half-gap-4"
+        data-testid="top-runners-table"
+      >
+        <template #cell(runner)="{ value }">
+          <gl-link :href="value.adminUrl" class="gl-text-body!">{{ runnerName(value) }}</gl-link>
+        </template>
+        <template #cell(ciMinutesUsed)="{ value }">{{ formatNumber(value) }}</template>
+      </gl-table-lite>
+    </div>
   </div>
 </template>
diff --git a/ee/app/assets/javascripts/ci/runner/graphql/performance/runner_usage.query.graphql b/ee/app/assets/javascripts/ci/runner/graphql/performance/runner_usage.query.graphql
new file mode 100644
index 0000000000000000000000000000000000000000..84edf3403c6de831f87bc203f3d093ab83cf826c
--- /dev/null
+++ b/ee/app/assets/javascripts/ci/runner/graphql/performance/runner_usage.query.graphql
@@ -0,0 +1,11 @@
+query getRunnerUsage {
+  runnerUsage(runnerType: INSTANCE_TYPE, runnersLimit: 5) {
+    runner {
+      id
+      shortSha
+      description
+      adminUrl
+    }
+    ciMinutesUsed
+  }
+}
diff --git a/ee/app/assets/javascripts/ci/runner/graphql/performance/runner_usage_by_project.query.graphql b/ee/app/assets/javascripts/ci/runner/graphql/performance/runner_usage_by_project.query.graphql
new file mode 100644
index 0000000000000000000000000000000000000000..fb7f5ec2f5017e52d1254f8daace43a35dce1a68
--- /dev/null
+++ b/ee/app/assets/javascripts/ci/runner/graphql/performance/runner_usage_by_project.query.graphql
@@ -0,0 +1,11 @@
+query getRunnerUsageByProject {
+  runnerUsageByProject(runnerType: INSTANCE_TYPE, projectsLimit: 5) {
+    project {
+      id
+      name
+      avatarUrl
+      webUrl
+    }
+    ciMinutesUsed
+  }
+}
diff --git a/ee/app/assets/stylesheets/page_bundles/runners.scss b/ee/app/assets/stylesheets/page_bundles/runners.scss
index ce52f67432a69c1bda4d7c18fa347beef1689baf..a1838f4c4dc87df5f03cf0d0d9af3870c436b795 100644
--- a/ee/app/assets/stylesheets/page_bundles/runners.scss
+++ b/ee/app/assets/stylesheets/page_bundles/runners.scss
@@ -1,13 +1,21 @@
 @import 'page_bundles/mixins_and_variables_and_functions';
 
+.runners-dashboard-half-gap-4 {
+  // Subtract the length of gl-gap-4 to show a correct gap
+  flex-basis: calc(50% - #{$gl-spacing-scale-4});
+  flex-grow: 1;
+}
+
 .runners-dashboard-third-gap-4 {
   // Subtract the length of gl-gap-4 to show a correct gap
   flex-basis: calc(33.33% - #{$gl-spacing-scale-4});
+  flex-grow: 1;
 }
 
 .runners-dashboard-two-thirds-gap-4 {
   // Subtract half of the length of gl-gap-4 to show a correct gap
   flex-basis: calc(66.66% - #{$gl-spacing-scale-4 / 2});
+  flex-grow: 1;
 }
 
 .runner-active-list-table tr:first-child th {
@@ -17,3 +25,19 @@
 .runner-active-list-table tr:last-child td {
   border-bottom: 0;
 }
+
+
+.runners-top-result-table {
+  td {
+    white-space: nowrap;
+    text-overflow:ellipsis;
+    overflow: hidden;
+    max-width: 1px;
+    padding-top: $gl-spacing-scale-3 !important;
+    padding-bottom: $gl-spacing-scale-3 !important;
+  }
+
+  tr:last-child td {
+    border-bottom: 0;
+  }
+}
diff --git a/ee/spec/frontend/ci/runner/admin_runners_dashboard/admin_runners_dashboard_app_spec.js b/ee/spec/frontend/ci/runner/admin_runners_dashboard/admin_runners_dashboard_app_spec.js
index 51e60c3878b8948057ba815f6100c70a4210a535..fb1532039ec50e4bccda44642917895bb7c1b433 100644
--- a/ee/spec/frontend/ci/runner/admin_runners_dashboard/admin_runners_dashboard_app_spec.js
+++ b/ee/spec/frontend/ci/runner/admin_runners_dashboard/admin_runners_dashboard_app_spec.js
@@ -16,12 +16,13 @@ const mockNewRunnerPath = '/runners/new';
 describe('AdminRunnersDashboardApp', () => {
   let wrapper;
 
-  const createComponent = () => {
+  const createComponent = (options) => {
     wrapper = shallowMountExtended(AdminRunnersDashboardApp, {
       propsData: {
         adminRunnersPath: mockAdminRunnersPath,
         newRunnerPath: mockNewRunnerPath,
       },
+      ...options,
     });
   };
 
@@ -42,9 +43,39 @@ describe('AdminRunnersDashboardApp', () => {
   it('shows dashboard panels', () => {
     expect(wrapper.findComponent(RunnerDashboardStatOnline).exists()).toBe(true);
     expect(wrapper.findComponent(RunnerDashboardStatOffline).exists()).toBe(true);
-    expect(wrapper.findComponent(RunnerUsage).exists()).toBe(true);
-    expect(wrapper.findComponent(RunnerJobFailures).exists()).toBe(true);
     expect(wrapper.findComponent(RunnerActiveList).exists()).toBe(true);
     expect(wrapper.findComponent(RunnerWaitTimes).exists()).toBe(true);
   });
+
+  describe('when clickhouse is available', () => {
+    beforeEach(() => {
+      createComponent({
+        provide: { clickhouseCiAnalyticsAvailable: true },
+      });
+    });
+
+    it('shows runner usage', () => {
+      expect(wrapper.findComponent(RunnerUsage).exists()).toBe(true);
+    });
+
+    it('does not show job failures', () => {
+      expect(wrapper.findComponent(RunnerJobFailures).exists()).toBe(false);
+    });
+  });
+
+  describe('when clickhouse is not available', () => {
+    beforeEach(() => {
+      createComponent({
+        provide: { clickhouseCiAnalyticsAvailable: false },
+      });
+    });
+
+    it('does not runner usage', () => {
+      expect(wrapper.findComponent(RunnerUsage).exists()).toBe(false);
+    });
+
+    it('shows job failures', () => {
+      expect(wrapper.findComponent(RunnerJobFailures).exists()).toBe(true);
+    });
+  });
 });
diff --git a/ee/spec/frontend/ci/runner/components/runner_usage_spec.js b/ee/spec/frontend/ci/runner/components/runner_usage_spec.js
index 3cb68a397c35ef3ea3dc29bc535770bf32daf389..8e012c269897973ae07783a45c78050847a5787a 100644
--- a/ee/spec/frontend/ci/runner/components/runner_usage_spec.js
+++ b/ee/spec/frontend/ci/runner/components/runner_usage_spec.js
@@ -1,7 +1,7 @@
 import Vue from 'vue';
 import VueApollo from 'vue-apollo';
-import { GlButton } from '@gitlab/ui';
-import { shallowMount } from '@vue/test-utils';
+import { GlAvatar, GlButton } from '@gitlab/ui';
+import { shallowMountExtended, mountExtended } from 'helpers/vue_test_utils_helper';
 import { createAlert } from '~/alert';
 import { confirmAction } from '~/lib/utils/confirm_via_gl_modal/confirm_via_gl_modal';
 import * as Sentry from '~/sentry/sentry_browser_wrapper';
@@ -9,6 +9,9 @@ import * as Sentry from '~/sentry/sentry_browser_wrapper';
 import waitForPromises from 'helpers/wait_for_promises';
 import createMockApollo from 'helpers/mock_apollo_helper';
 import { INSTANCE_TYPE } from '~/ci/runner/constants';
+
+import RunnerUsageQuery from 'ee/ci/runner/graphql/performance/runner_usage.query.graphql';
+import RunnerUsageByProjectQuery from 'ee/ci/runner/graphql/performance/runner_usage_by_project.query.graphql';
 import RunnerUsageExportMutation from 'ee/ci/runner/graphql/performance/runner_usage_export.mutation.graphql';
 
 import RunnerUsage from 'ee/ci/runner/components/runner_usage.vue';
@@ -19,36 +22,102 @@ jest.mock('~/alert');
 jest.mock('~/lib/utils/confirm_via_gl_modal/confirm_via_gl_modal');
 jest.mock('~/sentry/sentry_browser_wrapper');
 
+const mockRunnerUsage = [
+  {
+    runner: {
+      id: 'gid://gitlab/Ci::Runner/1',
+      shortSha: 'sha1',
+      description: 'Runner 1',
+      adminUrl: '/admin/runners/1',
+      __typename: 'CiRunner',
+    },
+    ciMinutesUsed: 1001,
+    __typename: 'CiRunnerUsage',
+  },
+  {
+    runner: {
+      id: 'gid://gitlab/Ci::Runner/2',
+      shortSha: 'sha2',
+      description: 'Runner 2',
+      adminUrl: '/admin/runners/2',
+      __typename: 'CiRunner',
+    },
+    ciMinutesUsed: 11,
+    __typename: 'CiRunnerUsage',
+  },
+];
+
+const mockRunnerUsageByProject = [
+  {
+    project: {
+      id: 'gid://gitlab/Project/1',
+      name: 'Project1',
+      avatarUrl: '/project1.png',
+      webUrl: '/group1/project1',
+      __typename: 'Project',
+    },
+    ciMinutesUsed: 1002,
+    __typename: 'CiRunnerUsageByProject',
+  },
+  {
+    project: {
+      id: 'gid://gitlab/Project/22',
+      name: 'Project2',
+      avatarUrl: '/project2.png',
+      webUrl: '/group1/project2',
+      __typename: 'Project',
+    },
+    ciMinutesUsed: 12,
+    __typename: 'CiRunnerUsageByProject',
+  },
+];
+
 describe('RunnerUsage', () => {
   let wrapper;
   let mockToast;
-  let runnerUsageExportHandler;
+
+  const runnerUsageHandler = jest.fn();
+  const runnerUsageByProjectHandler = jest.fn();
+  const runnerUsageExportHandler = jest.fn();
 
   const findButton = () => wrapper.findComponent(GlButton);
+  const findTopRunners = () => wrapper.findByTestId('top-runners-table').findAll('tr');
+  const findTopProjects = () => wrapper.findByTestId('top-projects-table').findAll('tr');
+
   const clickButton = async () => {
     findButton().vm.$emit('click');
     await waitForPromises();
   };
 
-  const createWrapper = ({ provide } = {}) => {
+  const createWrapper = ({ mountFn = shallowMountExtended } = {}) => {
     confirmAction.mockResolvedValue(true);
 
-    runnerUsageExportHandler = jest.fn();
     mockToast = jest.fn();
 
-    wrapper = shallowMount(RunnerUsage, {
-      apolloProvider: createMockApollo([[RunnerUsageExportMutation, runnerUsageExportHandler]]),
+    runnerUsageHandler.mockResolvedValue({
+      data: { runnerUsage: mockRunnerUsage },
+    });
+    runnerUsageByProjectHandler.mockResolvedValue({
+      data: { runnerUsageByProject: mockRunnerUsageByProject },
+    });
+
+    wrapper = mountFn(RunnerUsage, {
+      apolloProvider: createMockApollo([
+        [RunnerUsageQuery, runnerUsageHandler],
+        [RunnerUsageByProjectQuery, runnerUsageByProjectHandler],
+        [RunnerUsageExportMutation, runnerUsageExportHandler],
+      ]),
       mocks: {
         $toast: { show: mockToast },
       },
-      provide: {
-        clickhouseCiAnalyticsAvailable: true,
-        ...provide,
-      },
     });
   };
 
   beforeEach(() => {
+    runnerUsageHandler.mockReset();
+    runnerUsageByProjectHandler.mockReset();
+    runnerUsageExportHandler.mockReset();
+
     createWrapper();
   });
 
@@ -56,12 +125,48 @@ describe('RunnerUsage', () => {
     expect(findButton().text()).toBe('Export as CSV');
   });
 
-  it('does not render when clickhouseCiAnalytics is disabled', () => {
-    createWrapper({
-      provide: { clickhouseCiAnalyticsAvailable: false },
+  it('loads top projects', async () => {
+    createWrapper({ mountFn: mountExtended });
+
+    await waitForPromises();
+
+    const [header, row1, row2] = findTopProjects().wrappers;
+
+    expect(header.text()).toContain('Top projects consuming runners');
+    expect(header.text()).toContain('Usage (min)');
+
+    expect(row1.findComponent(GlAvatar).attributes()).toMatchObject({
+      label: 'Project1',
+      src: '/project1.png',
+    });
+    expect(row1.text()).toContain('Project1');
+    expect(row1.text()).toContain('1,002');
+
+    expect(row2.findComponent(GlAvatar).attributes()).toMatchObject({
+      label: 'Project2',
+      src: '/project2.png',
     });
+    expect(row2.text()).toContain('Project2');
+    expect(row2.text()).toContain('12');
+  });
+
+  it('loads top runners', async () => {
+    createWrapper({ mountFn: mountExtended });
+
+    await waitForPromises();
+
+    const [header, row1, row2] = findTopRunners().wrappers.map((w) => w.text());
+
+    expect(header).toContain('Most used instance runners');
+    expect(header).toContain('Usage (min)');
+
+    expect(row1).toContain('#1 (sha1)');
+    expect(row1).toContain('Runner 1');
+    expect(row1).toContain('1,001');
 
-    expect(wrapper.html()).toBe('');
+    expect(row2).toContain('#2 (sha2)');
+    expect(row2).toContain('Runner 2');
+    expect(row2).toContain('11');
   });
 
   it('calls mutation on button click', async () => {
diff --git a/locale/gitlab.pot b/locale/gitlab.pot
index 8d4ff6a7c9a38edc36066c8dae5b3c642f97b6e6..cc5c4eff54a257bff621cf9e3c1589ef3090e411 100644
--- a/locale/gitlab.pot
+++ b/locale/gitlab.pot
@@ -42579,6 +42579,9 @@ msgstr ""
 msgid "Runners|Most recent failures"
 msgstr ""
 
+msgid "Runners|Most used instance runners"
+msgstr ""
+
 msgid "Runners|Name"
 msgstr ""
 
@@ -42760,7 +42763,7 @@ msgstr ""
 msgid "Runners|Runner Registration token"
 msgstr ""
 
-msgid "Runners|Runner Usage"
+msgid "Runners|Runner Usage (previous month)"
 msgstr ""
 
 msgid "Runners|Runner assigned to project."
@@ -43031,6 +43034,9 @@ msgstr ""
 msgid "Runners|Token expiry"
 msgstr ""
 
+msgid "Runners|Top projects consuming runners"
+msgstr ""
+
 msgid "Runners|UTC Time"
 msgstr ""
 
@@ -43052,6 +43058,9 @@ msgstr ""
 msgid "Runners|Upgrade recommended"
 msgstr ""
 
+msgid "Runners|Usage (min)"
+msgstr ""
+
 msgid "Runners|Use Group runners when you want all projects in a group to have access to a set of runners."
 msgstr ""