diff --git a/app/controllers/projects/environments_controller.rb b/app/controllers/projects/environments_controller.rb
index 5c49fa842a4fda598bfbf52dd8317da87fbfa348..e51a5c7b84d12db8a21840269aefdaa758e09765 100644
--- a/app/controllers/projects/environments_controller.rb
+++ b/app/controllers/projects/environments_controller.rb
@@ -222,7 +222,7 @@ def metrics_params
 
   def metrics_dashboard_params
     params
-      .permit(:embedded, :group, :title, :y_label, :dashboard_path, :environment, :sample_metrics)
+      .permit(:embedded, :group, :title, :y_label, :dashboard_path, :environment, :sample_metrics, :embed_json)
       .merge(dashboard_path: params[:dashboard], environment: environment)
   end
 
diff --git a/app/services/metrics/dashboard/transient_embed_service.rb b/app/services/metrics/dashboard/transient_embed_service.rb
new file mode 100644
index 0000000000000000000000000000000000000000..035707dceb9fab94dab2a8eb68df149e1c5f4beb
--- /dev/null
+++ b/app/services/metrics/dashboard/transient_embed_service.rb
@@ -0,0 +1,35 @@
+# frozen_string_literal: true
+
+# Acts as a pass-through to allow embeddable dashboards to be
+# generated based on external data, but still processed with the
+# required attributes that allow the FE to render them appropriately.
+#
+# Use Gitlab::Metrics::Dashboard::Finder to retrive dashboards.
+module Metrics
+  module Dashboard
+    class TransientEmbedService < ::Metrics::Dashboard::BaseEmbedService
+      extend ::Gitlab::Utils::Override
+
+      class << self
+        def valid_params?(params)
+          [
+            embedded?(params[:embedded]),
+            params[:embed_json]
+          ].all?
+        end
+      end
+
+      private
+
+      override :get_raw_dashboard
+      def get_raw_dashboard
+        JSON.parse(params[:embed_json])
+      end
+
+      override :sequence
+      def sequence
+        [STAGES::EndpointInserter]
+      end
+    end
+  end
+end
diff --git a/changelogs/unreleased/sy-transient-embeds.yml b/changelogs/unreleased/sy-transient-embeds.yml
new file mode 100644
index 0000000000000000000000000000000000000000..2e2189827bc9f23231c4311b649338e7a099d16e
--- /dev/null
+++ b/changelogs/unreleased/sy-transient-embeds.yml
@@ -0,0 +1,5 @@
+---
+title: Add support for database-independent embedded metric charts
+merge_request: 28618
+author:
+type: added
diff --git a/lib/gitlab/metrics/dashboard/service_selector.rb b/lib/gitlab/metrics/dashboard/service_selector.rb
index b455f95537726f7bc86c615529e84e42f6873fce..49682da320cfa9b9d82b4e98a8bbd26db3f0fbb1 100644
--- a/lib/gitlab/metrics/dashboard/service_selector.rb
+++ b/lib/gitlab/metrics/dashboard/service_selector.rb
@@ -16,6 +16,7 @@ class << self
             ::Metrics::Dashboard::GitlabAlertEmbedService,
             ::Metrics::Dashboard::CustomMetricEmbedService,
             ::Metrics::Dashboard::GrafanaMetricEmbedService,
+            ::Metrics::Dashboard::TransientEmbedService,
             ::Metrics::Dashboard::DynamicEmbedService,
             ::Metrics::Dashboard::DefaultEmbedService,
             ::Metrics::Dashboard::SystemDashboardService,
diff --git a/spec/features/markdown/metrics_spec.rb b/spec/features/markdown/metrics_spec.rb
index 0d8858a7afd9e5abe0cf934b4605cd38002986f0..dadb9571c545ace325d63cef6bbc1eb56c8a7f65 100644
--- a/spec/features/markdown/metrics_spec.rb
+++ b/spec/features/markdown/metrics_spec.rb
@@ -136,6 +136,36 @@
     end
   end
 
+  context 'transient metrics embeds' do
+    let(:metrics_url) { urls.metrics_project_environment_url(project, environment, embed_json: embed_json) }
+    let(:title) { 'Important Metrics' }
+    let(:embed_json) do
+      {
+        panel_groups: [{
+          panels: [{
+            type: "line-graph",
+            title: title,
+            y_label: "metric",
+            metrics: [{
+              query_range: "metric * 0.5 < 1"
+            }]
+          }]
+        }]
+      }.to_json
+    end
+
+    before do
+      stub_any_prometheus_request_with_response
+    end
+
+    it 'shows embedded metrics' do
+      visit project_issue_path(project, issue)
+
+      expect(page).to have_css('div.prometheus-graph')
+      expect(page).to have_text(title)
+    end
+  end
+
   def import_common_metrics
     ::Gitlab::DatabaseImporters::CommonMetrics::Importer.new.execute
   end
diff --git a/spec/lib/gitlab/metrics/dashboard/service_selector_spec.rb b/spec/lib/gitlab/metrics/dashboard/service_selector_spec.rb
index 387baf1ee53646793c3e7d82ab5ea73fe43c6e18..245c98cdd00cb49683d99841612bf7fabf6c83f2 100644
--- a/spec/lib/gitlab/metrics/dashboard/service_selector_spec.rb
+++ b/spec/lib/gitlab/metrics/dashboard/service_selector_spec.rb
@@ -98,6 +98,17 @@
 
         it { is_expected.to be Metrics::Dashboard::GrafanaMetricEmbedService }
       end
+
+      context 'with the embed defined in the arguments' do
+        let(:arguments) do
+          {
+            embedded: true,
+            embed_json: '{}'
+          }
+        end
+
+        it { is_expected.to be Metrics::Dashboard::TransientEmbedService }
+      end
     end
   end
 end
diff --git a/spec/services/metrics/dashboard/transient_embed_service_spec.rb b/spec/services/metrics/dashboard/transient_embed_service_spec.rb
new file mode 100644
index 0000000000000000000000000000000000000000..fddfbe1528118f5e2b3eb1d0f78a025d62b6b324
--- /dev/null
+++ b/spec/services/metrics/dashboard/transient_embed_service_spec.rb
@@ -0,0 +1,72 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe Metrics::Dashboard::TransientEmbedService, :use_clean_rails_memory_store_caching do
+  let_it_be(:project) { build(:project) }
+  let_it_be(:user) { create(:user) }
+  let_it_be(:environment) { create(:environment, project: project) }
+
+  before do
+    project.add_maintainer(user)
+  end
+
+  describe '.valid_params?' do
+    let(:params) { { embedded: 'true', embed_json: '{}' } }
+
+    subject { described_class.valid_params?(params) }
+
+    it { is_expected.to be_truthy }
+
+    context 'missing embedded' do
+      let(:params) { { embed_json: '{}' } }
+
+      it { is_expected.to be_falsey }
+    end
+
+    context 'not embedded' do
+      let(:params) { { embedded: 'false', embed_json: '{}' } }
+
+      it { is_expected.to be_falsey }
+    end
+
+    context 'missing embed_json' do
+      let(:params) { { embedded: 'true' } }
+
+      it { is_expected.to be_falsey }
+    end
+  end
+
+  describe '#get_dashboard' do
+    let(:embed_json) do
+      {
+       panel_groups: [{
+         panels: [{
+           type: 'line-graph',
+           title: 'title',
+           y_label: 'y_label',
+           metrics: [{
+             query_range: 'up',
+             label: 'y_label'
+           }]
+         }]
+       }]
+      }.to_json
+    end
+    let(:service_params) { [project, user, { environment: environment, embedded: 'true', embed_json: embed_json }] }
+    let(:service_call) { described_class.new(*service_params).get_dashboard }
+
+    it_behaves_like 'valid embedded dashboard service response'
+    it_behaves_like 'raises error for users with insufficient permissions'
+
+    it 'caches the unprocessed dashboard for subsequent calls' do
+      expect_any_instance_of(described_class)
+        .to receive(:get_raw_dashboard)
+        .once
+        .and_call_original
+
+      described_class.new(*service_params).get_dashboard
+      described_class.new(*service_params).get_dashboard
+    end
+  end
+end