diff --git a/ee/app/assets/javascripts/roadmap/components/epics_list_section.vue b/ee/app/assets/javascripts/roadmap/components/epics_list_section.vue
index 3b8997df6bbe11e6a99b7e5beba4e187e88101e8..5df15ef791ffdcb9ea84adf13c01760fe5c96e84 100644
--- a/ee/app/assets/javascripts/roadmap/components/epics_list_section.vue
+++ b/ee/app/assets/javascripts/roadmap/components/epics_list_section.vue
@@ -70,9 +70,16 @@ export default {
     },
     sectionContainerStyles() {
       return {
+        height: `calc(100% - ${60 + this.milestonesHeight}px)`,
         width: `${EPIC_DETAILS_CELL_WIDTH + TIMELINE_CELL_MIN_WIDTH * this.timeframe.length}px`,
       };
     },
+    footerMessageHeight() {
+      return document.querySelector('.footer-message')?.getBoundingClientRect().height || 0;
+    },
+    milestonesHeight() {
+      return document.querySelector('.milestones-list-section')?.clientHeight || 0;
+    },
     epicsWithAssociatedParents() {
       return this.epics.filter((epic) => {
         if (epic.hasParent) {
@@ -93,6 +100,9 @@ export default {
       // Return epics with correct parent associations.
       return this.epicsWithAssociatedParents;
     },
+    scrollBarHeight() {
+      return this.$parent.$el.getBoundingClientRect().height - this.$parent.$el.clientHeight + 1;
+    },
   },
   mounted() {
     eventHub.$on('epicsListScrolled', this.handleEpicsListScroll);
@@ -141,7 +151,7 @@ export default {
         return {
           height: this.isScopedRoadmap
             ? `calc(${this.$root.$el.clientHeight}px - ${offsetTop}px)`
-            : `calc(100vh - ${top}px)`,
+            : `calc(100vh - ${top + this.scrollBarHeight + this.footerMessageHeight}px)`,
         };
       }
       return {};
diff --git a/ee/app/assets/javascripts/roadmap/components/milestones_list_section.vue b/ee/app/assets/javascripts/roadmap/components/milestones_list_section.vue
index 842652158d8217e77fd5b772def53a830177df3e..39ebb03337771cdd284e312996c8547fbce9da06 100644
--- a/ee/app/assets/javascripts/roadmap/components/milestones_list_section.vue
+++ b/ee/app/assets/javascripts/roadmap/components/milestones_list_section.vue
@@ -85,6 +85,7 @@ export default {
   mounted() {
     eventHub.$on('epicsListScrolled', this.handleEpicsListScroll);
     this.initMounted();
+    this.$emit('milestonesMounted');
   },
   beforeDestroy() {
     eventHub.$off('epicsListScrolled', this.handleEpicsListScroll);
diff --git a/ee/app/assets/javascripts/roadmap/components/roadmap_shell.vue b/ee/app/assets/javascripts/roadmap/components/roadmap_shell.vue
index c626181527f8ec72ca6b7b0831664b16e7885940..ecab5c10d8a9975ac1af7d29af77e98083c4da0e 100644
--- a/ee/app/assets/javascripts/roadmap/components/roadmap_shell.vue
+++ b/ee/app/assets/javascripts/roadmap/components/roadmap_shell.vue
@@ -40,6 +40,7 @@ export default {
   data() {
     return {
       containerStyles: {},
+      canCalculateEpicsListHeight: false,
     };
   },
   computed: {
@@ -60,6 +61,9 @@ export default {
           return this.milestones;
       }
     },
+    footerMessageHeight() {
+      return document.querySelector('.footer-message')?.getBoundingClientRect().height || 0;
+    },
   },
   mounted() {
     if (this.isShowingMilestones) {
@@ -79,9 +83,12 @@ export default {
     getContainerStyles() {
       const { top } = this.$el.getBoundingClientRect();
       return {
-        height: this.isScopedRoadmap ? '100%' : `calc(100vh - ${top}px)`,
+        height: this.isScopedRoadmap ? '100%' : `calc(100vh - ${top + this.footerMessageHeight}px)`,
       };
     },
+    toggleCanCalculateEpicsListHeight() {
+      this.canCalculateEpicsListHeight = true;
+    },
   },
 };
 </script>
@@ -105,8 +112,10 @@ export default {
       :milestones="milestonesToShow"
       :timeframe="timeframe"
       :current-group-id="currentGroupId"
+      @milestonesMounted="toggleCanCalculateEpicsListHeight"
     />
     <epics-list-section
+      :key="canCalculateEpicsListHeight"
       :preset-type="presetType"
       :epics="epics"
       :timeframe="timeframe"
diff --git a/ee/app/assets/stylesheets/page_bundles/roadmap.scss b/ee/app/assets/stylesheets/page_bundles/roadmap.scss
index 5a807dfae25d15ab01478ac1fe9fb782b8274485..a2a6e3a9128e7e7b3364a2beb8bedef4a6958cba 100644
--- a/ee/app/assets/stylesheets/page_bundles/roadmap.scss
+++ b/ee/app/assets/stylesheets/page_bundles/roadmap.scss
@@ -200,10 +200,6 @@ html.group-epics-roadmap-html {
   }
 }
 
-.epics-list-section {
-  height: calc(100% - 60px);
-}
-
 .epics-list-item {
   &:hover {
     .epic-details-cell,
diff --git a/ee/spec/frontend/roadmap/components/epics_list_section_spec.js b/ee/spec/frontend/roadmap/components/epics_list_section_spec.js
index c5f6404e2ca3d6dbe65faecce092c356240bece2..3ec0692095b1a68b96c9730da1c0ef81d1e56a10 100644
--- a/ee/spec/frontend/roadmap/components/epics_list_section_spec.js
+++ b/ee/spec/frontend/roadmap/components/epics_list_section_spec.js
@@ -95,7 +95,7 @@ describe('EpicsListSectionComponent', () => {
 
     describe('sectionContainerStyles', () => {
       it('returns style string for container element based on sectionShellWidth', () => {
-        expect(wrapper.attributes('style')).toBe(
+        expect(wrapper.attributes('style')).toContain(
           `width: ${
             EPIC_DETAILS_CELL_WIDTH + TIMELINE_CELL_MIN_WIDTH * mockTimeframeMonths.length
           }px;`,
@@ -165,7 +165,7 @@ describe('EpicsListSectionComponent', () => {
       });
 
       it('sets style attribute containing `height` on empty row', () => {
-        expect(findEmptyRowEl().attributes('style')).toBe('height: calc(100vh - 0px);');
+        expect(findEmptyRowEl().attributes('style')).toBe('height: calc(100vh - 1px);');
       });
     });
 
@@ -177,7 +177,7 @@ describe('EpicsListSectionComponent', () => {
       });
 
       it('sets style attribute with `height` on empty row when there epics available to render', () => {
-        expect(findEmptyRowEl().attributes('style')).toBe('height: calc(100vh - 0px);');
+        expect(findEmptyRowEl().attributes('style')).toBe('height: calc(100vh - 1px);');
       });
     });