diff --git a/app/assets/javascripts/monitoring/components/dashboard.vue b/app/assets/javascripts/monitoring/components/dashboard.vue
index 33f6afc9c2d91a98ff3a8c5eec7dfffbe9eb8d6f..a2bf58d007c03d6185e2e73589ec46ecfaceb1a9 100644
--- a/app/assets/javascripts/monitoring/components/dashboard.vue
+++ b/app/assets/javascripts/monitoring/components/dashboard.vue
@@ -1,5 +1,5 @@
 <script>
-import { GlDropdown, GlDropdownItem, GlLink } from '@gitlab/ui';
+import { GlButton, GlDropdown, GlDropdownItem, GlLink } from '@gitlab/ui';
 import _ from 'underscore';
 import { s__ } from '~/locale';
 import Icon from '~/vue_shared/components/icon.vue';
@@ -23,12 +23,18 @@ export default {
     GraphGroup,
     EmptyState,
     Icon,
+    GlButton,
     GlDropdown,
     GlDropdownItem,
     GlLink,
   },
 
   props: {
+    externalDashboardPath: {
+      type: String,
+      required: false,
+      default: '',
+    },
     hasMetrics: {
       type: Boolean,
       required: false,
@@ -241,6 +247,15 @@ export default {
           >
         </gl-dropdown>
       </div>
+      <gl-button
+        v-if="externalDashboardPath.length"
+        class="js-external-dashboard-link"
+        variant="primary"
+        :href="externalDashboardPath"
+      >
+        {{ __('View full dashboard') }}
+        <icon name="external-link" />
+      </gl-button>
     </div>
     <graph-group
       v-for="(groupData, index) in store.groups"
diff --git a/app/assets/javascripts/operation_settings/components/external_dashboard.vue b/app/assets/javascripts/operation_settings/components/external_dashboard.vue
new file mode 100644
index 0000000000000000000000000000000000000000..0a87d193b720b82bd9b3029098f242a6cb23721a
--- /dev/null
+++ b/app/assets/javascripts/operation_settings/components/external_dashboard.vue
@@ -0,0 +1,57 @@
+<script>
+import { GlButton, GlFormGroup, GlFormInput, GlLink } from '@gitlab/ui';
+
+export default {
+  components: {
+    GlButton,
+    GlFormGroup,
+    GlFormInput,
+    GlLink,
+  },
+  props: {
+    externalDashboardPath: {
+      type: String,
+      required: false,
+      default: '',
+    },
+    externalDashboardHelpPagePath: {
+      type: String,
+      required: true,
+    },
+  },
+};
+</script>
+
+<template>
+  <section class="settings expanded">
+    <div class="settings-header">
+      <h4 class="js-section-header">
+        {{ s__('ExternalMetrics|External Dashboard') }}
+      </h4>
+      <p class="js-section-sub-header">
+        {{
+          s__(
+            'ExternalMetrics|Add a button to the metrics dashboard linking directly to your existing external dashboards.',
+          )
+        }}
+        <gl-link :href="externalDashboardHelpPagePath">{{ __('Learn more') }}</gl-link>
+      </p>
+    </div>
+    <div class="settings-content">
+      <form>
+        <gl-form-group
+          :label="s__('ExternalMetrics|Full dashboard URL')"
+          :description="s__('ExternalMetrics|Enter the URL of the dashboard you want to link to')"
+        >
+          <gl-form-input
+            :value="externalDashboardPath"
+            placeholder="https://my-org.gitlab.io/my-dashboards"
+          />
+        </gl-form-group>
+        <gl-button variant="success">
+          {{ __('Save Changes') }}
+        </gl-button>
+      </form>
+    </div>
+  </section>
+</template>
diff --git a/app/assets/javascripts/operation_settings/index.js b/app/assets/javascripts/operation_settings/index.js
new file mode 100644
index 0000000000000000000000000000000000000000..1171f3ece9ffb35b47c0c84a49782265b224dff1
--- /dev/null
+++ b/app/assets/javascripts/operation_settings/index.js
@@ -0,0 +1,26 @@
+import Vue from 'vue';
+import ExternalDashboardForm from './components/external_dashboard.vue';
+
+export default () => {
+  /**
+   * This check can be removed when we remove
+   * the :grafana_dashboard_link feature flag
+   */
+  if (!gon.features.grafanaDashboardLink) {
+    return null;
+  }
+
+  const el = document.querySelector('.js-operation-settings');
+
+  return new Vue({
+    el,
+    render(createElement) {
+      return createElement(ExternalDashboardForm, {
+        props: {
+          ...el.dataset,
+          expanded: false,
+        },
+      });
+    },
+  });
+};
diff --git a/app/assets/javascripts/pages/projects/settings/operations/show/index.js b/app/assets/javascripts/pages/projects/settings/operations/show/index.js
index 73c745179be2e633deae459373568900c01e2a65..5270a7924ec9e29b3ac868678633370d86751fca 100644
--- a/app/assets/javascripts/pages/projects/settings/operations/show/index.js
+++ b/app/assets/javascripts/pages/projects/settings/operations/show/index.js
@@ -1,5 +1,7 @@
 import mountErrorTrackingForm from '~/error_tracking_settings';
+import mountOperationSettings from '~/operation_settings';
 
 document.addEventListener('DOMContentLoaded', () => {
   mountErrorTrackingForm();
+  mountOperationSettings();
 });
diff --git a/app/controllers/projects/environments_controller.rb b/app/controllers/projects/environments_controller.rb
index d8812c023ca75c100cb74dc687d6355590ffb2e5..5a4adea497b002066ecdfdc3b85b63e0d99d33fd 100644
--- a/app/controllers/projects/environments_controller.rb
+++ b/app/controllers/projects/environments_controller.rb
@@ -14,6 +14,7 @@ class Projects::EnvironmentsController < Projects::ApplicationController
     push_frontend_feature_flag(:metrics_time_window)
     push_frontend_feature_flag(:environment_metrics_use_prometheus_endpoint)
     push_frontend_feature_flag(:environment_metrics_show_multiple_dashboards)
+    push_frontend_feature_flag(:grafana_dashboard_link)
   end
 
   def index
diff --git a/app/controllers/projects/settings/operations_controller.rb b/app/controllers/projects/settings/operations_controller.rb
index 5cfb0ac307d45c3becf100bb103efe7c46a5d43e..b5c77e5bbf419853f33e6d21ae0df8f67354c433 100644
--- a/app/controllers/projects/settings/operations_controller.rb
+++ b/app/controllers/projects/settings/operations_controller.rb
@@ -5,6 +5,10 @@ module Projects
     class OperationsController < Projects::ApplicationController
       before_action :authorize_update_environment!
 
+      before_action do
+        push_frontend_feature_flag(:grafana_dashboard_link)
+      end
+
       helper_method :error_tracking_setting
 
       def show
diff --git a/app/views/projects/settings/operations/_external_dashboard.html.haml b/app/views/projects/settings/operations/_external_dashboard.html.haml
new file mode 100644
index 0000000000000000000000000000000000000000..2fbb9195a04dcabf9cb737679975502097f3b398
--- /dev/null
+++ b/app/views/projects/settings/operations/_external_dashboard.html.haml
@@ -0,0 +1,2 @@
+.js-operation-settings{ data: { external_dashboard: { path: '',
+  help_page_path: help_page_path('user/project/operations/link_to_external_dashboard') } } }
diff --git a/app/views/projects/settings/operations/show.html.haml b/app/views/projects/settings/operations/show.html.haml
index 6f777305a549a4b71081766fe07e368bea517af3..edc2c58a8ed6dffbf94a8118a12d6495ba1e1777 100644
--- a/app/views/projects/settings/operations/show.html.haml
+++ b/app/views/projects/settings/operations/show.html.haml
@@ -4,4 +4,5 @@
 
 = render_if_exists 'projects/settings/operations/incidents'
 = render 'projects/settings/operations/error_tracking', expanded: true
+= render 'projects/settings/operations/external_dashboard'
 = render_if_exists 'projects/settings/operations/tracing'
diff --git a/locale/gitlab.pot b/locale/gitlab.pot
index 43c88848126fedba519da773ad7b0945b662b127..05e56f2b89d9be751dd7f6e41187eb643ea4ec86 100644
--- a/locale/gitlab.pot
+++ b/locale/gitlab.pot
@@ -4115,6 +4115,18 @@ msgstr ""
 msgid "ExternalAuthorizationService|When no classification label is set the default label `%{default_label}` will be used."
 msgstr ""
 
+msgid "ExternalMetrics|Add a button to the metrics dashboard linking directly to your existing external dashboards."
+msgstr ""
+
+msgid "ExternalMetrics|Enter the URL of the dashboard you want to link to"
+msgstr ""
+
+msgid "ExternalMetrics|External Dashboard"
+msgstr ""
+
+msgid "ExternalMetrics|Full dashboard URL"
+msgstr ""
+
 msgid "ExternalWikiService|External Wiki"
 msgstr ""
 
@@ -10565,6 +10577,9 @@ msgstr ""
 msgid "View file @ "
 msgstr ""
 
+msgid "View full dashboard"
+msgstr ""
+
 msgid "View group labels"
 msgstr ""
 
diff --git a/spec/frontend/operation_settings/components/external_dashboard_spec.js b/spec/frontend/operation_settings/components/external_dashboard_spec.js
new file mode 100644
index 0000000000000000000000000000000000000000..de1dd219fe0337284dd0707d9f57dc3a097fa276
--- /dev/null
+++ b/spec/frontend/operation_settings/components/external_dashboard_spec.js
@@ -0,0 +1,100 @@
+import { shallowMount } from '@vue/test-utils';
+import { GlButton, GlLink, GlFormGroup, GlFormInput } from '@gitlab/ui';
+import ExternalDashboard from '~/operation_settings/components/external_dashboard.vue';
+import { TEST_HOST } from 'helpers/test_constants';
+
+describe('operation settings external dashboard component', () => {
+  let wrapper;
+  const externalDashboardPath = `http://mock-external-domain.com/external/dashboard/path`;
+  const externalDashboardHelpPagePath = `${TEST_HOST}/help/page/path`;
+
+  beforeEach(() => {
+    wrapper = shallowMount(ExternalDashboard, {
+      propsData: {
+        externalDashboardPath,
+        externalDashboardHelpPagePath,
+      },
+    });
+  });
+
+  it('renders header text', () => {
+    expect(wrapper.find('.js-section-header').text()).toBe('External Dashboard');
+  });
+
+  describe('sub-header', () => {
+    let subHeader;
+
+    beforeEach(() => {
+      subHeader = wrapper.find('.js-section-sub-header');
+    });
+
+    it('renders descriptive text', () => {
+      expect(subHeader.text()).toContain(
+        'Add a button to the metrics dashboard linking directly to your existing external dashboards.',
+      );
+    });
+
+    it('renders help page link', () => {
+      const link = subHeader.find(GlLink);
+
+      expect(link.text()).toBe('Learn more');
+      expect(link.attributes().href).toBe(externalDashboardHelpPagePath);
+    });
+  });
+
+  describe('form', () => {
+    let form;
+
+    beforeEach(() => {
+      form = wrapper.find('form');
+    });
+
+    describe('external dashboard url', () => {
+      describe('input label', () => {
+        let formGroup;
+
+        beforeEach(() => {
+          formGroup = form.find(GlFormGroup);
+        });
+
+        it('uses label text', () => {
+          expect(formGroup.attributes().label).toBe('Full dashboard URL');
+        });
+
+        it('uses description text', () => {
+          expect(formGroup.attributes().description).toBe(
+            'Enter the URL of the dashboard you want to link to',
+          );
+        });
+      });
+
+      describe('input field', () => {
+        let input;
+
+        beforeEach(() => {
+          input = form.find(GlFormInput);
+        });
+
+        it('defaults to externalDashboardPath prop', () => {
+          expect(input.attributes().value).toBe(externalDashboardPath);
+        });
+
+        it('uses a placeholder', () => {
+          expect(input.attributes().placeholder).toBe('https://my-org.gitlab.io/my-dashboards');
+        });
+      });
+
+      describe('submit button', () => {
+        let submit;
+
+        beforeEach(() => {
+          submit = form.find(GlButton);
+        });
+
+        it('renders button label', () => {
+          expect(submit.text()).toBe('Save Changes');
+        });
+      });
+    });
+  });
+});
diff --git a/spec/javascripts/monitoring/dashboard_spec.js b/spec/javascripts/monitoring/dashboard_spec.js
index 5c28840d3a4868f997e1760a1899e3b8a5e1ce04..fc722867b0b0930f7ad8eeed89d324f37954b5ca 100644
--- a/spec/javascripts/monitoring/dashboard_spec.js
+++ b/spec/javascripts/monitoring/dashboard_spec.js
@@ -37,6 +37,9 @@ describe('Dashboard', () => {
     window.gon = {
       ...window.gon,
       ee: false,
+      features: {
+        grafanaDashboardLink: true,
+      },
     };
 
     mock = new MockAdapter(axios);
@@ -323,4 +326,63 @@ describe('Dashboard', () => {
         .catch(done.fail);
     });
   });
+
+  describe('external dashboard link', () => {
+    let component;
+
+    beforeEach(() => {
+      mock.onGet(mockApiEndpoint).reply(200, metricsGroupsAPIResponse);
+    });
+
+    afterEach(() => {
+      component.$destroy();
+    });
+
+    describe('with feature flag enabled', () => {
+      beforeEach(() => {
+        component = new DashboardComponent({
+          el: document.querySelector('.prometheus-graphs'),
+          propsData: {
+            ...propsData,
+            hasMetrics: true,
+            showPanels: false,
+            showTimeWindowDropdown: false,
+            externalDashboardPath: '/mockPath',
+          },
+        });
+      });
+
+      it('shows the link', done => {
+        setTimeout(() => {
+          expect(component.$el.querySelector('.js-external-dashboard-link').innerText).toContain(
+            'View full dashboard',
+          );
+          done();
+        });
+      });
+    });
+
+    describe('without feature flage enabled', () => {
+      beforeEach(() => {
+        window.gon.features.grafanaDashboardLink = false;
+        component = new DashboardComponent({
+          el: document.querySelector('.prometheus-graphs'),
+          propsData: {
+            ...propsData,
+            hasMetrics: true,
+            showPanels: false,
+            showTimeWindowDropdown: false,
+            externalDashboardPath: '',
+          },
+        });
+      });
+
+      it('does not show the link', done => {
+        setTimeout(() => {
+          expect(component.$el.querySelector('.js-external-dashboard-link')).toBe(null);
+          done();
+        });
+      });
+    });
+  });
 });