diff --git a/ee/app/assets/javascripts/analytics/cycle_analytics/components/base.vue b/ee/app/assets/javascripts/analytics/cycle_analytics/components/base.vue
index fce076eb833953a9e5d322306f036327ab64394f..6109e5afb88794e786abf826e0ff8d83beb3046f 100644
--- a/ee/app/assets/javascripts/analytics/cycle_analytics/components/base.vue
+++ b/ee/app/assets/javascripts/analytics/cycle_analytics/components/base.vue
@@ -17,6 +17,7 @@ import ValueStreamAggregationStatus from './value_stream_aggregation_status.vue'
 import ValueStreamAggregatingWarning from './value_stream_aggregating_warning.vue';
 import ValueStreamEmptyState from './value_stream_empty_state.vue';
 import ValueStreamSelect from './value_stream_select.vue';
+import DurationOverviewChart from './duration_overview_chart.vue';
 
 export default {
   name: 'CycleAnalytics',
@@ -33,6 +34,7 @@ export default {
     ValueStreamMetrics,
     ValueStreamSelect,
     UrlSync,
+    DurationOverviewChart,
   },
   props: {
     emptyStateSvgPath: {
@@ -265,7 +267,13 @@ export default {
           :dashboards-path="dashboardsPath"
         />
         <div :class="[isOverviewStageSelected ? 'gl-mt-2' : 'gl-mt-6']">
-          <duration-chart class="gl-mb-6" :stages="activeStages" :selected-stage="selectedStage" />
+          <duration-overview-chart v-if="isOverviewStageSelected" class="gl-mb-6" />
+          <duration-chart
+            v-else
+            class="gl-mb-6"
+            :stages="activeStages"
+            :selected-stage="selectedStage"
+          />
           <type-of-work-charts v-if="shouldRenderTasksByType" class="gl-mb-6" />
         </div>
         <stage-table
diff --git a/ee/app/assets/javascripts/analytics/cycle_analytics/components/duration_overview_chart.vue b/ee/app/assets/javascripts/analytics/cycle_analytics/components/duration_overview_chart.vue
new file mode 100644
index 0000000000000000000000000000000000000000..4a5ac480d9a552c566b570b55ddf290f279e60cc
--- /dev/null
+++ b/ee/app/assets/javascripts/analytics/cycle_analytics/components/duration_overview_chart.vue
@@ -0,0 +1,219 @@
+<script>
+import { GlAreaChart, GlChartSeriesLabel } from '@gitlab/ui/dist/charts';
+import { mapGetters, mapState } from 'vuex';
+import { GlAlert, GlIcon, GlTooltipDirective } from '@gitlab/ui';
+import ChartSkeletonLoader from '~/vue_shared/components/resizable_chart/skeleton_loader.vue';
+import dateFormat from '~/lib/dateformat';
+import { buildNullSeries, formatDurationOverviewTooltipMetric } from 'ee/analytics/shared/utils';
+import { isNumeric } from '~/lib/utils/number_utils';
+import { n__ } from '~/locale';
+import { progressiveSummation } from '../utils';
+import {
+  DURATION_CHART_Y_AXIS_TITLE,
+  DURATION_TOTAL_TIME_DESCRIPTION,
+  DURATION_TOTAL_TIME_LABEL,
+  DURATION_OVERVIEW_CHART_X_AXIS_DATE_FORMAT,
+  DURATION_OVERVIEW_CHART_X_AXIS_TOOLTIP_TITLE_DATE_FORMAT,
+  DURATION_OVERVIEW_CHART_NO_DATA,
+  DURATION_TOTAL_TIME_NO_DATA,
+} from '../constants';
+
+export default {
+  name: 'DurationOverviewChart',
+  components: {
+    GlAreaChart,
+    GlChartSeriesLabel,
+    GlIcon,
+    GlAlert,
+    ChartSkeletonLoader,
+  },
+  directives: {
+    GlTooltip: GlTooltipDirective,
+  },
+  data() {
+    return {
+      chart: null,
+      tooltipTitle: '',
+      tooltipContent: [],
+      activeDataSeries: '',
+    };
+  },
+  computed: {
+    ...mapState('durationChart', ['isLoading', 'errorMessage']),
+    ...mapGetters('durationChart', ['durationChartPlottableData']),
+    hasData() {
+      return Boolean(
+        !this.isLoading &&
+          this.durationChartPlottableData.some(({ data }) =>
+            data.some(([, metric]) => metric !== null),
+          ),
+      );
+    },
+    error() {
+      return this.errorMessage || DURATION_TOTAL_TIME_NO_DATA;
+    },
+    chartData() {
+      const summedData = progressiveSummation(this.durationChartPlottableData);
+
+      const nonNullSeries = [];
+      const nullSeries = [];
+
+      summedData.forEach(({ name: seriesName, data: seriesData }) => {
+        const valuesSeries = {
+          name: seriesName,
+          data: seriesData,
+        };
+
+        const [nullData, nonNullData] = buildNullSeries({
+          seriesData: [valuesSeries],
+          nullSeriesTitle: DURATION_OVERVIEW_CHART_NO_DATA,
+        });
+
+        const { data, name } = nonNullData;
+
+        nonNullSeries.push({ data, name });
+        nullSeries.push(nullData);
+      });
+
+      return [...nonNullSeries, ...nullSeries];
+    },
+    chartOptions() {
+      return {
+        xAxis: {
+          name: '',
+          type: 'time',
+          axisLabel: {
+            formatter: (date) => dateFormat(date, DURATION_OVERVIEW_CHART_X_AXIS_DATE_FORMAT),
+          },
+        },
+        yAxis: {
+          name: this.$options.i18n.yAxisTitle,
+          type: 'value',
+          axisLabel: {
+            formatter: (value) => value,
+          },
+        },
+      };
+    },
+    compiledChartOptions() {
+      return this.chart ? this.chart.getOption() : null;
+    },
+    legendSeriesInfo() {
+      if (!this.compiledChartOptions) return [];
+
+      const { series } = this.compiledChartOptions;
+      const seriesInfo = series.map(({ name, lineStyle: { color, type } }) => ({
+        name,
+        color,
+        type,
+      }));
+
+      const nonNullSeriesInfo = seriesInfo.filter(({ name }) => this.isNonNullSeriesData(name));
+      const [nullSeriesItem] = seriesInfo.filter(({ name }) => !this.isNonNullSeriesData(name));
+
+      return [...nonNullSeriesInfo, nullSeriesItem];
+    },
+  },
+  beforeDestroy() {
+    if (this.chart) {
+      this.chart.off('mouseover', this.onChartDataSeriesMouseOver);
+      this.chart.off('mouseout', this.onChartDataSeriesMouseOut);
+    }
+  },
+  methods: {
+    isNonNullSeriesData(seriesName) {
+      return seriesName !== DURATION_OVERVIEW_CHART_NO_DATA;
+    },
+    formatTooltipText({ seriesData }) {
+      const [dateTime] = seriesData[0].data;
+      this.tooltipTitle = dateFormat(
+        dateTime,
+        DURATION_OVERVIEW_CHART_X_AXIS_TOOLTIP_TITLE_DATE_FORMAT,
+      );
+
+      const nonNullSeries = seriesData.filter(({ seriesName }) =>
+        this.isNonNullSeriesData(seriesName),
+      );
+
+      this.tooltipContent = nonNullSeries.map(({ seriesName, color, seriesId, dataIndex }, idx) => {
+        const data = this.durationChartPlottableData[idx].data[dataIndex];
+        const [, metric] = data;
+
+        return {
+          seriesId,
+          label: seriesName,
+          value: isNumeric(metric)
+            ? n__('%d day', '%d days', formatDurationOverviewTooltipMetric(metric))
+            : this.$options.i18n.noData,
+          color,
+        };
+      });
+    },
+    onChartCreated(chart) {
+      this.chart = chart;
+
+      this.chart.on('mouseover', 'series', this.onChartDataSeriesMouseOver);
+      this.chart.on('mouseout', 'series', this.onChartDataSeriesMouseOut);
+    },
+    onChartDataSeriesMouseOver({ seriesId }) {
+      this.activeDataSeries = seriesId;
+    },
+    onChartDataSeriesMouseOut() {
+      this.activeDataSeries = null;
+    },
+  },
+  i18n: {
+    title: DURATION_TOTAL_TIME_LABEL,
+    tooltipText: DURATION_TOTAL_TIME_DESCRIPTION,
+    yAxisTitle: DURATION_CHART_Y_AXIS_TITLE,
+    noData: DURATION_OVERVIEW_CHART_NO_DATA,
+  },
+};
+</script>
+
+<template>
+  <chart-skeleton-loader v-if="isLoading" size="md" class="gl-my-4 gl-py-4" />
+  <div
+    v-else
+    class="gl-display-flex gl-flex-direction-column"
+    data-testid="vsa-duration-overview-chart"
+  >
+    <h4 class="gl-mt-0">
+      {{ $options.i18n.title }}&nbsp;<gl-icon
+        v-gl-tooltip.hover
+        name="information-o"
+        :title="$options.i18n.tooltipText"
+      />
+    </h4>
+    <gl-area-chart
+      v-if="hasData"
+      :option="chartOptions"
+      :data="chartData"
+      :include-legend-avg-max="false"
+      :format-tooltip-text="formatTooltipText"
+      :legend-series-info="legendSeriesInfo"
+      @created="onChartCreated"
+    >
+      <template #tooltip-title>
+        <div>{{ tooltipTitle }}</div>
+      </template>
+
+      <template #tooltip-content>
+        <div
+          v-for="metric in tooltipContent"
+          :key="metric.seriesId"
+          class="gl-display-flex gl-justify-content-space-between gl-line-height-24 gl-min-w-20"
+          :class="{ 'gl-font-weight-bold': activeDataSeries === metric.seriesId }"
+        >
+          <gl-chart-series-label class="gl-mr-7" :color="metric.color">
+            {{ metric.label }}
+          </gl-chart-series-label>
+          <div>{{ metric.value }}</div>
+        </div>
+      </template>
+    </gl-area-chart>
+    <gl-alert v-else variant="info" :dismissible="false" class="gl-mt-3">
+      {{ error }}
+    </gl-alert>
+  </div>
+</template>
diff --git a/ee/app/assets/javascripts/analytics/cycle_analytics/constants.js b/ee/app/assets/javascripts/analytics/cycle_analytics/constants.js
index ec6f61b458da7d3f65df659d0d53fa03f99d6b15..2a172791d43929967281d71ec22a53a8e8fc75ab 100644
--- a/ee/app/assets/javascripts/analytics/cycle_analytics/constants.js
+++ b/ee/app/assets/javascripts/analytics/cycle_analytics/constants.js
@@ -40,6 +40,9 @@ export const METRICS_REQUESTS = [
   },
 ];
 
+export const DURATION_OVERVIEW_CHART_X_AXIS_DATE_FORMAT = 'd mmm';
+export const DURATION_OVERVIEW_CHART_X_AXIS_TOOLTIP_TITLE_DATE_FORMAT = 'd mmm yyyy';
+export const DURATION_OVERVIEW_CHART_NO_DATA = s__('CycleAnalytics|No data');
 export const DURATION_CHART_X_AXIS_TITLE = s__('CycleAnalytics|Date');
 export const DURATION_CHART_Y_AXIS_TITLE = s__('CycleAnalytics|Average time to completion (days)');
 export const DURATION_CHART_Y_AXIS_TOOLTIP_TITLE = s__('CycleAnalytics|Average time to completion');
diff --git a/ee/app/assets/javascripts/analytics/cycle_analytics/store/modules/duration_chart/actions.js b/ee/app/assets/javascripts/analytics/cycle_analytics/store/modules/duration_chart/actions.js
index 75810f60e5f2c9ea47be931dd05bd9c24a083d5a..24e179468952163a9b0fb4494ec17c7bc7b708f2 100644
--- a/ee/app/assets/javascripts/analytics/cycle_analytics/store/modules/duration_chart/actions.js
+++ b/ee/app/assets/javascripts/analytics/cycle_analytics/store/modules/duration_chart/actions.js
@@ -25,7 +25,7 @@ export const fetchDurationData = ({ dispatch, commit, rootGetters }) => {
   } = rootGetters;
   return Promise.all(
     activeStages.map((stage) => {
-      const { id } = stage;
+      const { id, name } = stage;
 
       return getDurationChart({
         namespacePath,
@@ -34,7 +34,7 @@ export const fetchDurationData = ({ dispatch, commit, rootGetters }) => {
         params: cycleAnalyticsRequestParams,
       })
         .then(checkForDataError)
-        .then(({ data }) => ({ id, selected: true, data }));
+        .then(({ data }) => ({ id, name, selected: true, data }));
     }),
   )
     .then((data) => commit(types.RECEIVE_DURATION_DATA_SUCCESS, data))
diff --git a/ee/app/assets/javascripts/analytics/cycle_analytics/store/modules/duration_chart/getters.js b/ee/app/assets/javascripts/analytics/cycle_analytics/store/modules/duration_chart/getters.js
index a4d4ae21c556f8433a58ced36ee7efcdd16cfb1d..efecc6e87a9e78ac9430f8f42a87bdf6b5ea1f14 100644
--- a/ee/app/assets/javascripts/analytics/cycle_analytics/store/modules/duration_chart/getters.js
+++ b/ee/app/assets/javascripts/analytics/cycle_analytics/store/modules/duration_chart/getters.js
@@ -1,4 +1,4 @@
-import { getDurationChartData } from '../../../utils';
+import { getDurationChartData, getDurationOverviewChartData } from '../../../utils';
 
 export const hasPlottableData = ({ durationData = [] }) =>
   durationData.some(({ data }) => data.length);
@@ -15,11 +15,7 @@ export const durationChartPlottableData = (state, _, rootState, rootGetters) =>
     return [];
   }
 
-  const plottableData = getDurationChartData(
-    selectedStagesDurationData,
-    createdAfter,
-    createdBefore,
-  );
-
-  return plottableData;
+  return isOverviewStageSelected
+    ? getDurationOverviewChartData(selectedStagesDurationData)
+    : getDurationChartData(selectedStagesDurationData, createdAfter, createdBefore);
 };
diff --git a/ee/app/assets/javascripts/analytics/cycle_analytics/utils.js b/ee/app/assets/javascripts/analytics/cycle_analytics/utils.js
index f18769836237cbb8c93bd4f3beecb93584268b22..ab06339a005fee22496f9ac88d20498e19c2e7c2 100644
--- a/ee/app/assets/javascripts/analytics/cycle_analytics/utils.js
+++ b/ee/app/assets/javascripts/analytics/cycle_analytics/utils.js
@@ -219,6 +219,169 @@ export const getDurationChartData = (data, startDate, endDate) => {
   return eventData;
 };
 
+/**
+ * Takes the duration data of a stage and references a Map of grouped data by date to determine whether to render a null series if a day does not have any data across all stages, or flatten the stage line if a stage does not have data on a specific day but other stages do.
+ *
+ * For example, given the following duration data:
+ * [
+ *  { average_duration_in_seconds: null, date: '2023-04-01'},
+ *  { average_duration_in_seconds: 259200, date: '2023-04-02'},
+ *  { average_duration_in_seconds: null, date: '2023-04-03'},
+ * ]
+ *
+ * And the following Map:
+ * Map({
+ *  '2023-04-01' => [null, null, null],
+ *  '2023-04-02' => [86400, 259200, null],
+ *  '2023-04-03' => [432000, 172800, null],
+ * })
+ *
+ * It will return the following array:
+ * [
+ *  ['2023-04-01', null],
+ *  ['2023-04-02', 3],
+ *  ['2023-04-03', 0],
+ * ]
+ *
+ * @param {Array} data - Array of objects with average duration in seconds and date
+ * @param {Map} groupedDataByDay - a map of dates from all stages with an array of duration values
+ * @returns {Array} - Array of arrays with dates and average duration in seconds converted to days
+ */
+export const formatDurationOverviewChartData = (data, groupedDataByDay) => {
+  return data.map(({ average_duration_in_seconds: value, date }) => {
+    const currentISODate = dateFormat(new Date(date), dateFormats.isoDate);
+
+    const valuesByDay = groupedDataByDay.has(currentISODate)
+      ? groupedDataByDay.get(currentISODate)
+      : [];
+
+    if (!valuesByDay.length) return [currentISODate, null];
+
+    const isNonNullSeries = valuesByDay.some((durationData) => isNumeric(durationData));
+
+    if (!isNumeric(value)) {
+      return [currentISODate, isNonNullSeries ? 0 : null];
+    }
+
+    return [currentISODate, secondsToDays(value)];
+  });
+};
+
+/**
+ * Takes the duration data for selected stages, groups them by day and either flattens the stage line on a specific day depending on the data across stages on that day, renders a null series or returns value to be plotted.
+ *
+ * The received data is expected to be the following format; One top level object in the array per stage,
+ * each potentially having multiple data entries.
+ * [
+ *   {
+ *    id: 'issue',
+ *    name: 'Issue',
+ *    selected: true,
+ *    data: [
+ *      {
+ *        'average_duration_in_seconds': 1234,
+ *        'date': '2019-09-02T18:25:43.511Z'
+ *      },
+ *      ...
+ *    ]
+ *   },
+ *   ...
+ * ]
+ *
+ * The data is then computed and transformed into a format that can be passed to the chart:
+ * [
+ *  {
+ *    name: 'Issue',
+ *    data: [
+ *      ['2019-09-02', 7],
+ *      ['2019-09-03', 10],
+ *      ['2019-09-04', 8],
+ *    ]
+ *  }
+ *  ...
+ * ]
+ *
+ * In the data above, each array i in the data array represents a point in the area chart with the following data:
+ * i[0] = date, displayed on x axis
+ * i[1] = metric, displayed on y axis
+ *
+ * @param {Array} data - The duration data for selected stages
+ * @returns {Array} An array of stage objects with their names and arrays containing their duration data
+ */
+export const getDurationOverviewChartData = (data) => {
+  const groupedDurationsByDay = groupDurationsByDay(flattenDurationChartData(data));
+
+  return data.map(({ name, data: chartData }) => ({
+    name,
+    data: formatDurationOverviewChartData(chartData, groupedDurationsByDay),
+  }));
+};
+
+/**
+ * Takes the duration data for selected stages and progressively sums up the date values across stages in order to display a stacked area chart.
+ *
+ * For example, given the following duration data:
+ * [
+ *   {
+ *    name: 'Issue',
+ *    data: [
+ *      ['2023-04-05', 10],
+ *      ['2023-04-06', 20],
+ *      ...
+ *    ]
+ *   },
+ *   {
+ *    name: 'Plan',
+ *    data: [
+ *      ['2023-04-05', 20],
+ *      ['2023-04-06', 30],
+ *      ...
+ *    ]
+ *   },
+ *   ...
+ * ]
+ *
+ * It will return the stages with summed duration data:
+ * [
+ *   {
+ *    name: 'Issue',
+ *    data: [
+ *      ['2023-04-05', 10],
+ *      ['2023-04-06', 20],
+ *      ...
+ *    ]
+ *   },
+ *   {
+ *    name: 'Plan',
+ *    data: [
+ *      ['2023-04-05', 30],
+ *      ['2023-04-06', 50],
+ *      ...
+ *    ]
+ *   },
+ *   ...
+ * ]
+ *
+ * @param {Array} data - The duration data for selected stages
+ * @returns {Array} An array of stage objects with metadata and arrays containing summed duration data
+ */
+export const progressiveSummation = (data) => {
+  const tally = new Map();
+
+  return data.map(({ data: stageData, ...stageDetails }) => ({
+    ...stageDetails,
+    data: stageData.map(([date, value]) => {
+      if (value === null) return [date, null];
+
+      const nextValue = tally.has(date) ? tally.get(date) + value : value;
+
+      tally.set(date, nextValue);
+
+      return [date, nextValue];
+    }),
+  }));
+};
+
 export const orderByDate = (a, b, dateFmt = (datetime) => new Date(datetime).getTime()) =>
   dateFmt(a) - dateFmt(b);
 
diff --git a/ee/app/assets/javascripts/analytics/dashboards/utils.js b/ee/app/assets/javascripts/analytics/dashboards/utils.js
index a3389ace6e21100a1848ec1f0b1a6a5dc25415ce..6711620afc7052fb5fb04437414c1e56d11f0fbc 100644
--- a/ee/app/assets/javascripts/analytics/dashboards/utils.js
+++ b/ee/app/assets/javascripts/analytics/dashboards/utils.js
@@ -30,7 +30,7 @@ const trimZeros = (value) =>
  * @param {Number} value - the metric value
  * @returns {Number} The number of fractional digits to render
  */
-const fractionDigits = (value) => {
+export const fractionDigits = (value) => {
   const absVal = Math.abs(value);
   if (absVal === 0) {
     return 1;
diff --git a/ee/app/assets/javascripts/analytics/shared/utils.js b/ee/app/assets/javascripts/analytics/shared/utils.js
index 649611e53b388b811b77ec2042903afaec97889a..335249d68a04f9054f7b157dd51644d307498705 100644
--- a/ee/app/assets/javascripts/analytics/shared/utils.js
+++ b/ee/app/assets/javascripts/analytics/shared/utils.js
@@ -4,6 +4,7 @@ import { extractVSAFeaturesFromGON } from '~/analytics/shared/utils';
 import dateFormat from '~/lib/dateformat';
 import { convertObjectPropsToCamelCase, parseBoolean } from '~/lib/utils/common_utils';
 import { capitalizeFirstCharacter } from '~/lib/utils/text_utility';
+import { fractionDigits } from '../dashboards/utils';
 import { DEFAULT_NULL_SERIES_OPTIONS, DEFAULT_SERIES_DATA_OPTIONS } from './constants';
 
 export const formattedDate = (d) => dateFormat(d, dateFormats.defaultDate);
@@ -290,3 +291,14 @@ export const pairDataAndLabels = ({ datasetNames, datasets = [], axisLabels }) =
     data: zip(axisLabels, dataset.data),
   })),
 ];
+
+/**
+ * Takes average duration in days of a stage on a specific date and returns it with the correct amount digits after the decimal point
+ * @param {Number} metric - Average duration in days of a stage on a specific date
+ * @returns {number} Formatted metric with correct amount of digits after decimal point
+ */
+export const formatDurationOverviewTooltipMetric = (metric) => {
+  const decimalPlaces = fractionDigits(metric);
+
+  return Number(metric.toFixed(decimalPlaces));
+};
diff --git a/ee/spec/features/groups/analytics/cycle_analytics/filters_and_data_spec.rb b/ee/spec/features/groups/analytics/cycle_analytics/filters_and_data_spec.rb
index 295ab51164f42ec94e4e3b5bfd616f634a412ac6..23316ac32ef0587aa0b35ce8176266270ccfcc30 100644
--- a/ee/spec/features/groups/analytics/cycle_analytics/filters_and_data_spec.rb
+++ b/ee/spec/features/groups/analytics/cycle_analytics/filters_and_data_spec.rb
@@ -395,6 +395,7 @@ def vsa_stages(selected_group)
         expect(stage_name).to include(stage[:time])
 
         expect(page).to have_selector('[data-testid="vsa-duration-chart"]')
+        expect(page).not_to have_selector('[data-testid="vsa-duration-overview-chart"]')
       end
     end
 
@@ -405,10 +406,16 @@ def vsa_stages(selected_group)
       expect(page).to have_selector('[data-testid="vsa-stage-table"]')
     end
 
+    it 'displays the duration overview chart on the overview stage' do
+      expect(page).to have_selector('[data-testid="vsa-duration-overview-chart"]')
+
+      expect(page).not_to have_selector('[data-testid="vsa-duration-chart"]')
+    end
+
     it 'will have data available' do
-      duration_chart_content = page.find('[data-testid="vsa-duration-chart"]')
-      expect(duration_chart_content).not_to have_text(_("There is no data available. Please change your selection."))
-      expect(duration_chart_content).to have_text(s_('CycleAnalytics|Average time to completion (days)'))
+      duration_overview_chart = page.find('[data-testid="vsa-duration-overview-chart"]')
+      expect(duration_overview_chart).not_to have_text(_("There is no data available. Please change your selection."))
+      expect(duration_overview_chart).to have_text(s_('CycleAnalytics|Average time to completion (days)'))
 
       tasks_by_type_chart_content = page.find('.js-tasks-by-type-chart')
       expect(tasks_by_type_chart_content).not_to have_text(_("There is no data available. Please change your selection."))
@@ -422,9 +429,9 @@ def vsa_stages(selected_group)
       end
 
       it 'will filter the data' do
-        duration_chart_content = page.find('[data-testid="vsa-duration-chart"]')
-        expect(duration_chart_content).not_to have_text(s_('CycleAnalytics|Average time to completion (days)'))
-        expect(duration_chart_content).to have_text(s_("CycleAnalytics|There is no data for 'Total time' available. Adjust the current filters."))
+        duration_overview_chart = page.find('[data-testid="vsa-duration-overview-chart"]')
+        expect(duration_overview_chart).not_to have_text(s_('CycleAnalytics|Average time to completion (days)'))
+        expect(duration_overview_chart).to have_text(s_("CycleAnalytics|There is no data for 'Total time' available. Adjust the current filters."))
 
         tasks_by_type_chart_content = page.find('.js-tasks-by-type-chart')
         expect(tasks_by_type_chart_content).to have_text(_("There is no data available. Please change your selection."))
diff --git a/ee/spec/features/projects/analytics/cycle_analytics_spec.rb b/ee/spec/features/projects/analytics/cycle_analytics_spec.rb
index 59750f2c77afcd790f618967ab494de5189b81af..2638a6023888b9a14a207f18e2e156408f9d2af2 100644
--- a/ee/spec/features/projects/analytics/cycle_analytics_spec.rb
+++ b/ee/spec/features/projects/analytics/cycle_analytics_spec.rb
@@ -12,7 +12,7 @@
 
   let(:empty_state_selector) { '[data-testid="vsa-empty-state"]' }
   let(:value_stream_selector) { '[data-testid="dropdown-value-streams"]' }
-  let(:duration_chart_selector) { '[data-testid="vsa-duration-chart"]' }
+  let(:duration_chart_selector) { '[data-testid="vsa-duration-overview-chart"]' }
   let(:metrics_groups_selector) { '[data-testid="vsa-metrics-group"]' }
   let(:metrics_selector) { '[data-testid="vsa-metrics"]' }
 
diff --git a/ee/spec/frontend/analytics/cycle_analytics/components/base_spec.js b/ee/spec/frontend/analytics/cycle_analytics/components/base_spec.js
index a6f14dd8f79b00df2b99df41f09b101cc9a35b09..90c2c7c3931b6bd5b9aa7ec6bc1dcfea7bfbfbbb 100644
--- a/ee/spec/frontend/analytics/cycle_analytics/components/base_spec.js
+++ b/ee/spec/frontend/analytics/cycle_analytics/components/base_spec.js
@@ -6,6 +6,7 @@ import Vue, { nextTick } from 'vue';
 import Vuex from 'vuex';
 import Component from 'ee/analytics/cycle_analytics/components/base.vue';
 import DurationChart from 'ee/analytics/cycle_analytics/components/duration_chart.vue';
+import DurationOverviewChart from 'ee/analytics/cycle_analytics/components/duration_overview_chart.vue';
 import TypeOfWorkCharts from 'ee/analytics/cycle_analytics/components/type_of_work_charts.vue';
 import ValueStreamSelect from 'ee/analytics/cycle_analytics/components/value_stream_select.vue';
 import ValueStreamAggregationStatus from 'ee/analytics/cycle_analytics/components/value_stream_aggregation_status.vue';
@@ -195,6 +196,10 @@ describe('EE Value Stream Analytics component', () => {
     expect(wrapper.findComponent(DurationChart).exists()).toBe(flag);
   };
 
+  const displaysDurationOverviewChart = (flag) => {
+    expect(wrapper.findComponent(DurationOverviewChart).exists()).toBe(flag);
+  };
+
   const displaysTypeOfWork = (flag) => {
     expect(wrapper.findComponent(TypeOfWorkCharts).exists()).toBe(flag);
   };
@@ -246,6 +251,10 @@ describe('EE Value Stream Analytics component', () => {
       displaysDurationChart(false);
     });
 
+    it('does not display the duration overview chart', () => {
+      displaysDurationOverviewChart(false);
+    });
+
     it('does not display the path navigation', () => {
       displaysPathNavigation(false);
     });
@@ -290,6 +299,10 @@ describe('EE Value Stream Analytics component', () => {
       displaysDurationChart(false);
     });
 
+    it('does not display the duration overview chart', () => {
+      displaysDurationOverviewChart(false);
+    });
+
     it('does not display the path navigation', () => {
       displaysPathNavigation(false);
     });
@@ -330,8 +343,12 @@ describe('EE Value Stream Analytics component', () => {
       displaysTypeOfWork(true);
     });
 
-    it('displays the duration chart', () => {
-      displaysDurationChart(true);
+    it('displays the duration overview chart', () => {
+      displaysDurationOverviewChart(true);
+    });
+
+    it('does not display the duration chart', () => {
+      displaysDurationChart(false);
     });
 
     it('hides the stage table', () => {
@@ -369,6 +386,10 @@ describe('EE Value Stream Analytics component', () => {
       it('displays the duration chart', () => {
         displaysDurationChart(true);
       });
+
+      it('does not display the duration overview chart', () => {
+        displaysDurationOverviewChart(false);
+      });
     });
   });
 
diff --git a/ee/spec/frontend/analytics/cycle_analytics/components/duration_overview_chart_spec.js b/ee/spec/frontend/analytics/cycle_analytics/components/duration_overview_chart_spec.js
new file mode 100644
index 0000000000000000000000000000000000000000..3dfa06c6bde492b624c100affcf05df7ff3e156a
--- /dev/null
+++ b/ee/spec/frontend/analytics/cycle_analytics/components/duration_overview_chart_spec.js
@@ -0,0 +1,160 @@
+import { shallowMount } from '@vue/test-utils';
+import Vue from 'vue';
+import Vuex from 'vuex';
+import { GlAreaChart } from '@gitlab/ui/dist/charts';
+import { GlIcon } from '@gitlab/ui';
+import DurationOverviewChart from 'ee/analytics/cycle_analytics/components/duration_overview_chart.vue';
+import ChartSkeletonLoader from '~/vue_shared/components/resizable_chart/skeleton_loader.vue';
+import {
+  DURATION_TOTAL_TIME_DESCRIPTION,
+  DURATION_TOTAL_TIME_NO_DATA,
+  DURATION_OVERVIEW_CHART_NO_DATA,
+} from 'ee/analytics/cycle_analytics/constants';
+import {
+  durationOverviewChartPlottableData as durationOverviewData,
+  durationOverviewDataSeries,
+  durationOverviewDataNullSeries,
+  summedDurationOverviewData,
+} from '../mock_data';
+
+Vue.use(Vuex);
+
+const fakeStore = ({ initialGetters, initialState, rootGetters, rootState }) =>
+  new Vuex.Store({
+    state: {
+      ...rootState,
+    },
+    getters: {
+      isOverviewStageSelected: () => true,
+      ...rootGetters,
+    },
+    modules: {
+      durationChart: {
+        namespaced: true,
+        getters: {
+          durationChartPlottableData: () => durationOverviewData,
+          ...initialGetters,
+        },
+        state: {
+          isLoading: false,
+          ...initialState,
+        },
+      },
+    },
+  });
+
+describe('DurationOverviewChart', () => {
+  let wrapper;
+  let mockEChartInstance;
+
+  const findContainer = () => wrapper.find('[data-testid="vsa-duration-overview-chart"]');
+  const findChartDescription = () => wrapper.findComponent(GlIcon);
+  const findDurationOverviewChart = () => wrapper.findComponent(GlAreaChart);
+  const findLoader = () => wrapper.findComponent(ChartSkeletonLoader);
+
+  const emitChartCreated = () =>
+    findDurationOverviewChart().vm.$emit('created', mockEChartInstance);
+
+  const mockChartOptionSeries = [...durationOverviewDataSeries, ...durationOverviewDataNullSeries];
+
+  const createComponent = ({
+    stubs = {},
+    initialState = {},
+    initialGetters = {},
+    rootGetters = {},
+    rootState = {},
+  } = {}) => {
+    mockEChartInstance = {
+      on: jest.fn(),
+      off: jest.fn(),
+      getOption: () => {
+        return {
+          series: mockChartOptionSeries,
+        };
+      },
+    };
+
+    wrapper = shallowMount(DurationOverviewChart, {
+      store: fakeStore({ initialState, initialGetters, rootGetters, rootState }),
+      stubs: {
+        ChartSkeletonLoader: true,
+        ...stubs,
+      },
+    });
+  };
+
+  describe('default', () => {
+    beforeEach(() => {
+      createComponent({});
+      emitChartCreated();
+    });
+
+    it('renders the chart', () => {
+      expect(findDurationOverviewChart().exists()).toBe(true);
+    });
+
+    it('renders the chart description', () => {
+      expect(findChartDescription().attributes('title')).toBe(DURATION_TOTAL_TIME_DESCRIPTION);
+    });
+
+    it('correctly sets the chart options data property', () => {
+      const chartDataProps = findDurationOverviewChart().props('data');
+
+      expect(chartDataProps).toStrictEqual([
+        ...summedDurationOverviewData,
+        ...durationOverviewDataNullSeries,
+      ]);
+    });
+
+    it('correctly sets the chart legend-series-info property', () => {
+      const chartLegendSeriesInfoProps = findDurationOverviewChart().props('legendSeriesInfo');
+
+      const getNonNullSeriesInfo = ({ name }) => name !== DURATION_OVERVIEW_CHART_NO_DATA;
+
+      const legendSeriesInfo = mockChartOptionSeries.map(
+        ({ name, lineStyle: { color, type } }) => ({
+          name,
+          color,
+          type,
+        }),
+      );
+
+      const legendNonNullSeriesInfo = legendSeriesInfo.filter(getNonNullSeriesInfo);
+
+      const [nullSeriesItem] = legendSeriesInfo.filter(
+        (seriesItem) => !getNonNullSeriesInfo(seriesItem),
+      );
+
+      expect(chartLegendSeriesInfoProps).toStrictEqual([
+        ...legendNonNullSeriesInfo,
+        nullSeriesItem,
+      ]);
+
+      expect(chartLegendSeriesInfoProps).toHaveLength(summedDurationOverviewData.length + 1);
+    });
+  });
+
+  describe('with no chart data', () => {
+    beforeEach(() => {
+      createComponent({
+        initialGetters: {
+          durationChartPlottableData: () => [],
+        },
+      });
+    });
+
+    it('renders the no data available message', () => {
+      expect(findContainer().text()).toContain(DURATION_TOTAL_TIME_NO_DATA);
+    });
+  });
+
+  describe('when isLoading=true', () => {
+    beforeEach(() => {
+      createComponent({ initialState: { isLoading: true } });
+    });
+
+    it('renders a loader', () => {
+      expect(findLoader().exists()).toBe(true);
+    });
+  });
+});
diff --git a/ee/spec/frontend/analytics/cycle_analytics/mock_data.js b/ee/spec/frontend/analytics/cycle_analytics/mock_data.js
index 119d17bff7dcd276ce1e7f84c8ef1c42e8bb7fbd..c5f3e71f4e4a809ab6ace0ea2e15c61f4834d9bd 100644
--- a/ee/spec/frontend/analytics/cycle_analytics/mock_data.js
+++ b/ee/spec/frontend/analytics/cycle_analytics/mock_data.js
@@ -1,4 +1,4 @@
-import { dataVizBlue500 } from '@gitlab/ui/scss_to_js/scss_variables';
+import { dataVizBlue500, dataVizOrange600 } from '@gitlab/ui/scss_to_js/scss_variables';
 import { uniq } from 'lodash';
 import valueStreamAnalyticsStages from 'test_fixtures/analytics/value_stream_analytics/stages.json';
 import valueStreamAnalyticsSummary from 'test_fixtures/analytics/metrics/value_stream_analytics/summary.json';
@@ -24,6 +24,7 @@ import {
   TASKS_BY_TYPE_SUBJECT_ISSUE,
   OVERVIEW_STAGE_CONFIG,
   DURATION_CHART_Y_AXIS_TITLE,
+  DURATION_OVERVIEW_CHART_NO_DATA,
 } from 'ee/analytics/cycle_analytics/constants';
 import * as types from 'ee/analytics/cycle_analytics/store/mutation_types';
 import mutations from 'ee/analytics/cycle_analytics/store/mutations';
@@ -262,6 +263,7 @@ export const taskByTypeFilters = {
 export const transformedDurationData = [
   {
     id: issueStage.id,
+    name: 'Issue',
     selected: true,
     data: [
       {
@@ -276,6 +278,7 @@ export const transformedDurationData = [
   },
   {
     id: planStage.id,
+    name: 'Plan',
     selected: true,
     data: [
       {
@@ -290,6 +293,7 @@ export const transformedDurationData = [
   },
   {
     id: codeStage.id,
+    name: 'Code',
     selected: true,
     data: [
       {
@@ -357,3 +361,68 @@ export const durationDataNullSeries = {
   name: `${DURATION_CHART_Y_AXIS_TITLE} no data series`,
   showSymbol: false,
 };
+
+export const durationOverviewChartPlottableData = [
+  {
+    name: 'Issue',
+    data: [
+      ['2019-01-01', 13],
+      ['2019-01-02', 27],
+    ],
+  },
+  {
+    name: 'Plan',
+    data: [
+      ['2019-01-01', 25],
+      ['2019-01-02', 42],
+    ],
+  },
+];
+
+export const summedDurationOverviewData = [
+  {
+    name: 'Issue',
+    data: [
+      ['2019-01-01', 13],
+      ['2019-01-02', 27],
+    ],
+  },
+  {
+    name: 'Plan',
+    data: [
+      ['2019-01-01', 38],
+      ['2019-01-02', 69],
+    ],
+  },
+];
+
+export const durationOverviewDataSeries = summedDurationOverviewData.map((stageDetails, idx) => {
+  const colors = [dataVizBlue500, dataVizOrange600];
+
+  return {
+    ...stageDetails,
+    lineStyle: {
+      color: colors[idx],
+      type: 'solid',
+    },
+  };
+});
+
+export const durationOverviewDataNullSeries = summedDurationOverviewData.map(() => ({
+  areaStyle: {
+    color: 'none',
+  },
+  data: [
+    ['2019-01-01', null],
+    ['2019-01-02', null],
+  ],
+  itemStyle: {
+    color: '#a4a3a8',
+  },
+  lineStyle: {
+    color: '#a4a3a8',
+    type: 'dashed',
+  },
+  name: DURATION_OVERVIEW_CHART_NO_DATA,
+  showSymbol: false,
+}));
diff --git a/ee/spec/frontend/analytics/cycle_analytics/store/modules/duration_chart/getters_spec.js b/ee/spec/frontend/analytics/cycle_analytics/store/modules/duration_chart/getters_spec.js
index 1d5bf99c19fd5625984edc00c3e9b40728db8396..c579e582af81882a1224fb73e7d18be8ed34ebbb 100644
--- a/ee/spec/frontend/analytics/cycle_analytics/store/modules/duration_chart/getters_spec.js
+++ b/ee/spec/frontend/analytics/cycle_analytics/store/modules/duration_chart/getters_spec.js
@@ -2,7 +2,7 @@ import * as getters from 'ee/analytics/cycle_analytics/store/modules/duration_ch
 import { createdAfter, createdBefore } from 'jest/analytics/cycle_analytics/mock_data';
 import {
   transformedDurationData,
-  durationChartPlottableData as mockDurationChartPlottableData,
+  durationOverviewChartPlottableData as mockDurationOverviewChartPlottableData,
 } from '../../../mock_data';
 
 const rootState = {
@@ -85,7 +85,7 @@ describe('DurationChart getters', () => {
           rootGetters,
         );
 
-        expect(res).toEqual(expect.arrayContaining(mockDurationChartPlottableData));
+        expect(res).toEqual(expect.arrayContaining(mockDurationOverviewChartPlottableData));
       });
     });
   });
diff --git a/ee/spec/frontend/analytics/cycle_analytics/utils_spec.js b/ee/spec/frontend/analytics/cycle_analytics/utils_spec.js
index a1ccbaa26fc5263f3b6015b7404d88dea1ce6902..628b48271f03b91065b9de82b108adb3c2fe2f71 100644
--- a/ee/spec/frontend/analytics/cycle_analytics/utils_spec.js
+++ b/ee/spec/frontend/analytics/cycle_analytics/utils_spec.js
@@ -8,6 +8,7 @@ import {
   getLabelEventsIdentifiers,
   flattenDurationChartData,
   getDurationChartData,
+  getDurationOverviewChartData,
   transformRawStages,
   getTasksByTypeData,
   flattenTaskByTypeSeries,
@@ -17,6 +18,8 @@ import {
   formatMedianValuesWithOverview,
   generateFilterTextDescription,
   groupDurationsByDay,
+  progressiveSummation,
+  formatDurationOverviewChartData,
 } from 'ee/analytics/cycle_analytics/utils';
 import {
   TASKS_BY_TYPE_SUBJECT_MERGE_REQUEST,
@@ -39,6 +42,8 @@ import {
   transformedDurationData,
   flattenedDurationData,
   durationChartPlottableData,
+  durationOverviewChartPlottableData,
+  summedDurationOverviewData,
   issueStage,
   rawCustomStage,
   rawTasksByTypeData,
@@ -197,6 +202,122 @@ describe('Value Stream Analytics utils', () => {
     });
   });
 
+  describe('formatDurationOverviewChartData', () => {
+    it('formats the duration overview chart data as expected', () => {
+      const durationData = [
+        { average_duration_in_seconds: null, date: '2023-04-01' },
+        { average_duration_in_seconds: 259200, date: '2023-04-02' },
+        { average_duration_in_seconds: null, date: '2023-04-03' },
+      ];
+
+      const groupedDataByDay = new Map();
+
+      groupedDataByDay.set('2023-04-01', [null, null, null]);
+      groupedDataByDay.set('2023-04-02', [86400, 259200, null]);
+      groupedDataByDay.set('2023-04-03', [432000, 172800, null]);
+
+      const formattedDurationOverviewChartData = [
+        ['2023-04-01', null],
+        ['2023-04-02', 3],
+        ['2023-04-03', 0],
+      ];
+
+      expect(formatDurationOverviewChartData(durationData, groupedDataByDay)).toStrictEqual(
+        formattedDurationOverviewChartData,
+      );
+    });
+  });
+
+  describe('getDurationOverviewChartData', () => {
+    const nulledData = [
+      {
+        name: 'Plan',
+        data: [
+          { average_duration_in_seconds: null, date: '2019-01-01T00:00:00.000Z' },
+          { average_duration_in_seconds: null, date: '2019-01-02T00:00:00.000Z' },
+        ],
+      },
+    ];
+
+    const zerodData = [
+      {
+        name: 'Issue',
+        data: [
+          { average_duration_in_seconds: 0, date: '2019-01-01T00:00:00.000Z' },
+          { average_duration_in_seconds: 0, date: '2019-01-02T00:00:00.000Z' },
+        ],
+      },
+    ];
+
+    const nullAndPositiveData = [
+      {
+        name: 'Issue',
+        data: [
+          { average_duration_in_seconds: null, date: '2019-01-01T00:00:00.000Z' },
+          { average_duration_in_seconds: 259200, date: '2019-01-02T00:00:00.000Z' },
+        ],
+      },
+      ...nulledData,
+    ];
+
+    const nulledPlottableData = [
+      {
+        name: 'Plan',
+        data: [
+          ['2019-01-01', null],
+          ['2019-01-02', null],
+        ],
+      },
+    ];
+
+    const zeroedPlottableData = [
+      {
+        name: 'Issue',
+        data: [
+          ['2019-01-01', 0],
+          ['2019-01-02', 0],
+        ],
+      },
+    ];
+
+    const nullAndPositivePlottableData = [
+      {
+        name: 'Issue',
+        data: [
+          ['2019-01-01', null],
+          ['2019-01-02', 3],
+        ],
+      },
+      {
+        name: 'Plan',
+        data: [
+          ['2019-01-01', null],
+          ['2019-01-02', 0],
+        ],
+      },
+    ];
+
+    it.each`
+      description                                    | rawData                    | result
+      ${'with positive average durations'}           | ${transformedDurationData} | ${durationOverviewChartPlottableData}
+      ${'with zeroes'}                               | ${zerodData}               | ${zeroedPlottableData}
+      ${'with nulls'}                                | ${nulledData}              | ${nulledPlottableData}
+      ${'with positive average durations and nulls'} | ${nullAndPositiveData}     | ${nullAndPositivePlottableData}
+    `('computes the plottable data for each stage $description', ({ rawData, result }) => {
+      const plottableDurationOverviewData = getDurationOverviewChartData(rawData);
+
+      expect(plottableDurationOverviewData).toEqual(expect.arrayContaining(result));
+    });
+  });
+
+  describe('progressiveSummation', () => {
+    it('progressively sums up the duration data as expected', () => {
+      expect(progressiveSummation(durationOverviewChartPlottableData)).toStrictEqual(
+        summedDurationOverviewData,
+      );
+    });
+  });
+
   describe('transformRawStages', () => {
     it('retains all the stage properties', () => {
       const transformed = transformRawStages([rawCustomStage]);
diff --git a/ee/spec/frontend/analytics/shared/utils_spec.js b/ee/spec/frontend/analytics/shared/utils_spec.js
index 5b0e33f592e4ba15e9484bc21222ceee38ddc752..4266c49bbfbf177ea74487b11055dfbb224fb453 100644
--- a/ee/spec/frontend/analytics/shared/utils_spec.js
+++ b/ee/spec/frontend/analytics/shared/utils_spec.js
@@ -5,6 +5,7 @@ import {
   buildNullSeries,
   toLocalDate,
   pairDataAndLabels,
+  formatDurationOverviewTooltipMetric,
 } from 'ee/analytics/shared/utils';
 
 const rawValueStream = `{
@@ -402,3 +403,17 @@ describe('pairDataAndLabels', () => {
     });
   });
 });
+
+describe('formatDurationOverviewTooltipMetric', () => {
+  it.each`
+    num          | expected
+    ${2.2453}    | ${2.2}
+    ${0.98445}   | ${0.98}
+    ${0.08345}   | ${0.083}
+    ${0.0094423} | ${0.0094}
+  `('should display $num as $expected', ({ num, expected }) => {
+    const formattedNum = formatDurationOverviewTooltipMetric(num);
+
+    expect(formattedNum).toBe(expected);
+  });
+});
diff --git a/locale/gitlab.pot b/locale/gitlab.pot
index 3b0e03eba7da8399f9a16b3944502218c40a7385..f87b9a25f71fd64160a06e4ba346633b5dcf3687 100644
--- a/locale/gitlab.pot
+++ b/locale/gitlab.pot
@@ -13007,6 +13007,9 @@ msgstr ""
 msgid "CycleAnalytics|New value stream…"
 msgstr ""
 
+msgid "CycleAnalytics|No data"
+msgstr ""
+
 msgid "CycleAnalytics|Number of tasks"
 msgstr ""