diff --git a/package.json b/package.json
index 84bd703dba4e..d56fe68b2e07 100644
--- a/package.json
+++ b/package.json
@@ -220,7 +220,7 @@
     "deep-freeze-strict": "^1.1.1",
     "deepmerge": "^4.2.2",
     "del": "^5.1.0",
-    "elastic-apm-node": "3.24.0",
+    "elastic-apm-node": "^3.24.0",
     "execa": "^4.0.2",
     "exit-hook": "^2.2.0",
     "expiry-js": "0.1.7",
diff --git a/renovate.json b/renovate.json
index 82005e82299b..362fd9381b61 100644
--- a/renovate.json
+++ b/renovate.json
@@ -54,6 +54,14 @@
       "labels": ["release_note:skip", "Team:Operations", "Team:Core", "backport:skip"],
       "enabled": true
     },
+    {
+      "groupName": "APM",
+      "matchPackageNames": ["elastic-apm-node", "@elastic/apm-rum", "@elastic/apm-rum-react"],
+      "reviewers": ["team:kibana-core"],
+      "matchBaseBranches": ["main"],
+      "labels": ["release_note:skip", "Team:Core", "backport:skip"],
+      "enabled": true
+    },
     {
       "groupName": "babel",
       "matchPackageNames": ["@types/babel__core"],
diff --git a/src/core/public/doc_links/doc_links_service.ts b/src/core/public/doc_links/doc_links_service.ts
index 6b6ceeddee68..f97ec152aa5d 100644
--- a/src/core/public/doc_links/doc_links_service.ts
+++ b/src/core/public/doc_links/doc_links_service.ts
@@ -225,6 +225,7 @@ export class DocLinksService {
           mappingTermVector: `${ELASTICSEARCH_DOCS}term-vector.html`,
           mappingTypesRemoval: `${ELASTICSEARCH_DOCS}removal-of-types.html`,
           migrateIndexAllocationFilters: `${ELASTICSEARCH_DOCS}migrate-index-allocation-filters.html`,
+          migrationApiDeprecation: `${ELASTICSEARCH_DOCS}migration-api-deprecation.html`,
           nodeRoles: `${ELASTICSEARCH_DOCS}modules-node.html#node-roles`,
           releaseHighlights: `${ELASTICSEARCH_DOCS}release-highlights.html`,
           remoteClusters: `${ELASTICSEARCH_DOCS}remote-clusters.html`,
diff --git a/src/plugins/es_ui_shared/__packages_do_not_import__/authorization/components/with_privileges.tsx b/src/plugins/es_ui_shared/__packages_do_not_import__/authorization/components/with_privileges.tsx
index 6485bd7f45e5..16dffdb5d267 100644
--- a/src/plugins/es_ui_shared/__packages_do_not_import__/authorization/components/with_privileges.tsx
+++ b/src/plugins/es_ui_shared/__packages_do_not_import__/authorization/components/with_privileges.tsx
@@ -22,7 +22,7 @@ interface Props {
     isLoading: boolean;
     hasPrivileges: boolean;
     privilegesMissing: MissingPrivileges;
-  }) => JSX.Element;
+  }) => JSX.Element | null;
 }
 
 type Privilege = [string, string];
diff --git a/src/plugins/home/public/application/components/tutorial/instruction_set.js b/src/plugins/home/public/application/components/tutorial/instruction_set.js
index 822c60cdc31b..e434f2483fd1 100644
--- a/src/plugins/home/public/application/components/tutorial/instruction_set.js
+++ b/src/plugins/home/public/application/components/tutorial/instruction_set.js
@@ -37,6 +37,7 @@ class InstructionSetUi extends React.Component {
       return {
         id: variant.id,
         name: getDisplayText(variant.id),
+        initialSelected: variant.initialSelected,
       };
     });
 
@@ -45,7 +46,8 @@ class InstructionSetUi extends React.Component {
     };
 
     if (this.tabs.length > 0) {
-      this.state.selectedTabId = this.tabs[0].id;
+      this.state.selectedTabId =
+        this.tabs.find(({ initialSelected }) => initialSelected)?.id ?? this.tabs[0].id;
     }
   }
 
@@ -298,6 +300,7 @@ const instructionShape = PropTypes.shape({
 const instructionVariantShape = PropTypes.shape({
   id: PropTypes.string.isRequired,
   instructions: PropTypes.arrayOf(instructionShape).isRequired,
+  initialSelected: PropTypes.bool,
 });
 
 const statusCheckConfigShape = PropTypes.shape({
diff --git a/src/plugins/home/server/services/tutorials/lib/tutorial_schema.ts b/src/plugins/home/server/services/tutorials/lib/tutorial_schema.ts
index 114afa644c0c..f05c0af01cb1 100644
--- a/src/plugins/home/server/services/tutorials/lib/tutorial_schema.ts
+++ b/src/plugins/home/server/services/tutorials/lib/tutorial_schema.ts
@@ -63,6 +63,7 @@ export type Instruction = TypeOf<typeof instructionSchema>;
 const instructionVariantSchema = schema.object({
   id: schema.string(),
   instructions: schema.arrayOf(instructionSchema),
+  initialSelected: schema.maybe(schema.boolean()),
 });
 
 export type InstructionVariant = TypeOf<typeof instructionVariantSchema>;
diff --git a/src/plugins/vis_types/timelion/public/helpers/timelion_request_handler.ts b/src/plugins/vis_types/timelion/public/helpers/timelion_request_handler.ts
index e9a076b4dc83..69a818b4ae16 100644
--- a/src/plugins/vis_types/timelion/public/helpers/timelion_request_handler.ts
+++ b/src/plugins/vis_types/timelion/public/helpers/timelion_request_handler.ts
@@ -8,11 +8,12 @@
 
 import { i18n } from '@kbn/i18n';
 import type { KibanaExecutionContext } from 'kibana/public';
+import { DataView } from 'src/plugins/data/common';
 import { KibanaContext, TimeRange, Filter, esQuery, Query } from '../../../../data/public';
 import { TimelionVisDependencies } from '../plugin';
 import { getTimezone } from './get_timezone';
 import { TimelionVisParams } from '../timelion_vis_fn';
-import { getDataSearch } from '../helpers/plugin_services';
+import { getDataSearch, getIndexPatterns } from '../helpers/plugin_services';
 import { VisSeries } from '../../common/vis_data';
 
 interface Stats {
@@ -81,6 +82,14 @@ export function getTimelionRequestHandler({
       );
     }
 
+    let dataView: DataView | undefined;
+    const firstFilterIndex = filters[0]?.meta.index;
+    if (firstFilterIndex) {
+      dataView = await getIndexPatterns()
+        .get(firstFilterIndex)
+        .catch(() => undefined);
+    }
+
     const esQueryConfigs = esQuery.getEsQueryConfig(uiSettings);
 
     // parse the time range client side to make sure it behaves like other charts
@@ -100,7 +109,7 @@ export function getTimelionRequestHandler({
           sheet: [expression],
           extended: {
             es: {
-              filter: esQuery.buildEsQuery(undefined, query, filters, esQueryConfigs),
+              filter: esQuery.buildEsQuery(dataView, query, filters, esQueryConfigs),
             },
           },
           time: {
diff --git a/src/plugins/vis_types/vega/public/vega_request_handler.ts b/src/plugins/vis_types/vega/public/vega_request_handler.ts
index 2ae7169c2f73..78552ea2a170 100644
--- a/src/plugins/vis_types/vega/public/vega_request_handler.ts
+++ b/src/plugins/vis_types/vega/public/vega_request_handler.ts
@@ -6,6 +6,7 @@
  * Side Public License, v 1.
  */
 import type { KibanaExecutionContext } from 'src/core/public';
+import { DataView } from 'src/plugins/data/common';
 import { Filter, esQuery, TimeRange, Query } from '../../../data/public';
 
 import { SearchAPI } from './data_model/search_api';
@@ -18,7 +19,7 @@ import { VegaInspectorAdapters } from './vega_inspector';
 
 interface VegaRequestHandlerParams {
   query: Query;
-  filters: Filter;
+  filters: Filter[];
   timeRange: TimeRange;
   visParams: VisParams;
   searchSessionId?: string;
@@ -46,14 +47,14 @@ export function createVegaRequestHandler(
     searchSessionId,
     executionContext,
   }: VegaRequestHandlerParams) {
-    if (!searchAPI) {
-      const { search, indexPatterns } = getData();
+    const { dataViews, search } = getData();
 
+    if (!searchAPI) {
       searchAPI = new SearchAPI(
         {
           uiSettings,
           search,
-          indexPatterns,
+          indexPatterns: dataViews,
           injectedMetadata: getInjectedMetadata(),
         },
         context.abortSignal,
@@ -65,8 +66,14 @@ export function createVegaRequestHandler(
 
     timeCache.setTimeRange(timeRange);
 
+    let dataView: DataView;
+    const firstFilterIndex = filters[0]?.meta.index;
+    if (firstFilterIndex) {
+      dataView = await dataViews.get(firstFilterIndex).catch(() => undefined);
+    }
+
     const esQueryConfigs = esQuery.getEsQueryConfig(uiSettings);
-    const filtersDsl = esQuery.buildEsQuery(undefined, query, filters, esQueryConfigs);
+    const filtersDsl = esQuery.buildEsQuery(dataView, query, filters, esQueryConfigs);
     const { VegaParser } = await import('./data_model/vega_parser');
     const vp = new VegaParser(visParams.spec, searchAPI, timeCache, filtersDsl, getServiceSettings);
 
diff --git a/test/api_integration/apis/stats/stats.js b/test/api_integration/apis/stats/stats.js
index 61936a73da38..c15fa782ea3a 100644
--- a/test/api_integration/apis/stats/stats.js
+++ b/test/api_integration/apis/stats/stats.js
@@ -58,7 +58,8 @@ export default function ({ getService }) {
       );
     });
 
-    describe('basic', () => {
+    // FLAKY: https://github.com/elastic/kibana/issues/116725
+    describe.skip('basic', () => {
       it('should return the stats without cluster_uuid with no query string params', () => {
         return supertest
           .get('/api/stats')
diff --git a/test/functional/apps/discover/_search_on_page_load.ts b/test/functional/apps/discover/_search_on_page_load.ts
index 2a66e03c3cbb..277d2e72d729 100644
--- a/test/functional/apps/discover/_search_on_page_load.ts
+++ b/test/functional/apps/discover/_search_on_page_load.ts
@@ -76,6 +76,11 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
         expect(await testSubjects.exists('refreshDataButton')).to.be(true);
         await retry.waitFor('number of fetches to be 0', waitForFetches(0));
 
+        /**
+         * We should wait for debounce timeout expired 100 ms,
+         * otherwise click event will be skipped. See getFetch$ implementation.
+         */
+        await PageObjects.common.sleep(100);
         await testSubjects.click('refreshDataButton');
 
         await retry.waitFor('number of fetches to be 1', waitForFetches(1));
diff --git a/x-pack/plugins/apm/public/tutorial/tutorial_fleet_instructions/index.tsx b/x-pack/plugins/apm/public/tutorial/tutorial_fleet_instructions/index.tsx
index a922a10d6d6f..69f3c4bbbc40 100644
--- a/x-pack/plugins/apm/public/tutorial/tutorial_fleet_instructions/index.tsx
+++ b/x-pack/plugins/apm/public/tutorial/tutorial_fleet_instructions/index.tsx
@@ -34,7 +34,7 @@ const CentralizedContainer = styled.div`
   align-items: center;
 `;
 
-type APIResponseType = APIReturnType<'GET /internal/apm/fleet/has_data'>;
+type APIResponseType = APIReturnType<'GET /internal/apm/fleet/migration_check'>;
 
 function TutorialFleetInstructions({ http, basePath, isDarkTheme }: Props) {
   const [data, setData] = useState<APIResponseType | undefined>();
@@ -44,7 +44,7 @@ function TutorialFleetInstructions({ http, basePath, isDarkTheme }: Props) {
     async function fetchData() {
       setIsLoading(true);
       try {
-        const response = await http.get('/internal/apm/fleet/has_data');
+        const response = await http.get('/internal/apm/fleet/migration_check');
         setData(response as APIResponseType);
       } catch (e) {
         setIsLoading(false);
@@ -55,6 +55,22 @@ function TutorialFleetInstructions({ http, basePath, isDarkTheme }: Props) {
     fetchData();
   }, [http]);
 
+  const hasApmIntegrations = !!data?.has_apm_integrations;
+  const cloudApmMigrationEnabled = !!data?.cloud_apm_migration_enabled;
+  const hasCloudAgentPolicy = !!data?.has_cloud_agent_policy;
+  const cloudApmPackagePolicy = data?.cloud_apm_package_policy;
+  const hasCloudApmPackagePolicy = !!cloudApmPackagePolicy;
+  const hasRequiredRole = !!data?.has_required_role;
+  const shouldLinkToMigration =
+    cloudApmMigrationEnabled &&
+    hasCloudAgentPolicy &&
+    !hasCloudApmPackagePolicy &&
+    hasRequiredRole;
+
+  const apmIntegrationHref = shouldLinkToMigration
+    ? `${basePath}/app/apm/settings/schema`
+    : `${basePath}/app/integrations/detail/apm-${SUPPORTED_APM_PACKAGE_VERSION}/overview`;
+
   if (isLoading) {
     return (
       <CentralizedContainer>
@@ -64,9 +80,13 @@ function TutorialFleetInstructions({ http, basePath, isDarkTheme }: Props) {
   }
 
   // When APM integration is enable in Fleet
-  if (data?.hasData) {
+  if (hasApmIntegrations) {
     return (
-      <EuiButton iconType="gear" fill href={`${basePath}/app/fleet#/policies`}>
+      <EuiButton
+        iconType="gear"
+        fill
+        href={`${basePath}/app/integrations/detail/apm-${SUPPORTED_APM_PACKAGE_VERSION}/policies`}
+      >
         {i18n.translate(
           'xpack.apm.tutorial.apmServer.fleet.manageApmIntegration.button',
           {
@@ -99,7 +119,7 @@ function TutorialFleetInstructions({ http, basePath, isDarkTheme }: Props) {
                 <EuiButton
                   iconType="analyzeEvent"
                   color="success"
-                  href={`${basePath}/app/integrations#/detail/apm-${SUPPORTED_APM_PACKAGE_VERSION}/overview`}
+                  href={apmIntegrationHref}
                 >
                   {i18n.translate(
                     'xpack.apm.tutorial.apmServer.fleet.apmIntegration.button',
diff --git a/x-pack/plugins/apm/server/routes/fleet/route.ts b/x-pack/plugins/apm/server/routes/fleet/route.ts
index e9e7f2254bcf..b64d1764c846 100644
--- a/x-pack/plugins/apm/server/routes/fleet/route.ts
+++ b/x-pack/plugins/apm/server/routes/fleet/route.ts
@@ -128,14 +128,16 @@ const getMigrationCheckRoute = createApmServerRoute({
   endpoint: 'GET /internal/apm/fleet/migration_check',
   options: { tags: ['access:apm'] },
   handler: async (resources) => {
-    const { plugins, context, config, request } = resources;
+    const { core, plugins, context, config, request } = resources;
     const cloudApmMigrationEnabled = config.agent.migrations.enabled;
     if (!plugins.fleet || !plugins.security) {
       throw Boom.internal(FLEET_SECURITY_REQUIRED_MESSAGE);
     }
     const savedObjectsClient = context.core.savedObjects.client;
-    const fleetPluginStart = await plugins.fleet.start();
-    const securityPluginStart = await plugins.security.start();
+    const [fleetPluginStart, securityPluginStart] = await Promise.all([
+      plugins.fleet.start(),
+      plugins.security.start(),
+    ]);
     const hasRequiredRole = isSuperuser({ securityPluginStart, request });
     const cloudAgentPolicy = hasRequiredRole
       ? await getCloudAgentPolicy({
@@ -144,12 +146,17 @@ const getMigrationCheckRoute = createApmServerRoute({
         })
       : undefined;
     const apmPackagePolicy = getApmPackagePolicy(cloudAgentPolicy);
+    const packagePolicies = await getApmPackgePolicies({
+      core,
+      fleetPluginStart,
+    });
     return {
       has_cloud_agent_policy: !!cloudAgentPolicy,
       has_cloud_apm_package_policy: !!apmPackagePolicy,
       cloud_apm_migration_enabled: cloudApmMigrationEnabled,
       has_required_role: hasRequiredRole,
       cloud_apm_package_policy: apmPackagePolicy,
+      has_apm_integrations: packagePolicies.total > 0,
     };
   },
 });
diff --git a/x-pack/plugins/apm/server/tutorial/envs/elastic_cloud.ts b/x-pack/plugins/apm/server/tutorial/envs/elastic_cloud.ts
index c62e42f22219..654c0e675a05 100644
--- a/x-pack/plugins/apm/server/tutorial/envs/elastic_cloud.ts
+++ b/x-pack/plugins/apm/server/tutorial/envs/elastic_cloud.ts
@@ -25,10 +25,18 @@ import {
   createPhpAgentInstructions,
 } from '../../../common/tutorial/instructions/apm_agent_instructions';
 import { CloudSetup } from '../../../../cloud/server';
+import { APMConfig } from '../..';
+import { getOnPremApmServerInstructionSet } from './on_prem_apm_server_instruction_set';
 
-export function createElasticCloudInstructions(
-  cloudSetup?: CloudSetup
-): TutorialSchema['elasticCloud'] {
+export function createElasticCloudInstructions({
+  cloudSetup,
+  apmConfig,
+  isFleetPluginEnabled,
+}: {
+  cloudSetup?: CloudSetup;
+  apmConfig: APMConfig;
+  isFleetPluginEnabled: boolean;
+}): TutorialSchema['elasticCloud'] {
   const apmServerUrl = cloudSetup?.apm.url;
   const instructionSets = [];
 
@@ -36,6 +44,9 @@ export function createElasticCloudInstructions(
     instructionSets.push(getApmServerInstructionSet(cloudSetup));
   }
 
+  instructionSets.push(
+    getOnPremApmServerInstructionSet({ apmConfig, isFleetPluginEnabled })
+  );
   instructionSets.push(getApmAgentInstructionSet(cloudSetup));
 
   return {
diff --git a/x-pack/plugins/apm/server/tutorial/envs/on_prem.ts b/x-pack/plugins/apm/server/tutorial/envs/on_prem.ts
index 8051ef2a72b6..18e30fe07808 100644
--- a/x-pack/plugins/apm/server/tutorial/envs/on_prem.ts
+++ b/x-pack/plugins/apm/server/tutorial/envs/on_prem.ts
@@ -23,15 +23,7 @@ import {
   createRackAgentInstructions,
   createRailsAgentInstructions,
 } from '../../../common/tutorial/instructions/apm_agent_instructions';
-import {
-  createDownloadServerDeb,
-  createDownloadServerOsx,
-  createDownloadServerRpm,
-  createEditConfig,
-  createStartServerUnix,
-  createStartServerUnixSysv,
-  createWindowsServerInstructions,
-} from '../../../common/tutorial/instructions/apm_server_instructions';
+import { getOnPremApmServerInstructionSet } from './on_prem_apm_server_instruction_set';
 
 export function onPremInstructions({
   apmConfig,
@@ -40,121 +32,9 @@ export function onPremInstructions({
   apmConfig: APMConfig;
   isFleetPluginEnabled: boolean;
 }): InstructionsSchema {
-  const EDIT_CONFIG = createEditConfig();
-  const START_SERVER_UNIX = createStartServerUnix();
-  const START_SERVER_UNIX_SYSV = createStartServerUnixSysv();
-
   return {
     instructionSets: [
-      {
-        title: i18n.translate('xpack.apm.tutorial.apmServer.title', {
-          defaultMessage: 'APM Server',
-        }),
-        callOut: {
-          title: i18n.translate('xpack.apm.tutorial.apmServer.callOut.title', {
-            defaultMessage: 'Important: Updating to 7.0 or higher',
-          }),
-          message: i18n.translate(
-            'xpack.apm.tutorial.apmServer.callOut.message',
-            {
-              defaultMessage: `Please make sure your APM Server is updated to 7.0 or higher. \
-            You can also migrate your 6.x data with the migration assistant found in Kibana's management section.`,
-            }
-          ),
-          iconType: 'alert',
-        },
-        instructionVariants: [
-          {
-            id: INSTRUCTION_VARIANT.DEB,
-            instructions: [
-              createDownloadServerDeb(),
-              EDIT_CONFIG,
-              START_SERVER_UNIX_SYSV,
-            ],
-          },
-          {
-            id: INSTRUCTION_VARIANT.RPM,
-            instructions: [
-              createDownloadServerRpm(),
-              EDIT_CONFIG,
-              START_SERVER_UNIX_SYSV,
-            ],
-          },
-          {
-            id: INSTRUCTION_VARIANT.OSX,
-            instructions: [
-              createDownloadServerOsx(),
-              EDIT_CONFIG,
-              START_SERVER_UNIX,
-            ],
-          },
-          {
-            id: INSTRUCTION_VARIANT.WINDOWS,
-            instructions: createWindowsServerInstructions(),
-          },
-          // hides fleet section when plugin is disabled
-          ...(isFleetPluginEnabled
-            ? [
-                {
-                  id: INSTRUCTION_VARIANT.FLEET,
-                  instructions: [
-                    {
-                      title: i18n.translate('xpack.apm.tutorial.fleet.title', {
-                        defaultMessage: 'Fleet',
-                      }),
-                      customComponentName: 'TutorialFleetInstructions',
-                    },
-                  ],
-                },
-              ]
-            : []),
-        ],
-        statusCheck: {
-          title: i18n.translate(
-            'xpack.apm.tutorial.apmServer.statusCheck.title',
-            {
-              defaultMessage: 'APM Server status',
-            }
-          ),
-          text: i18n.translate(
-            'xpack.apm.tutorial.apmServer.statusCheck.text',
-            {
-              defaultMessage:
-                'Make sure APM Server is running before you start implementing the APM agents.',
-            }
-          ),
-          btnLabel: i18n.translate(
-            'xpack.apm.tutorial.apmServer.statusCheck.btnLabel',
-            {
-              defaultMessage: 'Check APM Server status',
-            }
-          ),
-          success: i18n.translate(
-            'xpack.apm.tutorial.apmServer.statusCheck.successMessage',
-            {
-              defaultMessage: 'You have correctly setup APM Server',
-            }
-          ),
-          error: i18n.translate(
-            'xpack.apm.tutorial.apmServer.statusCheck.errorMessage',
-            {
-              defaultMessage:
-                'No APM Server detected. Please make sure it is running and you have updated to 7.0 or higher.',
-            }
-          ),
-          esHitsCheck: {
-            index: apmConfig.indices.onboarding,
-            query: {
-              bool: {
-                filter: [
-                  { term: { 'processor.event': 'onboarding' } },
-                  { range: { 'observer.version_major': { gte: 7 } } },
-                ],
-              },
-            },
-          },
-        },
-      },
+      getOnPremApmServerInstructionSet({ apmConfig, isFleetPluginEnabled }),
       {
         title: i18n.translate('xpack.apm.tutorial.apmAgents.title', {
           defaultMessage: 'APM Agents',
diff --git a/x-pack/plugins/apm/server/tutorial/envs/on_prem_apm_server_instruction_set.ts b/x-pack/plugins/apm/server/tutorial/envs/on_prem_apm_server_instruction_set.ts
new file mode 100644
index 000000000000..b9c491082f78
--- /dev/null
+++ b/x-pack/plugins/apm/server/tutorial/envs/on_prem_apm_server_instruction_set.ts
@@ -0,0 +1,136 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License
+ * 2.0; you may not use this file except in compliance with the Elastic License
+ * 2.0.
+ */
+
+import { i18n } from '@kbn/i18n';
+import { APMConfig } from '../..';
+import {
+  InstructionsSchema,
+  INSTRUCTION_VARIANT,
+} from '../../../../../../src/plugins/home/server';
+import {
+  createDownloadServerDeb,
+  createDownloadServerOsx,
+  createDownloadServerRpm,
+  createEditConfig,
+  createStartServerUnix,
+  createStartServerUnixSysv,
+  createWindowsServerInstructions,
+} from '../../../common/tutorial/instructions/apm_server_instructions';
+
+const EDIT_CONFIG = createEditConfig();
+const START_SERVER_UNIX = createStartServerUnix();
+const START_SERVER_UNIX_SYSV = createStartServerUnixSysv();
+
+export function getOnPremApmServerInstructionSet({
+  apmConfig,
+  isFleetPluginEnabled,
+}: {
+  apmConfig: APMConfig;
+  isFleetPluginEnabled: boolean;
+}): InstructionsSchema['instructionSets'][0] {
+  return {
+    title: i18n.translate('xpack.apm.tutorial.apmServer.title', {
+      defaultMessage: 'APM Server',
+    }),
+    callOut: {
+      title: i18n.translate('xpack.apm.tutorial.apmServer.callOut.title', {
+        defaultMessage: 'Important: Updating to 7.0 or higher',
+      }),
+      message: i18n.translate('xpack.apm.tutorial.apmServer.callOut.message', {
+        defaultMessage: `Please make sure your APM Server is updated to 7.0 or higher. \
+            You can also migrate your 6.x data with the migration assistant found in Kibana's management section.`,
+      }),
+      iconType: 'alert',
+    },
+    instructionVariants: [
+      {
+        id: INSTRUCTION_VARIANT.DEB,
+        instructions: [
+          createDownloadServerDeb(),
+          EDIT_CONFIG,
+          START_SERVER_UNIX_SYSV,
+        ],
+      },
+      {
+        id: INSTRUCTION_VARIANT.RPM,
+        instructions: [
+          createDownloadServerRpm(),
+          EDIT_CONFIG,
+          START_SERVER_UNIX_SYSV,
+        ],
+      },
+      {
+        id: INSTRUCTION_VARIANT.OSX,
+        instructions: [
+          createDownloadServerOsx(),
+          EDIT_CONFIG,
+          START_SERVER_UNIX,
+        ],
+      },
+      {
+        id: INSTRUCTION_VARIANT.WINDOWS,
+        instructions: createWindowsServerInstructions(),
+      },
+      // hides fleet section when plugin is disabled
+      ...(isFleetPluginEnabled
+        ? [
+            {
+              id: INSTRUCTION_VARIANT.FLEET,
+              instructions: [
+                {
+                  title: i18n.translate('xpack.apm.tutorial.fleet.title', {
+                    defaultMessage: 'Fleet',
+                  }),
+                  customComponentName: 'TutorialFleetInstructions',
+                },
+              ],
+              initialSelected: true,
+            },
+          ]
+        : []),
+    ],
+    statusCheck: {
+      title: i18n.translate('xpack.apm.tutorial.apmServer.statusCheck.title', {
+        defaultMessage: 'APM Server status',
+      }),
+      text: i18n.translate('xpack.apm.tutorial.apmServer.statusCheck.text', {
+        defaultMessage:
+          'Make sure APM Server is running before you start implementing the APM agents.',
+      }),
+      btnLabel: i18n.translate(
+        'xpack.apm.tutorial.apmServer.statusCheck.btnLabel',
+        {
+          defaultMessage: 'Check APM Server status',
+        }
+      ),
+      success: i18n.translate(
+        'xpack.apm.tutorial.apmServer.statusCheck.successMessage',
+        {
+          defaultMessage: 'You have correctly setup APM Server',
+        }
+      ),
+      error: i18n.translate(
+        'xpack.apm.tutorial.apmServer.statusCheck.errorMessage',
+        {
+          defaultMessage:
+            'No APM Server detected. Please make sure it is running and you have updated to 7.0 or higher.',
+        }
+      ),
+      esHitsCheck: {
+        index: apmConfig.indices.onboarding,
+        query: {
+          bool: {
+            filter: [
+              { term: { 'processor.event': 'onboarding' } },
+              { range: { 'observer.version_major': { gte: 7 } } },
+            ],
+          },
+        },
+      },
+    },
+  };
+}
diff --git a/x-pack/plugins/apm/server/tutorial/index.ts b/x-pack/plugins/apm/server/tutorial/index.ts
index 5d3ff8636df4..c9fc7eb6467d 100644
--- a/x-pack/plugins/apm/server/tutorial/index.ts
+++ b/x-pack/plugins/apm/server/tutorial/index.ts
@@ -107,7 +107,11 @@ It allows you to monitor the performance of thousands of applications in real ti
       artifacts,
       customStatusCheckName: 'apm_fleet_server_status_check',
       onPrem: onPremInstructions({ apmConfig, isFleetPluginEnabled }),
-      elasticCloud: createElasticCloudInstructions(cloud),
+      elasticCloud: createElasticCloudInstructions({
+        apmConfig,
+        isFleetPluginEnabled,
+        cloudSetup: cloud,
+      }),
       previewImagePath: '/plugins/apm/assets/apm.png',
       savedObjects,
       savedObjectsInstallMsg: i18n.translate(
diff --git a/x-pack/plugins/fleet/common/constants/epm.ts b/x-pack/plugins/fleet/common/constants/epm.ts
index 734d578687bc..5137e422e097 100644
--- a/x-pack/plugins/fleet/common/constants/epm.ts
+++ b/x-pack/plugins/fleet/common/constants/epm.ts
@@ -32,6 +32,13 @@ export const STANDALONE_RUN_INSTRUCTIONS = './elastic-agent install';
 removable, but it doesn't install by default. Following the table, it needs to
 be in `unremovablePackages` and in `autoUpdatePackages`, but not in
 `defaultPackages`.
+
+
+We also define "auto upgrade policies" packages below. These are packages that are considered "stack-aligned"
+and require policies to be auto-upgraded in order to properly function. Commonly, packages that ship custom policy
+editor UI's in the Kibana codebase will be included in this set of packages to avoid backwards-compatibility concerns
+in their custom policy editor implementations.
+
 */
 
 export const unremovablePackages = [
@@ -49,6 +56,8 @@ export const autoUpdatePackages = [
   FLEET_SYNTHETICS_PACKAGE,
 ];
 
+export const autoUpgradePoliciesPackages = [FLEET_APM_PACKAGE, FLEET_SYNTHETICS_PACKAGE];
+
 export const agentAssetTypes = {
   Input: 'input',
 } as const;
diff --git a/x-pack/plugins/fleet/common/constants/preconfiguration.ts b/x-pack/plugins/fleet/common/constants/preconfiguration.ts
index 2ec67393df76..3e7377477c93 100644
--- a/x-pack/plugins/fleet/common/constants/preconfiguration.ts
+++ b/x-pack/plugins/fleet/common/constants/preconfiguration.ts
@@ -5,6 +5,8 @@
  * 2.0.
  */
 
+import { uniqBy } from 'lodash';
+
 import type { PreconfiguredAgentPolicy } from '../types';
 
 import {
@@ -13,6 +15,7 @@ import {
   FLEET_SERVER_PACKAGE,
   autoUpdatePackages,
   monitoringTypes,
+  autoUpgradePoliciesPackages,
 } from './epm';
 
 export const PRECONFIGURATION_DELETION_RECORD_SAVED_OBJECT_TYPE =
@@ -72,6 +75,18 @@ export const AUTO_UPDATE_PACKAGES = autoUpdatePackages.map((name) => ({
   version: PRECONFIGURATION_LATEST_KEYWORD,
 }));
 
+// These packages default to `keep_policies_up_to_date: true` and don't allow users to opt out
+export const AUTO_UPGRADE_POLICIES_PACKAGES = autoUpgradePoliciesPackages.map((name) => ({
+  name,
+  version: PRECONFIGURATION_LATEST_KEYWORD,
+}));
+
+// Controls whether the `Keep Policies up to date` setting is exposed to the user
+export const KEEP_POLICIES_UP_TO_DATE_PACKAGES = uniqBy(
+  [...AUTO_UPGRADE_POLICIES_PACKAGES, ...DEFAULT_PACKAGES, ...AUTO_UPDATE_PACKAGES],
+  ({ name }) => name
+);
+
 export interface PreconfigurationError {
   package?: { name: string; version: string };
   agentPolicy?: { name: string };
diff --git a/x-pack/plugins/fleet/common/types/models/epm.ts b/x-pack/plugins/fleet/common/types/models/epm.ts
index 078281fec980..f7b446cc53c7 100644
--- a/x-pack/plugins/fleet/common/types/models/epm.ts
+++ b/x-pack/plugins/fleet/common/types/models/epm.ts
@@ -402,7 +402,7 @@ export interface Installation extends SavedObjectAttributes {
   install_version: string;
   install_started_at: string;
   install_source: InstallSource;
-  keep_policies_up_to_date: boolean;
+  keep_policies_up_to_date?: boolean;
 }
 
 export interface PackageUsageStats {
diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_details_page/components/agent_logs/agent_logs.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_details_page/components/agent_logs/agent_logs.tsx
index 783a960aff12..045c9c28c378 100644
--- a/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_details_page/components/agent_logs/agent_logs.tsx
+++ b/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_details_page/components/agent_logs/agent_logs.tsx
@@ -277,9 +277,9 @@ export const AgentLogsUI: React.FunctionComponent<AgentLogsProps> = memo(
 
     return (
       <WrapperFlexGroup direction="column" gutterSize="m">
-        {agentPolicy && !agentPolicy.monitoring_enabled?.includes('logs') && (
-          <AgentPolicyLogsNotEnabledCallout agentPolicy={agentPolicy} />
-        )}
+        {agentPolicy &&
+          !agentPolicy.monitoring_enabled?.includes('logs') &&
+          !agentPolicy.is_managed && <AgentPolicyLogsNotEnabledCallout agentPolicy={agentPolicy} />}
         <EuiFlexItem grow={false}>
           <EuiFlexGroup gutterSize="m">
             <EuiFlexItem>
diff --git a/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/components/keep_policies_up_to_date_switch.tsx b/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/components/keep_policies_up_to_date_switch.tsx
index 751282cc4228..364b24ab4891 100644
--- a/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/components/keep_policies_up_to_date_switch.tsx
+++ b/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/components/keep_policies_up_to_date_switch.tsx
@@ -12,11 +12,13 @@ import { EuiSwitch, EuiSpacer, EuiText, EuiFlexGroup, EuiFlexItem, EuiIcon } fro
 
 interface Props {
   checked: boolean;
+  disabled?: boolean;
   onChange: () => void;
 }
 
 export const KeepPoliciesUpToDateSwitch: React.FunctionComponent<Props> = ({
   checked,
+  disabled = false,
   onChange,
 }) => (
   <>
@@ -27,6 +29,7 @@ export const KeepPoliciesUpToDateSwitch: React.FunctionComponent<Props> = ({
       )}
       checked={checked}
       onChange={onChange}
+      disabled={disabled}
     />
     <EuiSpacer size="s" />
     <EuiText color="subdued" size="xs">
@@ -35,10 +38,17 @@ export const KeepPoliciesUpToDateSwitch: React.FunctionComponent<Props> = ({
           <EuiIcon type="iInCircle" />
         </EuiFlexItem>
         <EuiFlexItem grow={false}>
-          <FormattedMessage
-            id="xpack.fleet.integrations.settings.keepIntegrationPoliciesUpToDateDescription"
-            defaultMessage="When enabled, Fleet will attempt to upgrade and deploy integration policies automatically"
-          />
+          {disabled ? (
+            <FormattedMessage
+              id="xpack.fleet.integrations.settings.keepIntegrationPoliciesUpToDateDisabledDescription"
+              defaultMessage="This integration requires Fleet to automatically upgrade its integration policies"
+            />
+          ) : (
+            <FormattedMessage
+              id="xpack.fleet.integrations.settings.keepIntegrationPoliciesUpToDateDescription"
+              defaultMessage="When enabled, Fleet will attempt to upgrade and deploy integration policies automatically"
+            />
+          )}
         </EuiFlexItem>
       </EuiFlexGroup>
     </EuiText>
diff --git a/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/settings/settings.tsx b/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/settings/settings.tsx
index 5fa274c0feb9..31a01ffd7bdc 100644
--- a/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/settings/settings.tsx
+++ b/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/settings/settings.tsx
@@ -9,7 +9,6 @@ import React, { memo, useCallback, useEffect, useMemo, useState } from 'react';
 import styled from 'styled-components';
 import { FormattedMessage } from '@kbn/i18n/react';
 import semverLt from 'semver/functions/lt';
-import { uniq } from 'lodash';
 
 import {
   EuiCallOut,
@@ -35,8 +34,8 @@ import {
 } from '../../../../../hooks';
 import {
   PACKAGE_POLICY_SAVED_OBJECT_TYPE,
-  AUTO_UPDATE_PACKAGES,
-  DEFAULT_PACKAGES,
+  KEEP_POLICIES_UP_TO_DATE_PACKAGES,
+  AUTO_UPGRADE_POLICIES_PACKAGES,
 } from '../../../../../constants';
 
 import { KeepPoliciesUpToDateSwitch } from '../components';
@@ -107,11 +106,11 @@ export const SettingsPage: React.FC<Props> = memo(({ packageInfo }: Props) => {
   const { notifications } = useStartServices();
 
   const shouldShowKeepPoliciesUpToDateSwitch = useMemo(() => {
-    const packages = [...DEFAULT_PACKAGES, ...AUTO_UPDATE_PACKAGES];
-
-    const packageNames = uniq(packages.map((pkg) => pkg.name));
+    return KEEP_POLICIES_UP_TO_DATE_PACKAGES.some((pkg) => pkg.name === name);
+  }, [name]);
 
-    return packageNames.includes(name);
+  const isShowKeepPoliciesUpToDateSwitchDisabled = useMemo(() => {
+    return AUTO_UPGRADE_POLICIES_PACKAGES.some((pkg) => pkg.name === name);
   }, [name]);
 
   const [keepPoliciesUpToDateSwitchValue, setKeepPoliciesUpToDateSwitchValue] = useState<boolean>(
@@ -274,6 +273,7 @@ export const SettingsPage: React.FC<Props> = memo(({ packageInfo }: Props) => {
                   <KeepPoliciesUpToDateSwitch
                     checked={keepPoliciesUpToDateSwitchValue}
                     onChange={handleKeepPoliciesUpToDateSwitchChange}
+                    disabled={isShowKeepPoliciesUpToDateSwitchDisabled}
                   />
                   <EuiSpacer size="l" />
                 </>
diff --git a/x-pack/plugins/fleet/public/constants/index.ts b/x-pack/plugins/fleet/public/constants/index.ts
index 38b7875c93b3..139f9d3d1f1c 100644
--- a/x-pack/plugins/fleet/public/constants/index.ts
+++ b/x-pack/plugins/fleet/public/constants/index.ts
@@ -21,6 +21,8 @@ export {
   // Preconfiguration
   AUTO_UPDATE_PACKAGES,
   DEFAULT_PACKAGES,
+  KEEP_POLICIES_UP_TO_DATE_PACKAGES,
+  AUTO_UPGRADE_POLICIES_PACKAGES,
 } from '../../common/constants';
 
 export * from './page_paths';
diff --git a/x-pack/plugins/fleet/server/saved_objects/index.ts b/x-pack/plugins/fleet/server/saved_objects/index.ts
index 3b459c938b5f..26adf7b9fcbc 100644
--- a/x-pack/plugins/fleet/server/saved_objects/index.ts
+++ b/x-pack/plugins/fleet/server/saved_objects/index.ts
@@ -35,7 +35,7 @@ import {
 import { migratePackagePolicyToV7140, migrateInstallationToV7140 } from './migrations/to_v7_14_0';
 import { migratePackagePolicyToV7150 } from './migrations/to_v7_15_0';
 import { migrateInstallationToV7160, migratePackagePolicyToV7160 } from './migrations/to_v7_16_0';
-import { migrateOutputToV800 } from './migrations/to_v8_0_0';
+import { migrateInstallationToV800, migrateOutputToV800 } from './migrations/to_v8_0_0';
 
 /*
  * Saved object types and mappings
@@ -255,6 +255,7 @@ const getSavedObjectTypes = (
       '7.14.0': migrateInstallationToV7140,
       '7.14.1': migrateInstallationToV7140,
       '7.16.0': migrateInstallationToV7160,
+      '8.0.0': migrateInstallationToV800,
     },
   },
   [ASSETS_SAVED_OBJECT_TYPE]: {
diff --git a/x-pack/plugins/fleet/server/saved_objects/migrations/to_v8_0_0.ts b/x-pack/plugins/fleet/server/saved_objects/migrations/to_v8_0_0.ts
index 77797b3d27ba..61db35fd9faf 100644
--- a/x-pack/plugins/fleet/server/saved_objects/migrations/to_v8_0_0.ts
+++ b/x-pack/plugins/fleet/server/saved_objects/migrations/to_v8_0_0.ts
@@ -7,8 +7,8 @@
 
 import type { SavedObjectMigrationFn } from 'kibana/server';
 
-import type { Output } from '../../../common';
-import {} from '../../../common';
+import type { Installation, Output } from '../../../common';
+import { AUTO_UPGRADE_POLICIES_PACKAGES } from '../../../common';
 
 export const migrateOutputToV800: SavedObjectMigrationFn<Output, Output> = (
   outputDoc,
@@ -20,3 +20,20 @@ export const migrateOutputToV800: SavedObjectMigrationFn<Output, Output> = (
 
   return outputDoc;
 };
+
+export const migrateInstallationToV800: SavedObjectMigrationFn<Installation, Installation> = (
+  installationDoc,
+  migrationContext
+) => {
+  const updatedInstallationDoc = installationDoc;
+
+  const shouldKeepPoliciesUpToDate = AUTO_UPGRADE_POLICIES_PACKAGES.some(
+    (pkg) => pkg.name === updatedInstallationDoc.attributes.name
+  );
+
+  if (shouldKeepPoliciesUpToDate) {
+    updatedInstallationDoc.attributes.keep_policies_up_to_date = true;
+  }
+
+  return updatedInstallationDoc;
+};
diff --git a/x-pack/plugins/fleet/server/services/epm/packages/install.ts b/x-pack/plugins/fleet/server/services/epm/packages/install.ts
index 5b86c944feb3..db26dc3a20a8 100644
--- a/x-pack/plugins/fleet/server/services/epm/packages/install.ts
+++ b/x-pack/plugins/fleet/server/services/epm/packages/install.ts
@@ -18,7 +18,7 @@ import type {
   InstallablePackage,
   InstallSource,
 } from '../../../../common';
-import { DEFAULT_PACKAGES } from '../../../../common';
+import { AUTO_UPGRADE_POLICIES_PACKAGES } from '../../../../common';
 import {
   IngestManagerError,
   PackageOperationNotSupportedError,
@@ -534,11 +534,14 @@ export async function createInstallation(options: {
   const removable = !isUnremovablePackage(pkgName);
   const toSaveESIndexPatterns = generateESIndexPatterns(packageInfo.data_streams);
 
-  // For default packages, default the `keep_policies_up_to_date` setting to true. For all other
-  // package, default it to false.
-  const defaultKeepPoliciesUpToDate = DEFAULT_PACKAGES.some(
+  // For "stack-aligned" packages, default the `keep_policies_up_to_date` setting to true. For all other
+  // packages, default it to undefined. Use undefined rather than false to allow us to differentiate
+  // between "unset" and "user explicitly disabled".
+  const defaultKeepPoliciesUpToDate = AUTO_UPGRADE_POLICIES_PACKAGES.some(
     ({ name }) => name === packageInfo.name
-  );
+  )
+    ? true
+    : undefined;
 
   const created = await savedObjectsClient.create<Installation>(
     PACKAGES_SAVED_OBJECT_TYPE,
diff --git a/x-pack/plugins/fleet/server/services/managed_package_policies.test.ts b/x-pack/plugins/fleet/server/services/managed_package_policies.test.ts
index b27248a3cb93..7ccfeb4fe764 100644
--- a/x-pack/plugins/fleet/server/services/managed_package_policies.test.ts
+++ b/x-pack/plugins/fleet/server/services/managed_package_policies.test.ts
@@ -8,7 +8,6 @@
 import { elasticsearchServiceMock, savedObjectsClientMock } from 'src/core/server/mocks';
 
 import type { Installation, PackageInfo } from '../../common';
-import { AUTO_UPDATE_PACKAGES } from '../../common';
 
 import { shouldUpgradePolicies, upgradeManagedPackagePolicies } from './managed_package_policies';
 import { packagePolicyService } from './package_policy';
@@ -227,50 +226,6 @@ describe('upgradeManagedPackagePolicies', () => {
 });
 
 describe('shouldUpgradePolicies', () => {
-  describe('package is marked as AUTO_UPDATE', () => {
-    describe('keep_policies_up_to_date is true', () => {
-      it('returns false', () => {
-        const packageInfo = {
-          version: '1.0.0',
-          keepPoliciesUpToDate: true,
-          name: AUTO_UPDATE_PACKAGES[0].name,
-        };
-
-        const installedPackage = {
-          version: '1.0.0',
-        };
-
-        const result = shouldUpgradePolicies(
-          packageInfo as PackageInfo,
-          installedPackage as Installation
-        );
-
-        expect(result).toBe(false);
-      });
-    });
-
-    describe('keep_policies_up_to_date is false', () => {
-      it('returns false', () => {
-        const packageInfo = {
-          version: '1.0.0',
-          keepPoliciesUpToDate: false,
-          name: AUTO_UPDATE_PACKAGES[0].name,
-        };
-
-        const installedPackage = {
-          version: '1.0.0',
-        };
-
-        const result = shouldUpgradePolicies(
-          packageInfo as PackageInfo,
-          installedPackage as Installation
-        );
-
-        expect(result).toBe(false);
-      });
-    });
-  });
-
   describe('package policy is up-to-date', () => {
     describe('keep_policies_up_to_date is true', () => {
       it('returns false', () => {
diff --git a/x-pack/plugins/lens/common/expressions/pie_chart/pie_chart.ts b/x-pack/plugins/lens/common/expressions/pie_chart/pie_chart.ts
index ed0391a16af2..053b46e480c7 100644
--- a/x-pack/plugins/lens/common/expressions/pie_chart/pie_chart.ts
+++ b/x-pack/plugins/lens/common/expressions/pie_chart/pie_chart.ts
@@ -49,7 +49,7 @@ export const pie: ExpressionFunctionDefinition<
     },
     shape: {
       types: ['string'],
-      options: ['pie', 'donut', 'treemap'],
+      options: ['pie', 'donut', 'treemap', 'mosaic'],
       help: '',
     },
     hideLabels: {
diff --git a/x-pack/plugins/lens/common/expressions/pie_chart/types.ts b/x-pack/plugins/lens/common/expressions/pie_chart/types.ts
index 8712675740f1..00fc7abaa043 100644
--- a/x-pack/plugins/lens/common/expressions/pie_chart/types.ts
+++ b/x-pack/plugins/lens/common/expressions/pie_chart/types.ts
@@ -8,6 +8,8 @@
 import type { PaletteOutput } from '../../../../../../src/plugins/charts/common';
 import type { LensMultiTable, LayerType } from '../../types';
 
+export type PieChartTypes = 'donut' | 'pie' | 'treemap' | 'mosaic';
+
 export interface SharedPieLayerState {
   groups: string[];
   metric?: string;
@@ -27,7 +29,7 @@ export type PieLayerState = SharedPieLayerState & {
 };
 
 export interface PieVisualizationState {
-  shape: 'donut' | 'pie' | 'treemap';
+  shape: PieChartTypes;
   layers: PieLayerState[];
   palette?: PaletteOutput;
 }
@@ -35,7 +37,7 @@ export interface PieVisualizationState {
 export type PieExpressionArgs = SharedPieLayerState & {
   title?: string;
   description?: string;
-  shape: 'pie' | 'donut' | 'treemap';
+  shape: PieChartTypes;
   hideLabels: boolean;
   palette: PaletteOutput;
 };
diff --git a/x-pack/plugins/lens/public/assets/chart_mosaic.tsx b/x-pack/plugins/lens/public/assets/chart_mosaic.tsx
new file mode 100644
index 000000000000..c385f0df1a00
--- /dev/null
+++ b/x-pack/plugins/lens/public/assets/chart_mosaic.tsx
@@ -0,0 +1,31 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License
+ * 2.0; you may not use this file except in compliance with the Elastic License
+ * 2.0.
+ */
+
+import React from 'react';
+import type { EuiIconProps } from '@elastic/eui';
+
+export const LensIconChartMosaic = ({ title, titleId, ...props }: Omit<EuiIconProps, 'type'>) => (
+  <svg
+    viewBox="0 0 30 22"
+    width={30}
+    height={22}
+    fill="none"
+    xmlns="http://www.w3.org/2000/svg"
+    aria-labelledby={titleId}
+    {...props}
+  >
+    {title ? <title id={titleId} /> : null}
+    <path
+      className="lensChartIcon__subdued"
+      d="M2 0a1 1 0 00-1 1v2a1 1 0 001 1h6a1 1 0 001-1V1a1 1 0 00-1-1H2zM2 14a1 1 0 00-1 1v6a1 1 0 001 1h6a1 1 0 001-1v-6a1 1 0 00-1-1H2zM11 13a1 1 0 011-1h6a1 1 0 011 1v8a1 1 0 01-1 1h-6a1 1 0 01-1-1v-8zM12 0a1 1 0 100 2h6a1 1 0 100-2h-6zM21 15a1 1 0 011-1h6a1 1 0 011 1v6a1 1 0 01-1 1h-6a1 1 0 01-1-1v-6zM22 0a1 1 0 00-1 1v4a1 1 0 001 1h6a1 1 0 001-1V1a1 1 0 00-1-1h-6z"
+    />
+    <path
+      className="lensChartIcon__accent"
+      d="M11 5a1 1 0 011-1h6a1 1 0 011 1v4a1 1 0 01-1 1h-6a1 1 0 01-1-1V5zM1 7a1 1 0 011-1h6a1 1 0 011 1v4a1 1 0 01-1 1H2a1 1 0 01-1-1V7zM22 8a1 1 0 00-1 1v2a1 1 0 001 1h6a1 1 0 001-1V9a1 1 0 00-1-1h-6z"
+    />
+  </svg>
+);
diff --git a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/layer_panel.test.tsx b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/layer_panel.test.tsx
index 92633d5e7305..a6be4acfbbcf 100644
--- a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/layer_panel.test.tsx
+++ b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/layer_panel.test.tsx
@@ -225,7 +225,39 @@ describe('LayerPanel', () => {
 
       const group = instance
         .find(EuiFormRow)
-        .findWhere((e) => e.prop('error') === 'Required dimension');
+        .findWhere((e) => e.prop('error') === 'Requires field');
+
+      expect(group).toHaveLength(1);
+    });
+
+    it('should render the required warning when only one group is configured (with requiredMinDimensionCount)', async () => {
+      mockVisualization.getConfiguration.mockReturnValue({
+        groups: [
+          {
+            groupLabel: 'A',
+            groupId: 'a',
+            accessors: [{ columnId: 'x' }],
+            filterOperations: () => true,
+            supportsMoreColumns: false,
+            dataTestSubj: 'lnsGroup',
+          },
+          {
+            groupLabel: 'B',
+            groupId: 'b',
+            accessors: [{ columnId: 'y' }],
+            filterOperations: () => true,
+            supportsMoreColumns: true,
+            dataTestSubj: 'lnsGroup',
+            requiredMinDimensionCount: 2,
+          },
+        ],
+      });
+
+      const { instance } = await mountWithProvider(<LayerPanel {...getDefaultProps()} />);
+
+      const group = instance
+        .find(EuiFormRow)
+        .findWhere((e) => e.prop('error') === 'Requires 2 fields');
 
       expect(group).toHaveLength(1);
     });
diff --git a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/layer_panel.tsx b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/layer_panel.tsx
index 6af3d88b17d4..84c7722ca1b8 100644
--- a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/layer_panel.tsx
+++ b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/layer_panel.tsx
@@ -385,7 +385,27 @@ export function LayerPanel(
           </header>
 
           {groups.map((group, groupIndex) => {
-            const isMissing = !isEmptyLayer && group.required && group.accessors.length === 0;
+            let isMissing = false;
+
+            if (!isEmptyLayer) {
+              if (group.requiredMinDimensionCount) {
+                isMissing = group.accessors.length < group.requiredMinDimensionCount;
+              } else if (group.required) {
+                isMissing = group.accessors.length === 0;
+              }
+            }
+
+            const isMissingError = group.requiredMinDimensionCount
+              ? i18n.translate('xpack.lens.editorFrame.requiresTwoOrMoreFieldsWarningLabel', {
+                  defaultMessage: 'Requires {requiredMinDimensionCount} fields',
+                  values: {
+                    requiredMinDimensionCount: group.requiredMinDimensionCount,
+                  },
+                })
+              : i18n.translate('xpack.lens.editorFrame.requiresFieldWarningLabel', {
+                  defaultMessage: 'Requires field',
+                });
+
             const isOptional = !group.required;
             return (
               <EuiFormRow
@@ -423,13 +443,7 @@ export function LayerPanel(
                 labelType="legend"
                 key={group.groupId}
                 isInvalid={isMissing}
-                error={
-                  isMissing
-                    ? i18n.translate('xpack.lens.editorFrame.requiredDimensionWarningLabel', {
-                        defaultMessage: 'Required dimension',
-                      })
-                    : []
-                }
+                error={isMissing ? isMissingError : []}
               >
                 <>
                   {group.accessors.length ? (
diff --git a/x-pack/plugins/lens/public/pie_visualization/constants.ts b/x-pack/plugins/lens/public/pie_visualization/constants.ts
index 9a2f39e7d34a..be0afc65aed3 100644
--- a/x-pack/plugins/lens/public/pie_visualization/constants.ts
+++ b/x-pack/plugins/lens/public/pie_visualization/constants.ts
@@ -6,41 +6,100 @@
  */
 
 import { i18n } from '@kbn/i18n';
+import { PartitionLayout } from '@elastic/charts';
 import { LensIconChartDonut } from '../assets/chart_donut';
 import { LensIconChartPie } from '../assets/chart_pie';
 import { LensIconChartTreemap } from '../assets/chart_treemap';
+import { LensIconChartMosaic } from '../assets/chart_mosaic';
+
+import type { SharedPieLayerState } from '../../common/expressions';
+
+interface CategoryOption {
+  value: SharedPieLayerState['categoryDisplay'];
+  inputDisplay: string;
+}
 
 const groupLabel = i18n.translate('xpack.lens.pie.groupLabel', {
   defaultMessage: 'Proportion',
 });
 
+const categoryOptions: CategoryOption[] = [
+  {
+    value: 'default',
+    inputDisplay: i18n.translate('xpack.lens.pieChart.showCategoriesLabel', {
+      defaultMessage: 'Inside or outside',
+    }),
+  },
+  {
+    value: 'inside',
+    inputDisplay: i18n.translate('xpack.lens.pieChart.fitInsideOnlyLabel', {
+      defaultMessage: 'Inside only',
+    }),
+  },
+  {
+    value: 'hide',
+    inputDisplay: i18n.translate('xpack.lens.pieChart.categoriesInLegendLabel', {
+      defaultMessage: 'Hide labels',
+    }),
+  },
+];
+
+const categoryOptionsTreemap: CategoryOption[] = [
+  {
+    value: 'default',
+    inputDisplay: i18n.translate('xpack.lens.pieChart.showTreemapCategoriesLabel', {
+      defaultMessage: 'Show labels',
+    }),
+  },
+  {
+    value: 'hide',
+    inputDisplay: i18n.translate('xpack.lens.pieChart.categoriesInLegendLabel', {
+      defaultMessage: 'Hide labels',
+    }),
+  },
+];
+
 export const CHART_NAMES = {
   donut: {
     icon: LensIconChartDonut,
     label: i18n.translate('xpack.lens.pie.donutLabel', {
       defaultMessage: 'Donut',
     }),
+    partitionType: PartitionLayout.sunburst,
     groupLabel,
+    categoryOptions,
   },
   pie: {
     icon: LensIconChartPie,
     label: i18n.translate('xpack.lens.pie.pielabel', {
       defaultMessage: 'Pie',
     }),
-
+    partitionType: PartitionLayout.sunburst,
     groupLabel,
+    categoryOptions,
   },
   treemap: {
     icon: LensIconChartTreemap,
     label: i18n.translate('xpack.lens.pie.treemaplabel', {
       defaultMessage: 'Treemap',
     }),
-
+    partitionType: PartitionLayout.treemap,
+    groupLabel,
+    categoryOptions: categoryOptionsTreemap,
+  },
+  mosaic: {
+    icon: LensIconChartMosaic,
+    label: i18n.translate('xpack.lens.pie.mosaiclabel', {
+      defaultMessage: 'Mosaic',
+    }),
+    partitionType: PartitionLayout.mosaic,
     groupLabel,
+    categoryOptions: [] as CategoryOption[],
   },
 };
 
 export const MAX_PIE_BUCKETS = 3;
 export const MAX_TREEMAP_BUCKETS = 2;
+export const MAX_MOSAIC_BUCKETS = 2;
 
 export const DEFAULT_PERCENT_DECIMALS = 2;
diff --git a/x-pack/plugins/lens/public/pie_visualization/render_function.tsx b/x-pack/plugins/lens/public/pie_visualization/render_function.tsx
index 05b9ca9c3416..2bf9827bb976 100644
--- a/x-pack/plugins/lens/public/pie_visualization/render_function.tsx
+++ b/x-pack/plugins/lens/public/pie_visualization/render_function.tsx
@@ -16,7 +16,6 @@ import {
   Partition,
   PartitionConfig,
   PartitionLayer,
-  PartitionLayout,
   PartitionFillLabel,
   RecursivePartial,
   Position,
@@ -29,7 +28,13 @@ import { VisualizationContainer } from '../visualization_container';
 import { CHART_NAMES, DEFAULT_PERCENT_DECIMALS } from './constants';
 import type { FormatFactory } from '../../common';
 import type { PieExpressionProps } from '../../common/expressions';
-import { getSliceValue, getFilterContext } from './render_helpers';
+import {
+  getSliceValue,
+  getFilterContext,
+  isTreemapOrMosaicShape,
+  byDataColorPaletteMap,
+  extractUniqTermsMap,
+} from './render_helpers';
 import { EmptyPlaceholder } from '../shared_components';
 import './visualization.scss';
 import {
@@ -110,6 +115,22 @@ export function PieComponent(
     })
   ).length;
 
+  const shouldUseByDataPalette = !syncColors && ['mosaic'].includes(shape) && bucketColumns[1]?.id;
+  let byDataPalette: ReturnType<typeof byDataColorPaletteMap>;
+  if (shouldUseByDataPalette) {
+    byDataPalette = byDataColorPaletteMap(
+      firstTable,
+      bucketColumns[1].id,
+      paletteService.get(palette.name),
+      palette
+    );
+  }
+
+  let sortingMap: Record<string, number>;
+  if (shape === 'mosaic') {
+    sortingMap = extractUniqTermsMap(firstTable, bucketColumns[0].id);
+  }
+
   const layers: PartitionLayer[] = bucketColumns.map((col, layerIndex) => {
     return {
       groupByRollup: (d: Datum) => d[col.id] ?? EMPTY_SLICE,
@@ -124,13 +145,29 @@ export function PieComponent(
         return String(d);
       },
       fillLabel,
+      sortPredicate:
+        shape === 'mosaic'
+          ? ([name1, node1], [, node2]) => {
+              // Sorting for first group
+              if (bucketColumns.length === 1 || (node1.children.length && name1 in sortingMap)) {
+                return sortingMap[name1];
+              }
+              // Sorting for second group
+              return node2.value - node1.value;
+            }
+          : undefined,
       shape: {
         fillColor: (d) => {
           const seriesLayers: SeriesLayer[] = [];
 
+          // Mind the difference here: the contrast computation for the text ignores the alpha/opacity
+          // therefore change it for dask mode
+          const defaultColor = isDarkMode ? 'rgba(0,0,0,0)' : 'rgba(255,255,255,0)';
+
           // Color is determined by round-robin on the index of the innermost slice
           // This has to be done recursively until we get to the slice index
           let tempParent: typeof d | typeof d['parent'] = d;
+
           while (tempParent.parent && tempParent.depth > 0) {
             seriesLayers.unshift({
               name: String(tempParent.parent.children[tempParent.sortIndex][0]),
@@ -140,12 +177,14 @@ export function PieComponent(
             tempParent = tempParent.parent;
           }
 
-          if (shape === 'treemap') {
+          if (byDataPalette && seriesLayers[1]) {
+            return byDataPalette.getColor(seriesLayers[1].name) || defaultColor;
+          }
+
+          if (isTreemapOrMosaicShape(shape)) {
             // Only highlight the innermost color of the treemap, as it accurately represents area
             if (layerIndex < bucketColumns.length - 1) {
-              // Mind the difference here: the contrast computation for the text ignores the alpha/opacity
-              // therefore change it for dask mode
-              return isDarkMode ? 'rgba(0,0,0,0)' : 'rgba(255,255,255,0)';
+              return defaultColor;
             }
             // only use the top level series layer for coloring
             if (seriesLayers.length > 1) {
@@ -164,14 +203,14 @@ export function PieComponent(
             palette.params
           );
 
-          return outputColor || 'rgba(0,0,0,0)';
+          return outputColor || defaultColor;
         },
       },
     };
   });
 
   const config: RecursivePartial<PartitionConfig> = {
-    partitionLayout: shape === 'treemap' ? PartitionLayout.treemap : PartitionLayout.sunburst,
+    partitionLayout: CHART_NAMES[shape].partitionType,
     fontFamily: chartTheme.barSeriesStyle?.displayValue?.fontFamily,
     outerSizeRatio: 1,
     specialFirstInnermostSector: true,
@@ -191,7 +230,7 @@ export function PieComponent(
     sectorLineWidth: 1.5,
     circlePadding: 4,
   };
-  if (shape === 'treemap') {
+  if (isTreemapOrMosaicShape(shape)) {
     if (hideLabels || categoryDisplay === 'hide') {
       config.fillLabel = { textColor: 'rgba(0,0,0,0)' };
     }
@@ -279,7 +318,9 @@ export function PieComponent(
           showLegend={
             !hideLabels &&
             (legendDisplay === 'show' ||
-              (legendDisplay === 'default' && bucketColumns.length > 1 && shape !== 'treemap'))
+              (legendDisplay === 'default' &&
+                bucketColumns.length > 1 &&
+                !isTreemapOrMosaicShape(shape)))
           }
           legendPosition={legendPosition || Position.Right}
           legendMaxDepth={nestedLegend ? undefined : 1 /* Color is based only on first layer */}
diff --git a/x-pack/plugins/lens/public/pie_visualization/render_helpers.test.ts b/x-pack/plugins/lens/public/pie_visualization/render_helpers.test.ts
index 7c55c0fa6193..dd27632b36e4 100644
--- a/x-pack/plugins/lens/public/pie_visualization/render_helpers.test.ts
+++ b/x-pack/plugins/lens/public/pie_visualization/render_helpers.test.ts
@@ -5,8 +5,16 @@
  * 2.0.
  */
 
-import { Datatable } from 'src/plugins/expressions/public';
-import { getSliceValue, getFilterContext } from './render_helpers';
+import type { Datatable } from 'src/plugins/expressions/public';
+import type { PaletteDefinition, PaletteOutput } from 'src/plugins/charts/public';
+
+import {
+  getSliceValue,
+  getFilterContext,
+  byDataColorPaletteMap,
+  extractUniqTermsMap,
+} from './render_helpers';
+import { chartPluginMock } from '../../../../../src/plugins/charts/public/mocks';
 
 describe('render helpers', () => {
   describe('#getSliceValue', () => {
@@ -200,4 +208,113 @@ describe('render helpers', () => {
       });
     });
   });
+
+  describe('#extractUniqTermsMap', () => {
+    it('should extract map', () => {
+      const table: Datatable = {
+        type: 'datatable',
+        columns: [
+          { id: 'a', name: 'A', meta: { type: 'string' } },
+          { id: 'b', name: 'B', meta: { type: 'string' } },
+          { id: 'c', name: 'C', meta: { type: 'number' } },
+        ],
+        rows: [
+          { a: 'Hi', b: 'Two', c: 2 },
+          { a: 'Test', b: 'Two', c: 5 },
+          { a: 'Foo', b: 'Three', c: 6 },
+        ],
+      };
+      expect(extractUniqTermsMap(table, 'a')).toMatchInlineSnapshot(`
+        Object {
+          "Foo": 2,
+          "Hi": 0,
+          "Test": 1,
+        }
+      `);
+      expect(extractUniqTermsMap(table, 'b')).toMatchInlineSnapshot(`
+        Object {
+          "Three": 1,
+          "Two": 0,
+        }
+      `);
+    });
+  });
+
+  describe('#byDataColorPaletteMap', () => {
+    let datatable: Datatable;
+    let paletteDefinition: PaletteDefinition;
+    let palette: PaletteOutput;
+    const columnId = 'foo';
+
+    beforeEach(() => {
+      datatable = {
+        rows: [
+          {
+            [columnId]: '1',
+          },
+          {
+            [columnId]: '2',
+          },
+        ],
+      } as unknown as Datatable;
+      paletteDefinition = chartPluginMock.createPaletteRegistry().get('default');
+      palette = { type: 'palette' } as PaletteOutput;
+    });
+
+    it('should create byDataColorPaletteMap', () => {
+      expect(byDataColorPaletteMap(datatable, columnId, paletteDefinition, palette))
+        .toMatchInlineSnapshot(`
+        Object {
+          "getColor": [Function],
+        }
+      `);
+    });
+
+    it('should get color', () => {
+      const colorPaletteMap = byDataColorPaletteMap(
+        datatable,
+        columnId,
+        paletteDefinition,
+        palette
+      );
+
+      expect(colorPaletteMap.getColor('1')).toBe('black');
+    });
+
+    it('should return undefined in case if values not in datatable', () => {
+      const colorPaletteMap = byDataColorPaletteMap(
+        datatable,
+        columnId,
+        paletteDefinition,
+        palette
+      );
+
+      expect(colorPaletteMap.getColor('wrong')).toBeUndefined();
+    });
+
+    it('should increase rankAtDepth for each new value', () => {
+      const colorPaletteMap = byDataColorPaletteMap(
+        datatable,
+        columnId,
+        paletteDefinition,
+        palette
+      );
+      colorPaletteMap.getColor('1');
+      colorPaletteMap.getColor('2');
+
+      expect(paletteDefinition.getCategoricalColor).toHaveBeenNthCalledWith(
+        1,
+        [{ name: '1', rankAtDepth: 0, totalSeriesAtDepth: 2 }],
+        { behindText: false },
+        undefined
+      );
+
+      expect(paletteDefinition.getCategoricalColor).toHaveBeenNthCalledWith(
+        2,
+        [{ name: '2', rankAtDepth: 1, totalSeriesAtDepth: 2 }],
+        { behindText: false },
+        undefined
+      );
+    });
+  });
 });
diff --git a/x-pack/plugins/lens/public/pie_visualization/render_helpers.ts b/x-pack/plugins/lens/public/pie_visualization/render_helpers.ts
index d2858efa9015..bdffacde6563 100644
--- a/x-pack/plugins/lens/public/pie_visualization/render_helpers.ts
+++ b/x-pack/plugins/lens/public/pie_visualization/render_helpers.ts
@@ -5,9 +5,11 @@
  * 2.0.
  */
 
-import { Datum, LayerValue } from '@elastic/charts';
-import { Datatable, DatatableColumn } from 'src/plugins/expressions/public';
-import { LensFilterEvent } from '../types';
+import type { Datum, LayerValue } from '@elastic/charts';
+import type { Datatable, DatatableColumn } from 'src/plugins/expressions/public';
+import type { LensFilterEvent } from '../types';
+import type { PieChartTypes } from '../../common/expressions/pie_chart/types';
+import type { PaletteDefinition, PaletteOutput } from '../../../../../src/plugins/charts/public';
 
 export function getSliceValue(d: Datum, metricColumn: DatatableColumn) {
   const value = d[metricColumn.id];
@@ -35,3 +37,61 @@ export function getFilterContext(
     })),
   };
 }
+
+export const isPartitionShape = (shape: PieChartTypes | string) =>
+  ['donut', 'pie', 'treemap', 'mosaic'].includes(shape);
+
+export const isTreemapOrMosaicShape = (shape: PieChartTypes | string) =>
+  ['treemap', 'mosaic'].includes(shape);
+
+export const extractUniqTermsMap = (dataTable: Datatable, columnId: string) =>
+  [...new Set(dataTable.rows.map((item) => item[columnId]))].reduce(
+    (acc, item, index) => ({
+      ...acc,
+      [item]: index,
+    }),
+    {}
+  );
+
+export const byDataColorPaletteMap = (
+  dataTable: Datatable,
+  columnId: string,
+  paletteDefinition: PaletteDefinition,
+  { params }: PaletteOutput
+) => {
+  const colorMap = new Map<string, string | undefined>(
+    dataTable.rows.map((item) => [String(item[columnId]), undefined])
+  );
+  let rankAtDepth = 0;
+
+  return {
+    getColor: (item: unknown) => {
+      const key = String(item);
+
+      if (colorMap.has(key)) {
+        let color = colorMap.get(key);
+
+        if (color) {
+          return color;
+        }
+        color =
+          paletteDefinition.getCategoricalColor(
+            [
+              {
+                name: key,
+                totalSeriesAtDepth: colorMap.size,
+                rankAtDepth: rankAtDepth++,
+              },
+            ],
+            {
+              behindText: false,
+            },
+            params
+          ) || undefined;
+
+        colorMap.set(key, color);
+        return color;
+      }
+    },
+  };
+};
diff --git a/x-pack/plugins/lens/public/pie_visualization/suggestions.test.ts b/x-pack/plugins/lens/public/pie_visualization/suggestions.test.ts
index 5a57371eb645..656d00960766 100644
--- a/x-pack/plugins/lens/public/pie_visualization/suggestions.test.ts
+++ b/x-pack/plugins/lens/public/pie_visualization/suggestions.test.ts
@@ -6,9 +6,9 @@
  */
 
 import { PaletteOutput } from 'src/plugins/charts/public';
-import { DataType, SuggestionRequest } from '../types';
 import { suggestions } from './suggestions';
-import { PieVisualizationState } from '../../common/expressions';
+import type { DataType, SuggestionRequest } from '../types';
+import type { PieLayerState, PieVisualizationState } from '../../common/expressions';
 import { layerTypes } from '../../common';
 
 describe('suggestions', () => {
@@ -144,6 +144,38 @@ describe('suggestions', () => {
       ).toHaveLength(0);
     });
 
+    it('should not reject histogram operations in case of switching between partition charts', () => {
+      expect(
+        suggestions({
+          table: {
+            layerId: 'first',
+            isMultiRow: true,
+            columns: [
+              {
+                columnId: 'b',
+                operation: {
+                  label: 'Durations',
+                  dataType: 'number' as DataType,
+                  isBucketed: true,
+                  scale: 'interval',
+                },
+              },
+              {
+                columnId: 'c',
+                operation: { label: 'Count', dataType: 'number' as DataType, isBucketed: false },
+              },
+            ],
+            changeType: 'initial',
+          },
+          state: {
+            shape: 'mosaic',
+            layers: [{} as PieLayerState],
+          },
+          keptLayerIds: ['first'],
+        }).length
+      ).toBeGreaterThan(0);
+    });
+
     it('should reject when there are too many buckets', () => {
       expect(
         suggestions({
@@ -272,7 +304,7 @@ describe('suggestions', () => {
         state: undefined,
         keptLayerIds: ['first'],
       });
-      expect(currentSuggestions).toHaveLength(3);
+      expect(currentSuggestions).toHaveLength(4);
       expect(currentSuggestions.every((s) => s.hide)).toEqual(true);
     });
 
@@ -292,7 +324,7 @@ describe('suggestions', () => {
         state: undefined,
         keptLayerIds: ['first'],
       });
-      expect(currentSuggestions).toHaveLength(3);
+      expect(currentSuggestions).toHaveLength(4);
       expect(currentSuggestions.every((s) => s.hide)).toEqual(true);
     });
 
@@ -721,4 +753,172 @@ describe('suggestions', () => {
       );
     });
   });
+
+  describe('mosaic', () => {
+    it('should reject when currently active and unchanged data', () => {
+      expect(
+        suggestions({
+          table: {
+            layerId: 'first',
+            isMultiRow: true,
+            columns: [],
+            changeType: 'unchanged',
+          },
+          state: {
+            shape: 'mosaic',
+            layers: [
+              {
+                layerId: 'first',
+                layerType: layerTypes.DATA,
+                groups: [],
+                metric: 'a',
+
+                numberDisplay: 'hidden',
+                categoryDisplay: 'default',
+                legendDisplay: 'default',
+              },
+            ],
+          },
+          keptLayerIds: ['first'],
+        })
+      ).toHaveLength(0);
+    });
+
+    it('mosaic type should be added only in case of 2 groups', () => {
+      expect(
+        suggestions({
+          table: {
+            layerId: 'first',
+            isMultiRow: true,
+            columns: [
+              {
+                columnId: 'a',
+                operation: { label: 'Top 5', dataType: 'string' as DataType, isBucketed: true },
+              },
+              {
+                columnId: 'b',
+                operation: { label: 'Top 6', dataType: 'string' as DataType, isBucketed: true },
+              },
+              {
+                columnId: 'c',
+                operation: { label: 'Count', dataType: 'number' as DataType, isBucketed: false },
+              },
+            ],
+            changeType: 'unchanged',
+          },
+          state: {
+            shape: 'treemap',
+            layers: [
+              {
+                layerId: 'first',
+                layerType: layerTypes.DATA,
+                groups: ['a', 'b'],
+                metric: 'c',
+
+                numberDisplay: 'hidden',
+                categoryDisplay: 'inside',
+                legendDisplay: 'show',
+                percentDecimals: 0,
+                legendMaxLines: 1,
+                truncateLegend: true,
+                nestedLegend: true,
+              },
+            ],
+          },
+          keptLayerIds: ['first'],
+        }).filter(({ hide, state }) => !hide && state.shape === 'mosaic')
+      ).toMatchInlineSnapshot(`
+        Array [
+          Object {
+            "hide": false,
+            "previewIcon": "bullseye",
+            "score": 0.6,
+            "state": Object {
+              "layers": Array [
+                Object {
+                  "categoryDisplay": "default",
+                  "groups": Array [
+                    "a",
+                    "b",
+                  ],
+                  "layerId": "first",
+                  "layerType": "data",
+                  "legendDisplay": "show",
+                  "legendMaxLines": 1,
+                  "metric": "c",
+                  "nestedLegend": true,
+                  "numberDisplay": "hidden",
+                  "percentDecimals": 0,
+                  "truncateLegend": true,
+                },
+              ],
+              "palette": undefined,
+              "shape": "mosaic",
+            },
+            "title": "As Mosaic",
+          },
+        ]
+      `);
+    });
+
+    it('mosaic type should be added only in case of 2 groups (negative test)', () => {
+      const meta: Parameters<typeof suggestions>[0] = {
+        table: {
+          layerId: 'first',
+          isMultiRow: true,
+          columns: [
+            {
+              columnId: 'a',
+              operation: { label: 'Top 5', dataType: 'string' as DataType, isBucketed: true },
+            },
+            {
+              columnId: 'c',
+              operation: { label: 'Count', dataType: 'number' as DataType, isBucketed: false },
+            },
+          ],
+          changeType: 'unchanged',
+        },
+        state: {
+          shape: 'pie',
+          layers: [
+            {
+              layerId: 'first',
+              layerType: layerTypes.DATA,
+              groups: ['a', 'b'],
+              metric: 'c',
+
+              numberDisplay: 'hidden',
+              categoryDisplay: 'inside',
+              legendDisplay: 'show',
+              percentDecimals: 0,
+              legendMaxLines: 1,
+              truncateLegend: true,
+              nestedLegend: true,
+            },
+          ],
+        },
+        keptLayerIds: ['first'],
+      };
+
+      // test with 1 group
+      expect(
+        suggestions(meta).filter(({ hide, state }) => !hide && state.shape === 'mosaic')
+      ).toMatchInlineSnapshot(`Array []`);
+
+      meta.table.columns.push({
+        columnId: 'b',
+        operation: { label: 'Top 6', dataType: 'string' as DataType, isBucketed: true },
+      });
+
+      meta.table.columns.push({
+        columnId: 'c',
+        operation: { label: 'Top 7', dataType: 'string' as DataType, isBucketed: true },
+      });
+
+      // test with 3 groups
+      expect(
+        suggestions(meta).filter(({ hide, state }) => !hide && state.shape === 'mosaic')
+      ).toMatchInlineSnapshot(`Array []`);
+    });
+  });
 });
diff --git a/x-pack/plugins/lens/public/pie_visualization/suggestions.ts b/x-pack/plugins/lens/public/pie_visualization/suggestions.ts
index 9078e18588a2..30cd63752f42 100644
--- a/x-pack/plugins/lens/public/pie_visualization/suggestions.ts
+++ b/x-pack/plugins/lens/public/pie_visualization/suggestions.ts
@@ -7,17 +7,26 @@
 
 import { partition } from 'lodash';
 import { i18n } from '@kbn/i18n';
-import type { SuggestionRequest, VisualizationSuggestion } from '../types';
+import type { SuggestionRequest, TableSuggestionColumn, VisualizationSuggestion } from '../types';
 import { layerTypes } from '../../common';
 import type { PieVisualizationState } from '../../common/expressions';
-import { CHART_NAMES, MAX_PIE_BUCKETS, MAX_TREEMAP_BUCKETS } from './constants';
+import { CHART_NAMES, MAX_MOSAIC_BUCKETS, MAX_PIE_BUCKETS, MAX_TREEMAP_BUCKETS } from './constants';
+import { isPartitionShape, isTreemapOrMosaicShape } from './render_helpers';
+
+function hasIntervalScale(columns: TableSuggestionColumn[]) {
+  return columns.some((col) => col.operation.scale === 'interval');
+}
+
+function shouldReject({ table, keptLayerIds, state }: SuggestionRequest<PieVisualizationState>) {
+  // Histograms are not good for pi. But we should not reject them on switching between partition charts.
+  const shouldRejectIntervals =
+    state?.shape && isPartitionShape(state.shape) ? false : hasIntervalScale(table.columns);
 
-function shouldReject({ table, keptLayerIds }: SuggestionRequest<PieVisualizationState>) {
   return (
     keptLayerIds.length > 1 ||
     (keptLayerIds.length && table.layerId !== keptLayerIds[0]) ||
     table.changeType === 'reorder' ||
-    table.columns.some((col) => col.operation.scale === 'interval') // Histograms are not good for pie
+    shouldRejectIntervals
   );
 }
 
@@ -52,7 +61,7 @@ export function suggestions({
 
   const results: Array<VisualizationSuggestion<PieVisualizationState>> = [];
 
-  if (groups.length <= MAX_PIE_BUCKETS && subVisualizationId !== 'treemap') {
+  if (groups.length <= MAX_PIE_BUCKETS && !isTreemapOrMosaicShape(subVisualizationId!)) {
     let newShape: PieVisualizationState['shape'] =
       (subVisualizationId as PieVisualizationState['shape']) || 'donut';
     if (groups.length !== 1 && !subVisualizationId) {
@@ -65,7 +74,7 @@ export function suggestions({
         values: { chartName: CHART_NAMES[newShape].label },
         description: 'chartName is already translated',
       }),
-      score: state && state.shape !== 'treemap' ? 0.6 : 0.4,
+      score: state && !isTreemapOrMosaicShape(state.shape) ? 0.6 : 0.4,
       state: {
         shape: newShape,
         palette: mainPalette || state?.palette,
@@ -92,7 +101,10 @@ export function suggestions({
       },
       previewIcon: 'bullseye',
       // dont show suggestions for same type
-      hide: table.changeType === 'reduced' || (state && state.shape !== 'treemap'),
+      hide:
+        table.changeType === 'reduced' ||
+        hasIntervalScale(groups) ||
+        (state && !isTreemapOrMosaicShape(state.shape)),
     };
 
     results.push(baseSuggestion);
@@ -153,7 +165,54 @@ export function suggestions({
       },
       previewIcon: 'bullseye',
       // hide treemap suggestions from bottom bar, but keep them for chart switcher
-      hide: table.changeType === 'reduced' || !state || (state && state.shape === 'treemap'),
+      hide:
+        table.changeType === 'reduced' ||
+        !state ||
+        hasIntervalScale(groups) ||
+        (state && state.shape === 'treemap'),
+    });
+  }
+
+  if (
+    groups.length <= MAX_MOSAIC_BUCKETS &&
+    (!subVisualizationId || subVisualizationId === 'mosaic')
+  ) {
+    results.push({
+      title: i18n.translate('xpack.lens.pie.mosaicSuggestionLabel', {
+        defaultMessage: 'As Mosaic',
+      }),
+      score: state?.shape === 'mosaic' ? 0.7 : 0.5,
+      state: {
+        shape: 'mosaic',
+        palette: mainPalette || state?.palette,
+        layers: [
+          state?.layers[0]
+            ? {
+                ...state.layers[0],
+                layerId: table.layerId,
+                groups: groups.map((col) => col.columnId),
+                metric: metricColumnId,
+                categoryDisplay: 'default',
+                layerType: layerTypes.DATA,
+              }
+            : {
+                layerId: table.layerId,
+                groups: groups.map((col) => col.columnId),
+                metric: metricColumnId,
+                numberDisplay: 'percent',
+                categoryDisplay: 'default',
+                legendDisplay: 'default',
+                nestedLegend: false,
+                layerType: layerTypes.DATA,
+              },
+        ],
+      },
+      previewIcon: 'bullseye',
+      hide:
+        groups.length !== 2 ||
+        table.changeType === 'reduced' ||
+        hasIntervalScale(groups) ||
+        (state && state.shape === 'mosaic'),
     });
   }
 
diff --git a/x-pack/plugins/lens/public/pie_visualization/toolbar.tsx b/x-pack/plugins/lens/public/pie_visualization/toolbar.tsx
index 685a8392dcfd..23003a4ec340 100644
--- a/x-pack/plugins/lens/public/pie_visualization/toolbar.tsx
+++ b/x-pack/plugins/lens/public/pie_visualization/toolbar.tsx
@@ -17,7 +17,7 @@ import {
 } from '@elastic/eui';
 import type { Position } from '@elastic/charts';
 import type { PaletteRegistry } from 'src/plugins/charts/public';
-import { DEFAULT_PERCENT_DECIMALS } from './constants';
+import { DEFAULT_PERCENT_DECIMALS, CHART_NAMES } from './constants';
 import type { PieVisualizationState, SharedPieLayerState } from '../../common/expressions';
 import { VisualizationDimensionEditorProps, VisualizationToolbarProps } from '../types';
 import { ToolbarPopover, LegendSettingsPopover, useDebouncedValue } from '../shared_components';
@@ -47,48 +47,6 @@ const numberOptions: Array<{
   },
 ];
 
-const categoryOptions: Array<{
-  value: SharedPieLayerState['categoryDisplay'];
-  inputDisplay: string;
-}> = [
-  {
-    value: 'default',
-    inputDisplay: i18n.translate('xpack.lens.pieChart.showCategoriesLabel', {
-      defaultMessage: 'Inside or outside',
-    }),
-  },
-  {
-    value: 'inside',
-    inputDisplay: i18n.translate('xpack.lens.pieChart.fitInsideOnlyLabel', {
-      defaultMessage: 'Inside only',
-    }),
-  },
-  {
-    value: 'hide',
-    inputDisplay: i18n.translate('xpack.lens.pieChart.categoriesInLegendLabel', {
-      defaultMessage: 'Hide labels',
-    }),
-  },
-];
-
-const categoryOptionsTreemap: Array<{
-  value: SharedPieLayerState['categoryDisplay'];
-  inputDisplay: string;
-}> = [
-  {
-    value: 'default',
-    inputDisplay: i18n.translate('xpack.lens.pieChart.showTreemapCategoriesLabel', {
-      defaultMessage: 'Show labels',
-    }),
-  },
-  {
-    value: 'hide',
-    inputDisplay: i18n.translate('xpack.lens.pieChart.categoriesInLegendLabel', {
-      defaultMessage: 'Hide labels',
-    }),
-  },
-];
-
 const legendOptions: Array<{
   value: SharedPieLayerState['legendDisplay'];
   label: string;
@@ -133,25 +91,27 @@ export function PieToolbar(props: VisualizationToolbarProps<PieVisualizationStat
         groupPosition="left"
         buttonDataTestSubj="lnsLabelsButton"
       >
-        <EuiFormRow
-          label={i18n.translate('xpack.lens.pieChart.labelPositionLabel', {
-            defaultMessage: 'Position',
-          })}
-          fullWidth
-          display="columnCompressed"
-        >
-          <EuiSuperSelect
-            compressed
-            valueOfSelected={layer.categoryDisplay}
-            options={state.shape === 'treemap' ? categoryOptionsTreemap : categoryOptions}
-            onChange={(option) => {
-              setState({
-                ...state,
-                layers: [{ ...layer, categoryDisplay: option }],
-              });
-            }}
-          />
-        </EuiFormRow>
+        {state.shape && CHART_NAMES[state.shape].categoryOptions.length ? (
+          <EuiFormRow
+            label={i18n.translate('xpack.lens.pieChart.labelPositionLabel', {
+              defaultMessage: 'Position',
+            })}
+            fullWidth
+            display="columnCompressed"
+          >
+            <EuiSuperSelect
+              compressed
+              valueOfSelected={layer.categoryDisplay}
+              options={CHART_NAMES[state.shape].categoryOptions}
+              onChange={(option) => {
+                setState({
+                  ...state,
+                  layers: [{ ...layer, categoryDisplay: option }],
+                });
+              }}
+            />
+          </EuiFormRow>
+        ) : null}
         <EuiFormRow
           label={i18n.translate('xpack.lens.pieChart.numberLabels', {
             defaultMessage: 'Values',
diff --git a/x-pack/plugins/lens/public/pie_visualization/visualization.tsx b/x-pack/plugins/lens/public/pie_visualization/visualization.tsx
index ea89ef0bfb85..f72a6e5bef11 100644
--- a/x-pack/plugins/lens/public/pie_visualization/visualization.tsx
+++ b/x-pack/plugins/lens/public/pie_visualization/visualization.tsx
@@ -10,7 +10,12 @@ import { render } from 'react-dom';
 import { i18n } from '@kbn/i18n';
 import { FormattedMessage, I18nProvider } from '@kbn/i18n/react';
 import type { PaletteRegistry } from 'src/plugins/charts/public';
-import type { Visualization, OperationMetadata, AccessorConfig } from '../types';
+import type {
+  Visualization,
+  OperationMetadata,
+  AccessorConfig,
+  VisualizationDimensionGroupConfig,
+} from '../types';
 import { toExpression, toPreviewExpression } from './to_expression';
 import type { PieLayerState, PieVisualizationState } from '../../common/expressions';
 import { layerTypes } from '../../common';
@@ -35,6 +40,24 @@ const bucketedOperations = (op: OperationMetadata) => op.isBucketed;
 const numberMetricOperations = (op: OperationMetadata) =>
   !op.isBucketed && op.dataType === 'number';
 
+const applyPaletteToColumnConfig = (
+  columns: AccessorConfig[],
+  { shape, palette }: PieVisualizationState,
+  paletteService: PaletteRegistry
+) => {
+  const colorPickerIndex = shape === 'mosaic' ? columns.length - 1 : 0;
+
+  if (colorPickerIndex >= 0) {
+    columns[colorPickerIndex] = {
+      columnId: columns[colorPickerIndex].columnId,
+      triggerIcon: 'colorBy',
+      palette: paletteService
+        .get(palette?.name || 'default')
+        .getCategoricalColors(10, palette?.params),
+    };
+  }
+};
+
 export const getPieVisualization = ({
   paletteService,
 }: {
@@ -61,6 +84,13 @@ export const getPieVisualization = ({
       label: CHART_NAMES.treemap.label,
       groupLabel: CHART_NAMES.treemap.groupLabel,
     },
+    {
+      id: 'mosaic',
+      icon: CHART_NAMES.mosaic.icon,
+      label: CHART_NAMES.mosaic.label,
+      showExperimentalBadge: true,
+      groupLabel: CHART_NAMES.mosaic.groupLabel,
+    },
   ],
 
   getVisualizationTypeId(state) {
@@ -79,13 +109,7 @@ export const getPieVisualization = ({
   },
 
   getDescription(state) {
-    if (state.shape === 'treemap') {
-      return CHART_NAMES.treemap;
-    }
-    if (state.shape === 'donut') {
-      return CHART_NAMES.donut;
-    }
-    return CHART_NAMES.pie;
+    return CHART_NAMES[state.shape] ?? CHART_NAMES.pie;
   },
 
   switchVisualizationType: (visualizationTypeId, state) => ({
@@ -122,76 +146,58 @@ export const getPieVisualization = ({
     const sortedColumns: AccessorConfig[] = Array.from(
       new Set(originalOrder.concat(layer.groups))
     ).map((accessor) => ({ columnId: accessor }));
-    if (sortedColumns.length > 0) {
-      sortedColumns[0] = {
-        columnId: sortedColumns[0].columnId,
-        triggerIcon: 'colorBy',
-        palette: paletteService
-          .get(state.palette?.name || 'default')
-          .getCategoricalColors(10, state.palette?.params),
-      };
+
+    if (sortedColumns.length) {
+      applyPaletteToColumnConfig(sortedColumns, state, paletteService);
     }
 
-    if (state.shape === 'treemap') {
-      return {
-        groups: [
-          {
-            groupId: 'groups',
+    const getSliceByGroup = (): VisualizationDimensionGroupConfig => {
+      const baseProps = {
+        required: true,
+        groupId: 'groups',
+        accessors: sortedColumns,
+        enableDimensionEditor: true,
+        filterOperations: bucketedOperations,
+      };
+
+      switch (state.shape) {
+        case 'mosaic':
+        case 'treemap':
+          return {
+            ...baseProps,
             groupLabel: i18n.translate('xpack.lens.pie.treemapGroupLabel', {
               defaultMessage: 'Group by',
             }),
-            layerId,
-            accessors: sortedColumns,
             supportsMoreColumns: sortedColumns.length < MAX_TREEMAP_BUCKETS,
-            filterOperations: bucketedOperations,
-            required: true,
             dataTestSubj: 'lnsPie_groupByDimensionPanel',
-            enableDimensionEditor: true,
-          },
-          {
-            groupId: 'metric',
-            groupLabel: i18n.translate('xpack.lens.pie.groupsizeLabel', {
-              defaultMessage: 'Size by',
+            requiredMinDimensionCount: state.shape === 'mosaic' ? 2 : undefined,
+          };
+        default:
+          return {
+            ...baseProps,
+            groupLabel: i18n.translate('xpack.lens.pie.sliceGroupLabel', {
+              defaultMessage: 'Slice by',
             }),
-            layerId,
-            accessors: layer.metric ? [{ columnId: layer.metric }] : [],
-            supportsMoreColumns: !layer.metric,
-            filterOperations: numberMetricOperations,
-            required: true,
-            dataTestSubj: 'lnsPie_sizeByDimensionPanel',
-          },
-        ],
-      };
-    }
+            supportsMoreColumns: sortedColumns.length < MAX_PIE_BUCKETS,
+            dataTestSubj: 'lnsPie_sliceByDimensionPanel',
+          };
+      }
+    };
+
+    const getMetricGroup = (): VisualizationDimensionGroupConfig => ({
+      groupId: 'metric',
+      groupLabel: i18n.translate('xpack.lens.pie.groupsizeLabel', {
+        defaultMessage: 'Size by',
+      }),
+      accessors: layer.metric ? [{ columnId: layer.metric }] : [],
+      supportsMoreColumns: !layer.metric,
+      filterOperations: numberMetricOperations,
+      required: true,
+      dataTestSubj: 'lnsPie_sizeByDimensionPanel',
+    });
 
     return {
-      groups: [
-        {
-          groupId: 'groups',
-          groupLabel: i18n.translate('xpack.lens.pie.sliceGroupLabel', {
-            defaultMessage: 'Slice by',
-          }),
-          layerId,
-          accessors: sortedColumns,
-          supportsMoreColumns: sortedColumns.length < MAX_PIE_BUCKETS,
-          filterOperations: bucketedOperations,
-          required: true,
-          dataTestSubj: 'lnsPie_sliceByDimensionPanel',
-          enableDimensionEditor: true,
-        },
-        {
-          groupId: 'metric',
-          groupLabel: i18n.translate('xpack.lens.pie.groupsizeLabel', {
-            defaultMessage: 'Size by',
-          }),
-          layerId,
-          accessors: layer.metric ? [{ columnId: layer.metric }] : [],
-          supportsMoreColumns: !layer.metric,
-          filterOperations: numberMetricOperations,
-          required: true,
-          dataTestSubj: 'lnsPie_sizeByDimensionPanel',
-        },
-      ],
+      groups: [getSliceByGroup(), getMetricGroup()],
     };
   },
 
diff --git a/x-pack/plugins/lens/public/types.ts b/x-pack/plugins/lens/public/types.ts
index a9a953906465..ab6f1d8d5508 100644
--- a/x-pack/plugins/lens/public/types.ts
+++ b/x-pack/plugins/lens/public/types.ts
@@ -470,6 +470,7 @@ export type VisualizationDimensionGroupConfig = SharedDimensionProps & {
   supportsMoreColumns: boolean;
   /** If required, a warning will appear if accessors are empty */
   required?: boolean;
+  requiredMinDimensionCount?: number;
   dataTestSubj?: string;
 
   /**
diff --git a/x-pack/plugins/ml/common/index.ts b/x-pack/plugins/ml/common/index.ts
index ad9927a533a6..ea8ad43d6bb3 100644
--- a/x-pack/plugins/ml/common/index.ts
+++ b/x-pack/plugins/ml/common/index.ts
@@ -14,3 +14,4 @@ export { composeValidators, patternValidator } from './util/validators';
 export { isRuntimeMappings, isRuntimeField } from './util/runtime_field_utils';
 export { extractErrorMessage } from './util/errors';
 export type { RuntimeMappings } from './types/fields';
+export { getDefaultCapabilities as getDefaultMlCapabilities } from './types/capabilities';
diff --git a/x-pack/plugins/ml/common/types/capabilities.ts b/x-pack/plugins/ml/common/types/capabilities.ts
index ed0f3595cb94..36377aaa1ed3 100644
--- a/x-pack/plugins/ml/common/types/capabilities.ts
+++ b/x-pack/plugins/ml/common/types/capabilities.ts
@@ -63,6 +63,8 @@ export const adminMlCapabilities = {
   // Alerts
   canCreateMlAlerts: false,
   canUseMlAlerts: false,
+  // Model management
+  canViewMlNodes: false,
 };
 
 export type UserMlCapabilities = typeof userMlCapabilities;
diff --git a/x-pack/plugins/ml/common/types/trained_models.ts b/x-pack/plugins/ml/common/types/trained_models.ts
index 89b8a50846cb..0670849f07f2 100644
--- a/x-pack/plugins/ml/common/types/trained_models.ts
+++ b/x-pack/plugins/ml/common/types/trained_models.ts
@@ -151,6 +151,8 @@ export interface TrainedModelDeploymentStatsResponse {
     routing_state: { routing_state: string };
     average_inference_time_ms: number;
     last_access: number;
+    number_of_pending_requests: number;
+    start_time: number;
   }>;
 }
 
@@ -161,11 +163,18 @@ export interface AllocatedModel {
     state: string;
     allocation_count: number;
   };
-  model_id: string;
+  /**
+   * Not required for rendering in the Model stats
+   */
+  model_id?: string;
   state: string;
   model_threads: number;
   model_size_bytes: number;
   node: {
+    /**
+     * Not required for rendering in the Nodes overview
+     */
+    name?: string;
     average_inference_time_ms: number;
     inference_count: number;
     routing_state: {
@@ -173,13 +182,14 @@ export interface AllocatedModel {
       reason?: string;
     };
     last_access?: number;
+    number_of_pending_requests: number;
+    start_time: number;
   };
 }
 
 export interface NodeDeploymentStatsResponse {
   id: string;
   name: string;
-  transport_address: string;
   attributes: Record<string, string>;
   roles: string[];
   allocated_models: AllocatedModel[];
diff --git a/x-pack/plugins/ml/public/application/components/help_icon/help_icon.tsx b/x-pack/plugins/ml/public/application/components/help_icon/help_icon.tsx
new file mode 100644
index 000000000000..5ab4fd4de5dd
--- /dev/null
+++ b/x-pack/plugins/ml/public/application/components/help_icon/help_icon.tsx
@@ -0,0 +1,23 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License
+ * 2.0; you may not use this file except in compliance with the Elastic License
+ * 2.0.
+ */
+
+import React, { FC, ReactNode } from 'react';
+import { EuiIcon, EuiToolTip } from '@elastic/eui';
+
+export const HelpIcon: FC<{ content: ReactNode | string }> = ({ content }) => {
+  return (
+    <EuiToolTip position="top" content={content}>
+      <EuiIcon
+        tabIndex={0}
+        type="questionInCircle"
+        color={'subdued'}
+        className="eui-alignTop"
+        size="s"
+      />
+    </EuiToolTip>
+  );
+};
diff --git a/x-pack/plugins/ml/public/application/components/help_icon/index.tsx b/x-pack/plugins/ml/public/application/components/help_icon/index.tsx
new file mode 100644
index 000000000000..712f457da47c
--- /dev/null
+++ b/x-pack/plugins/ml/public/application/components/help_icon/index.tsx
@@ -0,0 +1,8 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License
+ * 2.0; you may not use this file except in compliance with the Elastic License
+ * 2.0.
+ */
+
+export { HelpIcon } from './help_icon';
diff --git a/x-pack/plugins/ml/public/application/services/ml_api_service/trained_models.ts b/x-pack/plugins/ml/public/application/services/ml_api_service/trained_models.ts
index c483b0a23c2d..822c5059982e 100644
--- a/x-pack/plugins/ml/public/application/services/ml_api_service/trained_models.ts
+++ b/x-pack/plugins/ml/public/application/services/ml_api_service/trained_models.ts
@@ -15,7 +15,6 @@ import {
   ModelPipelines,
   TrainedModelStat,
   NodesOverviewResponse,
-  TrainedModelDeploymentStatsResponse,
 } from '../../../../common/types/trained_models';
 
 export interface InferenceQueryParams {
@@ -122,21 +121,6 @@ export function trainedModelsApiProvider(httpService: HttpService) {
       });
     },
 
-    getTrainedModelDeploymentStats(modelId?: string | string[]) {
-      let model = modelId ?? '*';
-      if (Array.isArray(modelId)) {
-        model = modelId.join(',');
-      }
-
-      return httpService.http<{
-        count: number;
-        deployment_stats: TrainedModelDeploymentStatsResponse[];
-      }>({
-        path: `${apiBasePath}/trained_models/${model}/deployment/_stats`,
-        method: 'GET',
-      });
-    },
-
     getTrainedModelsNodesOverview() {
       return httpService.http<NodesOverviewResponse>({
         path: `${apiBasePath}/trained_models/nodes_overview`,
diff --git a/x-pack/plugins/ml/public/application/trained_models/models_management/expanded_row.tsx b/x-pack/plugins/ml/public/application/trained_models/models_management/expanded_row.tsx
index 6dd7db1dbb7b..469973a378c8 100644
--- a/x-pack/plugins/ml/public/application/trained_models/models_management/expanded_row.tsx
+++ b/x-pack/plugins/ml/public/application/trained_models/models_management/expanded_row.tsx
@@ -5,53 +5,50 @@
  * 2.0.
  */
 
-import React, { FC, Fragment, useEffect, useState } from 'react';
-import { omit } from 'lodash';
+import React, { FC, useEffect, useState } from 'react';
+import { omit, pick } from 'lodash';
 import {
   EuiBadge,
-  EuiButtonEmpty,
   EuiCodeBlock,
   EuiDescriptionList,
   EuiFlexGrid,
-  EuiFlexGroup,
   EuiFlexItem,
-  EuiHorizontalRule,
-  EuiListGroup,
   EuiNotificationBadge,
   EuiPanel,
   EuiSpacer,
   EuiTabbedContent,
-  EuiText,
-  EuiTextColor,
   EuiTitle,
 } from '@elastic/eui';
-import { EuiDescriptionListProps } from '@elastic/eui/src/components/description_list/description_list';
+import type { EuiDescriptionListProps } from '@elastic/eui/src/components/description_list/description_list';
 import { FormattedMessage } from '@kbn/i18n/react';
-import type { EuiListGroupItemProps } from '@elastic/eui/src/components/list_group/list_group_item';
-import { ModelItemFull } from './models_list';
-import { useMlKibana, useMlLocator } from '../../contexts/kibana';
+import type { ModelItemFull } from './models_list';
 import { timeFormatter } from '../../../../common/util/date_utils';
 import { isDefined } from '../../../../common/types/guards';
 import { isPopulatedObject } from '../../../../common';
-import { ML_PAGES } from '../../../../common/constants/locator';
+import { ModelPipelines } from './pipelines';
+import { AllocatedModels } from '../nodes_overview/allocated_models';
+import type { AllocatedModel } from '../../../../common/types/trained_models';
 
 interface ExpandedRowProps {
   item: ModelItemFull;
 }
 
+const badgeFormatter = (items: string[]) => {
+  if (items.length === 0) return;
+  return (
+    <div>
+      {items.map((item) => (
+        <EuiBadge key={item} color="hollow">
+          {item}
+        </EuiBadge>
+      ))}
+    </div>
+  );
+};
+
 const formatterDictionary: Record<string, (value: any) => JSX.Element | string | undefined> = {
-  tags: (tags: string[]) => {
-    if (tags.length === 0) return;
-    return (
-      <div>
-        {tags.map((tag) => (
-          <EuiBadge key={tag} color="hollow">
-            {tag}
-          </EuiBadge>
-        ))}
-      </div>
-    );
-  },
+  tags: badgeFormatter,
+  roles: badgeFormatter,
   create_time: timeFormatter,
   timestamp: timeFormatter,
 };
@@ -89,11 +86,7 @@ export function formatToListItems(
 }
 
 export const ExpandedRow: FC<ExpandedRowProps> = ({ item }) => {
-  const mlLocator = useMlLocator();
-
-  const [deploymentStatsItems, setDeploymentStats] = useState<EuiDescriptionListProps['listItems']>(
-    []
-  );
+  const [modelItems, setModelItems] = useState<AllocatedModel[]>([]);
 
   const {
     inference_config: inferenceConfig,
@@ -125,41 +118,32 @@ export const ExpandedRow: FC<ExpandedRowProps> = ({ item }) => {
     license_level,
   };
 
-  const {
-    services: { share },
-  } = useMlKibana();
-
   useEffect(
-    function updateDeploymentState() {
+    function updateModelItems() {
       (async function () {
-        const { nodes, ...deploymentStats } = stats.deployment_stats ?? {};
-
-        if (!isPopulatedObject(deploymentStats)) return;
+        const deploymentStats = stats.deployment_stats;
 
-        const result = formatToListItems(deploymentStats)!;
+        if (!deploymentStats) return;
 
-        const items: EuiListGroupItemProps[] = await Promise.all(
-          nodes!.map(async (v) => {
-            const nodeObject = Object.values(v.node)[0];
-            const href = await mlLocator!.getUrl({
-              page: ML_PAGES.TRAINED_MODELS_NODES,
-              pageState: {
-                nodeId: nodeObject.name,
-              },
-            });
-            return {
-              label: nodeObject.name,
-              href,
-            };
-          })
-        );
-
-        result.push({
-          title: 'nodes',
-          description: <EuiListGroup size={'s'} gutterSize={'s'} listItems={items} />,
+        const items: AllocatedModel[] = deploymentStats.nodes.map((n) => {
+          const nodeName = Object.values(n.node)[0].name;
+          return {
+            ...deploymentStats,
+            node: {
+              ...pick(n, [
+                'average_inference_time_ms',
+                'inference_count',
+                'routing_state',
+                'last_access',
+                'number_of_pending_requests',
+                'start_time',
+              ]),
+              name: nodeName,
+            } as AllocatedModel['node'],
+          };
         });
 
-        setDeploymentStats(result);
+        setModelItems(items);
       })();
     },
     [stats.deployment_stats]
@@ -176,7 +160,7 @@ export const ExpandedRow: FC<ExpandedRowProps> = ({ item }) => {
       ),
       content: (
         <>
-          <EuiSpacer size={'m'} />
+          <EuiSpacer size={'s'} />
           <EuiFlexGrid columns={2} gutterSize={'m'}>
             <EuiFlexItem>
               <EuiPanel>
@@ -232,7 +216,7 @@ export const ExpandedRow: FC<ExpandedRowProps> = ({ item }) => {
             ),
             content: (
               <>
-                <EuiSpacer size={'m'} />
+                <EuiSpacer size={'s'} />
                 <EuiFlexGrid columns={2} gutterSize={'m'}>
                   <EuiFlexItem>
                     <EuiPanel>
@@ -280,7 +264,7 @@ export const ExpandedRow: FC<ExpandedRowProps> = ({ item }) => {
           },
         ]
       : []),
-    ...(isPopulatedObject(omit(stats, 'pipeline_count'))
+    ...(isPopulatedObject(omit(stats, ['pipeline_count', 'ingest']))
       ? [
           {
             id: 'stats',
@@ -292,57 +276,33 @@ export const ExpandedRow: FC<ExpandedRowProps> = ({ item }) => {
             ),
             content: (
               <>
-                <EuiSpacer size={'m'} />
-                {!!deploymentStatsItems?.length ? (
-                  <>
-                    <EuiPanel>
-                      <EuiTitle size={'xs'}>
-                        <h5>
-                          <FormattedMessage
-                            id="xpack.ml.trainedModels.modelsList.expandedRow.deploymentStatsTitle"
-                            defaultMessage="Deployment stats"
-                          />
-                        </h5>
-                      </EuiTitle>
-                      <EuiSpacer size={'m'} />
-                      <EuiDescriptionList
-                        compressed={true}
-                        type="column"
-                        listItems={deploymentStatsItems}
-                      />
-                    </EuiPanel>
-                    <EuiSpacer size={'m'} />
-                  </>
-                ) : null}
-                <EuiFlexGrid columns={2}>
-                  {stats.inference_stats && (
-                    <EuiFlexItem>
+                <EuiSpacer size={'s'} />
+
+                <EuiFlexGrid columns={2} gutterSize={'m'}>
+                  {!!modelItems?.length ? (
+                    <EuiFlexItem grow={2}>
                       <EuiPanel>
                         <EuiTitle size={'xs'}>
                           <h5>
                             <FormattedMessage
-                              id="xpack.ml.trainedModels.modelsList.expandedRow.inferenceStatsTitle"
-                              defaultMessage="Inference stats"
+                              id="xpack.ml.trainedModels.modelsList.expandedRow.deploymentStatsTitle"
+                              defaultMessage="Deployment stats"
                             />
                           </h5>
                         </EuiTitle>
                         <EuiSpacer size={'m'} />
-                        <EuiDescriptionList
-                          compressed={true}
-                          type="column"
-                          listItems={formatToListItems(stats.inference_stats)}
-                        />
+                        <AllocatedModels models={modelItems} hideColumns={['model_id']} />
                       </EuiPanel>
                     </EuiFlexItem>
-                  )}
-                  {stats.ingest?.total && (
+                  ) : null}
+                  {stats.inference_stats ? (
                     <EuiFlexItem>
-                      <EuiPanel style={{ maxHeight: '400px', overflow: 'auto' }}>
+                      <EuiPanel>
                         <EuiTitle size={'xs'}>
                           <h5>
                             <FormattedMessage
-                              id="xpack.ml.trainedModels.modelsList.expandedRow.ingestStatsTitle"
-                              defaultMessage="Ingest stats"
+                              id="xpack.ml.trainedModels.modelsList.expandedRow.inferenceStatsTitle"
+                              defaultMessage="Inference stats"
                             />
                           </h5>
                         </EuiTitle>
@@ -350,99 +310,18 @@ export const ExpandedRow: FC<ExpandedRowProps> = ({ item }) => {
                         <EuiDescriptionList
                           compressed={true}
                           type="column"
-                          listItems={formatToListItems(stats.ingest.total)}
+                          listItems={formatToListItems(stats.inference_stats)}
                         />
-
-                        {stats.ingest?.pipelines && (
-                          <>
-                            <EuiSpacer size={'m'} />
-                            <EuiTitle size={'xs'}>
-                              <h5>
-                                <FormattedMessage
-                                  id="xpack.ml.trainedModels.modelsList.expandedRow.byPipelineTitle"
-                                  defaultMessage="By pipeline"
-                                />
-                              </h5>
-                            </EuiTitle>
-                            <EuiSpacer size={'s'} />
-                            {Object.entries(stats.ingest.pipelines).map(
-                              ([pipelineName, { processors, ...pipelineStats }], i) => {
-                                return (
-                                  <Fragment key={pipelineName}>
-                                    <EuiFlexGroup>
-                                      <EuiFlexItem grow={false}>
-                                        <EuiTitle size={'xs'}>
-                                          <EuiTextColor color="subdued">
-                                            <h5>
-                                              {i + 1}. {pipelineName}
-                                            </h5>
-                                          </EuiTextColor>
-                                        </EuiTitle>
-                                      </EuiFlexItem>
-                                      <EuiFlexItem>
-                                        <EuiHorizontalRule size={'full'} margin={'s'} />
-                                      </EuiFlexItem>
-                                    </EuiFlexGroup>
-                                    <EuiSpacer size={'m'} />
-                                    <EuiDescriptionList
-                                      compressed={true}
-                                      type="column"
-                                      listItems={formatToListItems(pipelineStats)}
-                                    />
-                                    <EuiSpacer size={'m'} />
-                                    <EuiTitle size={'xxs'}>
-                                      <h6>
-                                        <FormattedMessage
-                                          id="xpack.ml.trainedModels.modelsList.expandedRow.byProcessorTitle"
-                                          defaultMessage="By processor"
-                                        />
-                                      </h6>
-                                    </EuiTitle>
-                                    <EuiSpacer size={'s'} />
-                                    <>
-                                      {processors.map((processor) => {
-                                        const name = Object.keys(processor)[0];
-                                        const { stats: processorStats } = processor[name];
-                                        return (
-                                          <Fragment key={name}>
-                                            <EuiFlexGroup>
-                                              <EuiFlexItem grow={false}>
-                                                <EuiTitle size={'xxs'}>
-                                                  <EuiTextColor color="subdued">
-                                                    <h6>{name}</h6>
-                                                  </EuiTextColor>
-                                                </EuiTitle>
-                                              </EuiFlexItem>
-                                              <EuiFlexItem>
-                                                <EuiHorizontalRule size={'full'} margin={'s'} />
-                                              </EuiFlexItem>
-                                            </EuiFlexGroup>
-                                            <EuiSpacer size={'m'} />
-                                            <EuiDescriptionList
-                                              compressed={true}
-                                              type="column"
-                                              listItems={formatToListItems(processorStats)}
-                                            />
-                                          </Fragment>
-                                        );
-                                      })}
-                                    </>
-                                  </Fragment>
-                                );
-                              }
-                            )}
-                          </>
-                        )}
                       </EuiPanel>
                     </EuiFlexItem>
-                  )}
+                  ) : null}
                 </EuiFlexGrid>
               </>
             ),
           },
         ]
       : []),
-    ...(pipelines && Object.keys(pipelines).length > 0
+    ...((pipelines && Object.keys(pipelines).length > 0) || stats.ingest
       ? [
           {
             id: 'pipelines',
@@ -457,66 +336,8 @@ export const ExpandedRow: FC<ExpandedRowProps> = ({ item }) => {
             ),
             content: (
               <>
-                <EuiSpacer size={'m'} />
-                <EuiFlexGrid columns={2} gutterSize={'m'}>
-                  {Object.entries(pipelines).map(
-                    ([pipelineName, { processors, description: pipelineDescription }]) => {
-                      return (
-                        <EuiFlexItem key={pipelineName}>
-                          <EuiPanel>
-                            <EuiFlexGroup alignItems="center" justifyContent="spaceBetween">
-                              <EuiFlexItem grow={false}>
-                                <EuiTitle size={'xs'}>
-                                  <h5>{pipelineName}</h5>
-                                </EuiTitle>
-                              </EuiFlexItem>
-                              <EuiFlexItem grow={false}>
-                                <EuiButtonEmpty
-                                  onClick={() => {
-                                    const locator = share.url.locators.get(
-                                      'INGEST_PIPELINES_APP_LOCATOR'
-                                    );
-                                    if (!locator) return;
-                                    locator.navigate({
-                                      page: 'pipeline_edit',
-                                      pipelineId: pipelineName,
-                                      absolute: true,
-                                    });
-                                  }}
-                                >
-                                  <FormattedMessage
-                                    id="xpack.ml.trainedModels.modelsList.expandedRow.editPipelineLabel"
-                                    defaultMessage="Edit"
-                                  />
-                                </EuiButtonEmpty>
-                              </EuiFlexItem>
-                            </EuiFlexGroup>
-
-                            {pipelineDescription && <EuiText>{pipelineDescription}</EuiText>}
-                            <EuiSpacer size={'m'} />
-                            <EuiTitle size={'xxs'}>
-                              <h6>
-                                <FormattedMessage
-                                  id="xpack.ml.trainedModels.modelsList.expandedRow.processorsTitle"
-                                  defaultMessage="Processors"
-                                />
-                              </h6>
-                            </EuiTitle>
-                            <EuiCodeBlock
-                              language="json"
-                              fontSize="m"
-                              paddingSize="m"
-                              overflowHeight={300}
-                              isCopyable
-                            >
-                              {JSON.stringify(processors, null, 2)}
-                            </EuiCodeBlock>
-                          </EuiPanel>
-                        </EuiFlexItem>
-                      );
-                    }
-                  )}
-                </EuiFlexGrid>
+                <EuiSpacer size={'s'} />
+                <ModelPipelines pipelines={pipelines!} ingestStats={stats.ingest} />
               </>
             ),
           },
diff --git a/x-pack/plugins/ml/public/application/trained_models/models_management/models_list.tsx b/x-pack/plugins/ml/public/application/trained_models/models_management/models_list.tsx
index 9c3cc1f93a9c..ce0e47df292d 100644
--- a/x-pack/plugins/ml/public/application/trained_models/models_management/models_list.tsx
+++ b/x-pack/plugins/ml/public/application/trained_models/models_management/models_list.tsx
@@ -6,7 +6,6 @@
  */
 
 import React, { FC, useCallback, useEffect, useMemo, useState } from 'react';
-import { omit } from 'lodash';
 import {
   EuiBadge,
   EuiButton,
@@ -153,9 +152,7 @@ export const ModelsList: FC = () => {
       }
 
       // Need to fetch state for 3rd party models to enable/disable actions
-      await fetchAndPopulateDeploymentStats(
-        newItems.filter((v) => v.model_type.includes('pytorch'))
-      );
+      await fetchModelsStats(newItems.filter((v) => v.model_type.includes('pytorch')));
 
       setItems(newItems);
 
@@ -237,39 +234,6 @@ export const ModelsList: FC = () => {
     }
   }, []);
 
-  /**
-   * Updates model items with deployment stats;
-   *
-   * We have to fetch all deployment stats on each update,
-   * because for stopped models the API returns 404 response.
-   */
-  const fetchAndPopulateDeploymentStats = useCallback(async (modelItems: ModelItem[]) => {
-    try {
-      const { deployment_stats: deploymentStats } =
-        await trainedModelsApiService.getTrainedModelDeploymentStats('*');
-
-      for (const deploymentStat of deploymentStats) {
-        const deployedModel = modelItems.find(
-          (model) => model.model_id === deploymentStat.model_id
-        );
-
-        if (deployedModel) {
-          deployedModel.stats = {
-            ...(deployedModel.stats ?? {}),
-            deployment_stats: omit(deploymentStat, 'model_id'),
-          };
-        }
-      }
-    } catch (error) {
-      displayErrorToast(
-        error,
-        i18n.translate('xpack.ml.trainedModels.modelsList.fetchDeploymentStatsErrorMessage', {
-          defaultMessage: 'Fetch deployment stats failed',
-        })
-      );
-    }
-  }, []);
-
   /**
    * Unique inference types from models
    */
@@ -398,11 +362,11 @@ export const ModelsList: FC = () => {
       },
     },
     {
-      name: i18n.translate('xpack.ml.inference.modelsList.startModelAllocationActionLabel', {
-        defaultMessage: 'Start allocation',
+      name: i18n.translate('xpack.ml.inference.modelsList.startModelDeploymentActionLabel', {
+        defaultMessage: 'Start deployment',
       }),
-      description: i18n.translate('xpack.ml.inference.modelsList.startModelAllocationActionLabel', {
-        defaultMessage: 'Start allocation',
+      description: i18n.translate('xpack.ml.inference.modelsList.startModelDeploymentActionLabel', {
+        defaultMessage: 'Start deployment',
       }),
       icon: 'play',
       type: 'icon',
@@ -442,11 +406,11 @@ export const ModelsList: FC = () => {
       },
     },
     {
-      name: i18n.translate('xpack.ml.inference.modelsList.stopModelAllocationActionLabel', {
-        defaultMessage: 'Stop allocation',
+      name: i18n.translate('xpack.ml.inference.modelsList.stopModelDeploymentActionLabel', {
+        defaultMessage: 'Stop deployment',
       }),
-      description: i18n.translate('xpack.ml.inference.modelsList.stopModelAllocationActionLabel', {
-        defaultMessage: 'Stop allocation',
+      description: i18n.translate('xpack.ml.inference.modelsList.stopModelDeploymentActionLabel', {
+        defaultMessage: 'Stop deployment',
       }),
       icon: 'stop',
       type: 'icon',
@@ -567,6 +531,7 @@ export const ModelsList: FC = () => {
         defaultMessage: 'Type',
       }),
       sortable: true,
+      truncateText: true,
       align: 'left',
       render: (types: string[]) => (
         <EuiFlexGroup gutterSize={'xs'} wrap>
@@ -587,6 +552,7 @@ export const ModelsList: FC = () => {
       }),
       sortable: (item) => item.stats?.deployment_stats?.state,
       align: 'left',
+      truncateText: true,
       render: (model: ModelItem) => {
         const state = model.stats?.deployment_stats?.state;
         return state ? <EuiBadge color="hollow">{state}</EuiBadge> : null;
diff --git a/x-pack/plugins/ml/public/application/trained_models/models_management/pipelines/expanded_row.tsx b/x-pack/plugins/ml/public/application/trained_models/models_management/pipelines/expanded_row.tsx
new file mode 100644
index 000000000000..7430d50219d3
--- /dev/null
+++ b/x-pack/plugins/ml/public/application/trained_models/models_management/pipelines/expanded_row.tsx
@@ -0,0 +1,177 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License
+ * 2.0; you may not use this file except in compliance with the Elastic License
+ * 2.0.
+ */
+
+import React, { FC } from 'react';
+import { EuiBadge, EuiInMemoryTable, EuiFlexGroup, EuiFlexItem } from '@elastic/eui';
+import { FormattedMessage } from '@kbn/i18n/react';
+
+import { EuiBasicTableColumn } from '@elastic/eui/src/components/basic_table/basic_table';
+import { i18n } from '@kbn/i18n';
+import { useFieldFormatter } from '../../../contexts/kibana/use_field_formatter';
+import { FIELD_FORMAT_IDS } from '../../../../../../../../src/plugins/field_formats/common';
+import { IngestStatsResponse } from './pipelines';
+import { HelpIcon } from '../../../components/help_icon';
+
+interface ProcessorsStatsProps {
+  stats: Exclude<IngestStatsResponse, undefined>['pipelines'][string]['processors'];
+}
+
+type ProcessorStatsItem = ProcessorsStatsProps['stats'][number][string] & { id: string };
+
+export const ProcessorsStats: FC<ProcessorsStatsProps> = ({ stats }) => {
+  const durationFormatter = useFieldFormatter(FIELD_FORMAT_IDS.DURATION);
+
+  const items: ProcessorStatsItem[] = stats.map((v, i) => {
+    const key = Object.keys(v)[0];
+    return {
+      ...v[key],
+      id: `${key}_${i}`,
+    };
+  });
+
+  const columns: Array<EuiBasicTableColumn<ProcessorStatsItem>> = [
+    {
+      field: 'type',
+      name: i18n.translate(
+        'xpack.ml.trainedModels.modelsList.pipelines.processorStats.typeHeader',
+        {
+          defaultMessage: 'Processor type',
+        }
+      ),
+      width: '100px',
+      sortable: true,
+      truncateText: false,
+      render: (type: string) => {
+        return <EuiBadge color="hollow">{type}</EuiBadge>;
+      },
+      'data-test-subj': 'mlProcessorStatsType',
+    },
+    {
+      field: 'stats.count',
+      name: (
+        <EuiFlexGroup gutterSize="xs">
+          <EuiFlexItem grow={false}>
+            <FormattedMessage
+              id="xpack.ml.trainedModels.modelsList.pipelines.processorStats.countHeader"
+              defaultMessage="Count"
+            />
+          </EuiFlexItem>
+          <EuiFlexItem grow={false}>
+            <HelpIcon
+              content={
+                <FormattedMessage
+                  id="xpack.ml.trainedModels.modelsList.pipelines.processorStats.countDescription"
+                  defaultMessage="Total number of documents ingested during the lifetime of this node"
+                />
+              }
+            />
+          </EuiFlexItem>
+        </EuiFlexGroup>
+      ),
+      width: '100px',
+      truncateText: true,
+      'data-test-subj': 'mlProcessorStatsCount',
+    },
+    {
+      field: 'stats.time_in_millis',
+      name: (
+        <EuiFlexGroup gutterSize="xs">
+          <EuiFlexItem grow={false}>
+            <FormattedMessage
+              id="xpack.ml.trainedModels.modelsList.pipelines.processorStats.timePerDocHeader"
+              defaultMessage="Time per doc"
+            />
+          </EuiFlexItem>
+          <EuiFlexItem grow={false}>
+            <HelpIcon
+              content={
+                <FormattedMessage
+                  id="xpack.ml.trainedModels.modelsList.pipelines.processorStats.timePerDocDescription"
+                  defaultMessage="Total time spent preprocessing ingest documents during the lifetime of this node"
+                />
+              }
+            />
+          </EuiFlexItem>
+        </EuiFlexGroup>
+      ),
+      width: '100px',
+      truncateText: false,
+      'data-test-subj': 'mlProcessorStatsTimePerDoc',
+      render: (v: number) => {
+        return durationFormatter(v);
+      },
+    },
+    {
+      field: 'stats.current',
+      name: (
+        <EuiFlexGroup gutterSize="xs">
+          <EuiFlexItem grow={false}>
+            <FormattedMessage
+              id="xpack.ml.trainedModels.modelsList.pipelines.processorStats.currentHeader"
+              defaultMessage="Current"
+            />
+          </EuiFlexItem>
+          <EuiFlexItem grow={false}>
+            <HelpIcon
+              content={
+                <FormattedMessage
+                  id="xpack.ml.trainedModels.modelsList.pipelines.processorStats.currentDescription"
+                  defaultMessage="Total number of documents currently being ingested"
+                />
+              }
+            />
+          </EuiFlexItem>
+        </EuiFlexGroup>
+      ),
+      width: '100px',
+      truncateText: false,
+      'data-test-subj': 'mlProcessorStatsCurrent',
+    },
+    {
+      field: 'stats.failed',
+      name: (
+        <EuiFlexGroup gutterSize="xs">
+          <EuiFlexItem grow={false}>
+            <FormattedMessage
+              id="xpack.ml.trainedModels.modelsList.pipelines.processorStats.failedHeader"
+              defaultMessage="Failed"
+            />
+          </EuiFlexItem>
+          <EuiFlexItem grow={false}>
+            <HelpIcon
+              content={
+                <FormattedMessage
+                  id="xpack.ml.trainedModels.modelsList.pipelines.processorStats.failedDescription"
+                  defaultMessage="Total number of failed ingest operations during the lifetime of this node"
+                />
+              }
+            />
+          </EuiFlexItem>
+        </EuiFlexGroup>
+      ),
+      width: '100px',
+      'data-test-subj': 'mlProcessorStatsFailed',
+    },
+  ];
+
+  return (
+    <EuiInMemoryTable<ProcessorStatsItem>
+      allowNeutralSort={false}
+      columns={columns}
+      hasActions={false}
+      isExpandable={false}
+      isSelectable={false}
+      items={items}
+      itemId={'id'}
+      rowProps={(item) => ({
+        'data-test-subj': `mlProcessorStatsTableRow row-${item.id}`,
+      })}
+      onTableChange={() => {}}
+      data-test-subj={'mlProcessorStatsTable'}
+    />
+  );
+};
diff --git a/x-pack/plugins/upgrade_assistant/public/application/components/overview/fix_logs_step/index.ts b/x-pack/plugins/ml/public/application/trained_models/models_management/pipelines/index.ts
similarity index 83%
rename from x-pack/plugins/upgrade_assistant/public/application/components/overview/fix_logs_step/index.ts
rename to x-pack/plugins/ml/public/application/trained_models/models_management/pipelines/index.ts
index 8a9a9faa6d09..791561b95816 100644
--- a/x-pack/plugins/upgrade_assistant/public/application/components/overview/fix_logs_step/index.ts
+++ b/x-pack/plugins/ml/public/application/trained_models/models_management/pipelines/index.ts
@@ -5,4 +5,4 @@
  * 2.0.
  */
 
-export { getFixLogsStep } from './fix_logs_step';
+export { ModelPipelines } from './pipelines';
diff --git a/x-pack/plugins/ml/public/application/trained_models/models_management/pipelines/pipelines.tsx b/x-pack/plugins/ml/public/application/trained_models/models_management/pipelines/pipelines.tsx
new file mode 100644
index 000000000000..9b2af52eb03c
--- /dev/null
+++ b/x-pack/plugins/ml/public/application/trained_models/models_management/pipelines/pipelines.tsx
@@ -0,0 +1,119 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License
+ * 2.0; you may not use this file except in compliance with the Elastic License
+ * 2.0.
+ */
+
+import React, { FC } from 'react';
+import {
+  EuiButtonEmpty,
+  EuiCodeBlock,
+  EuiFlexGrid,
+  EuiFlexItem,
+  EuiTitle,
+  EuiPanel,
+  EuiAccordion,
+} from '@elastic/eui';
+import { FormattedMessage } from '@kbn/i18n/react';
+import { useMlKibana } from '../../../contexts/kibana';
+import { ModelItem } from '../models_list';
+import { ProcessorsStats } from './expanded_row';
+
+export type IngestStatsResponse = Exclude<ModelItem['stats'], undefined>['ingest'];
+
+interface ModelPipelinesProps {
+  pipelines: Exclude<ModelItem['pipelines'], null | undefined>;
+  ingestStats: IngestStatsResponse;
+}
+
+export const ModelPipelines: FC<ModelPipelinesProps> = ({ pipelines, ingestStats }) => {
+  const {
+    services: { share },
+  } = useMlKibana();
+
+  return (
+    <>
+      {Object.entries(pipelines).map(([pipelineName, pipelineDefinition], i) => {
+        // Expand first 3 pipelines by default
+        const initialIsOpen = i <= 2;
+        return (
+          <>
+            <EuiAccordion
+              key={pipelineName}
+              id={pipelineName}
+              buttonContent={
+                <EuiTitle size="xs">
+                  <h5>{pipelineName}</h5>
+                </EuiTitle>
+              }
+              extraAction={
+                <EuiButtonEmpty
+                  onClick={() => {
+                    const locator = share.url.locators.get('INGEST_PIPELINES_APP_LOCATOR');
+                    if (!locator) return;
+                    locator.navigate({
+                      page: 'pipeline_edit',
+                      pipelineId: pipelineName,
+                      absolute: true,
+                    });
+                  }}
+                  iconType={'documentEdit'}
+                  iconSide="left"
+                >
+                  <FormattedMessage
+                    id="xpack.ml.trainedModels.modelsList.expandedRow.editPipelineLabel"
+                    defaultMessage="Edit"
+                  />
+                </EuiButtonEmpty>
+              }
+              paddingSize="l"
+              initialIsOpen={initialIsOpen}
+            >
+              <EuiFlexGrid columns={2}>
+                {ingestStats?.pipelines ? (
+                  <EuiFlexItem>
+                    <EuiPanel>
+                      <EuiTitle size={'xxs'}>
+                        <h6>
+                          <FormattedMessage
+                            id="xpack.ml.trainedModels.modelsList.expandedRow.ingestStatsTitle"
+                            defaultMessage="Ingest stats"
+                          />
+                        </h6>
+                      </EuiTitle>
+
+                      <ProcessorsStats stats={ingestStats!.pipelines[pipelineName].processors} />
+                    </EuiPanel>
+                  </EuiFlexItem>
+                ) : null}
+
+                <EuiFlexItem>
+                  <EuiPanel>
+                    <EuiTitle size={'xxs'}>
+                      <h6>
+                        <FormattedMessage
+                          id="xpack.ml.trainedModels.modelsList.expandedRow.processorsTitle"
+                          defaultMessage="Definition"
+                        />
+                      </h6>
+                    </EuiTitle>
+                    <EuiCodeBlock
+                      language="json"
+                      fontSize="m"
+                      paddingSize="m"
+                      overflowHeight={300}
+                      isCopyable
+                    >
+                      {JSON.stringify(pipelineDefinition, null, 2)}
+                    </EuiCodeBlock>
+                  </EuiPanel>
+                </EuiFlexItem>
+              </EuiFlexGrid>
+            </EuiAccordion>
+          </>
+        );
+      })}
+    </>
+  );
+};
diff --git a/x-pack/plugins/ml/public/application/trained_models/navigation_bar.tsx b/x-pack/plugins/ml/public/application/trained_models/navigation_bar.tsx
index da8605f075c2..ec91499bdb72 100644
--- a/x-pack/plugins/ml/public/application/trained_models/navigation_bar.tsx
+++ b/x-pack/plugins/ml/public/application/trained_models/navigation_bar.tsx
@@ -9,6 +9,7 @@ import React, { FC, useCallback, useMemo } from 'react';
 import { i18n } from '@kbn/i18n';
 import { EuiTab, EuiTabs } from '@elastic/eui';
 import { useNavigateToPath } from '../contexts/kibana';
+import { checkPermission } from '../capabilities/check_capabilities';
 
 interface Tab {
   id: string;
@@ -21,6 +22,8 @@ export const TrainedModelsNavigationBar: FC<{
 }> = ({ selectedTabId }) => {
   const navigateToPath = useNavigateToPath();
 
+  const canViewMlNodes = checkPermission('canViewMlNodes');
+
   const tabs = useMemo(() => {
     const navTabs = [
       {
@@ -31,17 +34,21 @@ export const TrainedModelsNavigationBar: FC<{
         path: '/trained_models',
         testSubj: 'mlTrainedModelsTab',
       },
-      {
-        id: 'nodes',
-        name: i18n.translate('xpack.ml.trainedModels.nodesTabLabel', {
-          defaultMessage: 'Nodes',
-        }),
-        path: '/trained_models/nodes',
-        testSubj: 'mlNodesOverviewTab',
-      },
+      ...(canViewMlNodes
+        ? [
+            {
+              id: 'nodes',
+              name: i18n.translate('xpack.ml.trainedModels.nodesTabLabel', {
+                defaultMessage: 'Nodes',
+              }),
+              path: '/trained_models/nodes',
+              testSubj: 'mlNodesOverviewTab',
+            },
+          ]
+        : []),
     ];
     return navTabs;
-  }, []);
+  }, [canViewMlNodes]);
 
   const onTabClick = useCallback(
     async (tab: Tab) => {
diff --git a/x-pack/plugins/ml/public/application/trained_models/nodes_overview/allocated_models.tsx b/x-pack/plugins/ml/public/application/trained_models/nodes_overview/allocated_models.tsx
index 2aad8183b799..f26be61fce6f 100644
--- a/x-pack/plugins/ml/public/application/trained_models/nodes_overview/allocated_models.tsx
+++ b/x-pack/plugins/ml/public/application/trained_models/nodes_overview/allocated_models.tsx
@@ -17,15 +17,31 @@ import { FIELD_FORMAT_IDS } from '../../../../../../../src/plugins/field_formats
 
 interface AllocatedModelsProps {
   models: NodeDeploymentStatsResponse['allocated_models'];
+  hideColumns?: string[];
 }
 
-export const AllocatedModels: FC<AllocatedModelsProps> = ({ models }) => {
+export const AllocatedModels: FC<AllocatedModelsProps> = ({
+  models,
+  hideColumns = ['node_name'],
+}) => {
   const bytesFormatter = useFieldFormatter(FIELD_FORMAT_IDS.BYTES);
   const dateFormatter = useFieldFormatter(FIELD_FORMAT_IDS.DATE);
   const durationFormatter = useFieldFormatter(FIELD_FORMAT_IDS.DURATION);
 
   const columns: Array<EuiBasicTableColumn<AllocatedModel>> = [
     {
+      id: 'node_name',
+      field: 'node.name',
+      name: i18n.translate('xpack.ml.trainedModels.nodesList.modelsList.nodeNameHeader', {
+        defaultMessage: 'Node name',
+      }),
+      width: '200px',
+      sortable: true,
+      truncateText: false,
+      'data-test-subj': 'mlAllocatedModelsTableNodeName',
+    },
+    {
+      id: 'model_id',
       field: 'model_id',
       name: i18n.translate('xpack.ml.trainedModels.nodesList.modelsList.modelNameHeader', {
         defaultMessage: 'Name',
@@ -84,6 +100,16 @@ export const AllocatedModels: FC<AllocatedModelsProps> = ({ models }) => {
         return v.node.inference_count;
       },
     },
+    {
+      name: i18n.translate('xpack.ml.trainedModels.nodesList.modelsList.modelStartTimeHeader', {
+        defaultMessage: 'Start time',
+      }),
+      width: '200px',
+      'data-test-subj': 'mlAllocatedModelsTableStartedTime',
+      render: (v: AllocatedModel) => {
+        return dateFormatter(v.node.start_time);
+      },
+    },
     {
       name: i18n.translate('xpack.ml.trainedModels.nodesList.modelsList.modelLastAccessHeader', {
         defaultMessage: 'Last access',
@@ -94,6 +120,19 @@ export const AllocatedModels: FC<AllocatedModelsProps> = ({ models }) => {
         return dateFormatter(v.node.last_access);
       },
     },
+    {
+      name: i18n.translate(
+        'xpack.ml.trainedModels.nodesList.modelsList.modelNumberOfPendingRequestsHeader',
+        {
+          defaultMessage: 'Pending requests',
+        }
+      ),
+      width: '100px',
+      'data-test-subj': 'mlAllocatedModelsTableNumberOfPendingRequests',
+      render: (v: AllocatedModel) => {
+        return v.node.number_of_pending_requests;
+      },
+    },
     {
       name: i18n.translate('xpack.ml.trainedModels.nodesList.modelsList.modelRoutingStateHeader', {
         defaultMessage: 'Routing state',
@@ -110,7 +149,7 @@ export const AllocatedModels: FC<AllocatedModelsProps> = ({ models }) => {
         );
       },
     },
-  ];
+  ].filter((v) => !hideColumns.includes(v.id!));
 
   return (
     <EuiInMemoryTable<AllocatedModel>
diff --git a/x-pack/plugins/ml/public/application/trained_models/nodes_overview/expanded_row.tsx b/x-pack/plugins/ml/public/application/trained_models/nodes_overview/expanded_row.tsx
index 508a5689e1c9..ba5cdd909321 100644
--- a/x-pack/plugins/ml/public/application/trained_models/nodes_overview/expanded_row.tsx
+++ b/x-pack/plugins/ml/public/application/trained_models/nodes_overview/expanded_row.tsx
@@ -15,15 +15,19 @@ import {
   EuiTitle,
 } from '@elastic/eui';
 import { FormattedMessage } from '@kbn/i18n/react';
-import { NodeItemWithStats } from './nodes_list';
+import { NodeItem } from './nodes_list';
 import { formatToListItems } from '../models_management/expanded_row';
 import { AllocatedModels } from './allocated_models';
+import { useFieldFormatter } from '../../contexts/kibana/use_field_formatter';
+import { FIELD_FORMAT_IDS } from '../../../../../../../src/plugins/field_formats/common';
 
 interface ExpandedRowProps {
-  item: NodeItemWithStats;
+  item: NodeItem;
 }
 
 export const ExpandedRow: FC<ExpandedRowProps> = ({ item }) => {
+  const bytesFormatter = useFieldFormatter(FIELD_FORMAT_IDS.BYTES);
+
   const {
     allocated_models: allocatedModels,
     attributes,
@@ -31,6 +35,11 @@ export const ExpandedRow: FC<ExpandedRowProps> = ({ item }) => {
     ...details
   } = item;
 
+  // Process node attributes
+  attributes['ml.machine_memory'] = bytesFormatter(attributes['ml.machine_memory']);
+  attributes['ml.max_jvm_size'] = bytesFormatter(attributes['ml.max_jvm_size']);
+  delete attributes['xpack.installed'];
+
   return (
     <>
       <EuiSpacer size={'m'} />
diff --git a/x-pack/plugins/ml/public/application/trained_models/nodes_overview/nodes_list.tsx b/x-pack/plugins/ml/public/application/trained_models/nodes_overview/nodes_list.tsx
index b1cc18e698c9..87211fedaea4 100644
--- a/x-pack/plugins/ml/public/application/trained_models/nodes_overview/nodes_list.tsx
+++ b/x-pack/plugins/ml/public/application/trained_models/nodes_overview/nodes_list.tsx
@@ -36,10 +36,6 @@ import { useRefresh } from '../../routing/use_refresh';
 
 export type NodeItem = NodeDeploymentStatsResponse;
 
-export interface NodeItemWithStats extends NodeItem {
-  stats: any;
-}
-
 export const getDefaultNodesListState = (): ListingPageUrlState => ({
   pageIndex: 0,
   pageSize: 10,
@@ -70,6 +66,14 @@ export const NodesList: FC = () => {
     try {
       const nodesResponse = await trainedModelsApiService.getTrainedModelsNodesOverview();
       setItems(nodesResponse.nodes);
+
+      // Update expanded rows.
+      nodesResponse.nodes.forEach((node) => {
+        if (itemIdToExpandedRowMap[node.id]) {
+          itemIdToExpandedRowMap[node.id] = <ExpandedRow item={node} />;
+        }
+      });
+
       setIsLoading(false);
       refreshAnalyticsList$.next(REFRESH_ANALYTICS_LIST_STATE.IDLE);
     } catch (e) {
@@ -80,14 +84,14 @@ export const NodesList: FC = () => {
         })
       );
     }
-  }, []);
+  }, [itemIdToExpandedRowMap]);
 
   const toggleDetails = (item: NodeItem) => {
     const itemIdToExpandedRowMapValues = { ...itemIdToExpandedRowMap };
     if (itemIdToExpandedRowMapValues[item.id]) {
       delete itemIdToExpandedRowMapValues[item.id];
     } else {
-      itemIdToExpandedRowMapValues[item.id] = <ExpandedRow item={item as NodeItemWithStats} />;
+      itemIdToExpandedRowMapValues[item.id] = <ExpandedRow item={item} />;
     }
     setItemIdToExpandedRowMap(itemIdToExpandedRowMapValues);
   };
diff --git a/x-pack/plugins/ml/public/application/trained_models/page.tsx b/x-pack/plugins/ml/public/application/trained_models/page.tsx
index 54849f3e651d..afbebf58937b 100644
--- a/x-pack/plugins/ml/public/application/trained_models/page.tsx
+++ b/x-pack/plugins/ml/public/application/trained_models/page.tsx
@@ -28,14 +28,9 @@ import { ModelsList } from './models_management';
 import { TrainedModelsNavigationBar } from './navigation_bar';
 import { RefreshAnalyticsListButton } from '../data_frame_analytics/pages/analytics_management/components/refresh_analytics_list_button';
 import { DatePickerWrapper } from '../components/navigation_menu/date_picker_wrapper';
-import { useRefreshAnalyticsList } from '../data_frame_analytics/common';
-import { useRefreshInterval } from '../data_frame_analytics/pages/analytics_management/components/analytics_list/use_refresh_interval';
 import { NodesList } from './nodes_overview';
 
 export const Page: FC = () => {
-  useRefreshInterval(() => {});
-
-  useRefreshAnalyticsList({ isLoading: () => {} });
   const location = useLocation();
   const selectedTabId = useMemo(() => location.pathname.split('/').pop(), [location]);
 
diff --git a/x-pack/plugins/ml/server/lib/capabilities/check_capabilities.test.ts b/x-pack/plugins/ml/server/lib/capabilities/check_capabilities.test.ts
index 920e1f703422..c72b4d5cb5dd 100644
--- a/x-pack/plugins/ml/server/lib/capabilities/check_capabilities.test.ts
+++ b/x-pack/plugins/ml/server/lib/capabilities/check_capabilities.test.ts
@@ -51,7 +51,7 @@ describe('check_capabilities', () => {
       );
       const { capabilities } = await getCapabilities();
       const count = Object.keys(capabilities).length;
-      expect(count).toBe(31);
+      expect(count).toBe(32);
     });
   });
 
@@ -101,6 +101,7 @@ describe('check_capabilities', () => {
       expect(capabilities.canCreateDataFrameAnalytics).toBe(false);
       expect(capabilities.canStartStopDataFrameAnalytics).toBe(false);
       expect(capabilities.canCreateMlAlerts).toBe(false);
+      expect(capabilities.canViewMlNodes).toBe(false);
     });
 
     test('full capabilities', async () => {
@@ -146,6 +147,7 @@ describe('check_capabilities', () => {
       expect(capabilities.canDeleteDataFrameAnalytics).toBe(true);
       expect(capabilities.canCreateDataFrameAnalytics).toBe(true);
       expect(capabilities.canStartStopDataFrameAnalytics).toBe(true);
+      expect(capabilities.canViewMlNodes).toBe(true);
     });
 
     test('upgrade in progress with full capabilities', async () => {
diff --git a/x-pack/plugins/ml/server/lib/ml_client/ml_client.ts b/x-pack/plugins/ml/server/lib/ml_client/ml_client.ts
index 6169d9ee9db4..c2b98ab1b0c2 100644
--- a/x-pack/plugins/ml/server/lib/ml_client/ml_client.ts
+++ b/x-pack/plugins/ml/server/lib/ml_client/ml_client.ts
@@ -380,11 +380,6 @@ export function getMlClient(
     async getTrainedModelsStats(...p: Parameters<MlClient['getTrainedModelsStats']>) {
       return mlClient.getTrainedModelsStats(...p);
     },
-    async getTrainedModelDeploymentStats(
-      ...p: Parameters<MlClient['getTrainedModelDeploymentStats']>
-    ) {
-      return mlClient.getTrainedModelDeploymentStats(...p);
-    },
     async startTrainedModelDeployment(...p: Parameters<MlClient['startTrainedModelDeployment']>) {
       return mlClient.startTrainedModelDeployment(...p);
     },
diff --git a/x-pack/plugins/ml/server/lib/ml_client/types.ts b/x-pack/plugins/ml/server/lib/ml_client/types.ts
index d8c65c4f5681..b4778f4e6d5b 100644
--- a/x-pack/plugins/ml/server/lib/ml_client/types.ts
+++ b/x-pack/plugins/ml/server/lib/ml_client/types.ts
@@ -48,7 +48,6 @@ export type MlClientParams =
   | Parameters<MlClient['getRecords']>
   | Parameters<MlClient['getTrainedModels']>
   | Parameters<MlClient['getTrainedModelsStats']>
-  | Parameters<MlClient['getTrainedModelDeploymentStats']>
   | Parameters<MlClient['startTrainedModelDeployment']>
   | Parameters<MlClient['stopTrainedModelDeployment']>
   | Parameters<MlClient['info']>
diff --git a/x-pack/plugins/ml/server/models/data_frame_analytics/__mocks__/mock_deployment_response.json b/x-pack/plugins/ml/server/models/data_frame_analytics/__mocks__/mock_deployment_response.json
index 0742c249b67b..5d80fa26b4c3 100644
--- a/x-pack/plugins/ml/server/models/data_frame_analytics/__mocks__/mock_deployment_response.json
+++ b/x-pack/plugins/ml/server/models/data_frame_analytics/__mocks__/mock_deployment_response.json
@@ -1,357 +1,355 @@
-{
-  "count" : 4,
-  "deployment_stats" : [
-    {
-      "model_id" : "distilbert-base-uncased-finetuned-sst-2-english",
-      "model_size_bytes" : 267386880,
-      "inference_threads" : 1,
-      "model_threads" : 1,
-      "state" : "started",
-      "allocation_status" : {
-        "allocation_count" : 2,
-        "target_allocation_count" : 3,
-        "state" : "started"
-      },
-      "nodes" : [
-        {
-          "node" : {
-            "3qIoLFnbSi-DwVrYioUCdw" : {
-              "name" : "node3",
-              "ephemeral_id" : "WeA49KLuRPmJM_ulLx0ANg",
-              "transport_address" : "10.142.0.2:9353",
-              "attributes" : {
-                "ml.machine_memory" : "15599742976",
-                "xpack.installed" : "true",
-                "ml.max_jvm_size" : "1073741824"
-              },
-              "roles" : [
-                "data",
-                "ingest",
-                "master",
-                "ml",
-                "transform"
-              ]
-            }
-          },
-          "routing_state" : {
-            "routing_state" : "started"
-          },
-          "inference_count" : 0,
-          "average_inference_time_ms" : 0.0
+[
+  {
+    "model_id": "distilbert-base-uncased-finetuned-sst-2-english",
+    "model_size_bytes": 267386880,
+    "inference_threads": 1,
+    "model_threads": 1,
+    "state": "started",
+    "allocation_status": {
+      "allocation_count": 2,
+      "target_allocation_count": 3,
+      "state": "started"
+    },
+    "nodes": [
+      {
+        "node": {
+          "3qIoLFnbSi-DwVrYioUCdw": {
+            "name": "node3",
+            "ephemeral_id": "WeA49KLuRPmJM_ulLx0ANg",
+            "transport_address": "10.142.0.2:9353",
+            "attributes": {
+              "ml.machine_memory": "15599742976",
+              "xpack.installed": "true",
+              "ml.max_jvm_size": "1073741824"
+            },
+            "roles": [
+              "data",
+              "ingest",
+              "master",
+              "ml",
+              "transform"
+            ]
+          }
         },
-        {
-          "node" : {
-            "DpCy7SOBQla3pu0Dq-tnYw" : {
-              "name" : "node2",
-              "ephemeral_id" : "17qcsXsNTYqbJ6uwSvdl9g",
-              "transport_address" : "10.142.0.2:9352",
-              "attributes" : {
-                "ml.machine_memory" : "15599742976",
-                "xpack.installed" : "true",
-                "ml.max_jvm_size" : "1073741824"
-              },
-              "roles" : [
-                "data",
-                "master",
-                "ml",
-                "transform"
-              ]
-            }
-          },
-          "routing_state" : {
-            "routing_state" : "failed",
-            "reason" : "The object cannot be set twice!"
+        "routing_state": {
+          "routing_state": "started"
+        },
+        "inference_count": 0,
+        "average_inference_time_ms": 0.0
+      },
+      {
+        "node": {
+          "DpCy7SOBQla3pu0Dq-tnYw": {
+            "name": "node2",
+            "ephemeral_id": "17qcsXsNTYqbJ6uwSvdl9g",
+            "transport_address": "10.142.0.2:9352",
+            "attributes": {
+              "ml.machine_memory": "15599742976",
+              "xpack.installed": "true",
+              "ml.max_jvm_size": "1073741824"
+            },
+            "roles": [
+              "data",
+              "master",
+              "ml",
+              "transform"
+            ]
           }
         },
-        {
-          "node" : {
-            "pt7s6lKHQJaP4QHKtU-Q0Q" : {
-              "name" : "node1",
-              "ephemeral_id" : "nMJBE9WSRQSWotk0zDPi_Q",
-              "transport_address" : "10.142.0.2:9351",
-              "attributes" : {
-                "ml.machine_memory" : "15599742976",
-                "xpack.installed" : "true",
-                "ml.max_jvm_size" : "1073741824"
-              },
-              "roles" : [
-                "data",
-                "master",
-                "ml"
-              ]
-            }
-          },
-          "routing_state" : {
-            "routing_state" : "started"
-          },
-          "inference_count" : 0,
-          "average_inference_time_ms" : 0.0
+        "routing_state": {
+          "routing_state": "failed",
+          "reason": "The object cannot be set twice!"
         }
-      ]
-    },
-    {
-      "model_id" : "elastic__distilbert-base-cased-finetuned-conll03-english",
-      "model_size_bytes" : 260947500,
-      "inference_threads" : 1,
-      "model_threads" : 1,
-      "state" : "started",
-      "allocation_status" : {
-        "allocation_count" : 2,
-        "target_allocation_count" : 3,
-        "state" : "started"
       },
-      "nodes" : [
-        {
-          "node" : {
-            "3qIoLFnbSi-DwVrYioUCdw" : {
-              "name" : "node3",
-              "ephemeral_id" : "WeA49KLuRPmJM_ulLx0ANg",
-              "transport_address" : "10.142.0.2:9353",
-              "attributes" : {
-                "ml.machine_memory" : "15599742976",
-                "xpack.installed" : "true",
-                "ml.max_jvm_size" : "1073741824"
-              },
-              "roles" : [
-                "data",
-                "ingest",
-                "master",
-                "ml",
-                "transform"
-              ]
-            }
-          },
-          "routing_state" : {
-            "routing_state" : "started"
-          },
-          "inference_count" : 0,
-          "average_inference_time_ms" : 0.0
+      {
+        "node": {
+          "pt7s6lKHQJaP4QHKtU-Q0Q": {
+            "name": "node1",
+            "ephemeral_id": "nMJBE9WSRQSWotk0zDPi_Q",
+            "transport_address": "10.142.0.2:9351",
+            "attributes": {
+              "ml.machine_memory": "15599742976",
+              "xpack.installed": "true",
+              "ml.max_jvm_size": "1073741824"
+            },
+            "roles": [
+              "data",
+              "master",
+              "ml"
+            ]
+          }
         },
-        {
-          "node" : {
-            "DpCy7SOBQla3pu0Dq-tnYw" : {
-              "name" : "node2",
-              "ephemeral_id" : "17qcsXsNTYqbJ6uwSvdl9g",
-              "transport_address" : "10.142.0.2:9352",
-              "attributes" : {
-                "ml.machine_memory" : "15599742976",
-                "xpack.installed" : "true",
-                "ml.max_jvm_size" : "1073741824"
-              },
-              "roles" : [
-                "data",
-                "master",
-                "ml",
-                "transform"
-              ]
-            }
-          },
-          "routing_state" : {
-            "routing_state" : "failed",
-            "reason" : "The object cannot be set twice!"
+        "routing_state": {
+          "routing_state": "started"
+        },
+        "inference_count": 0,
+        "average_inference_time_ms": 0.0
+      }
+    ]
+  },
+  {
+    "model_id": "elastic__distilbert-base-cased-finetuned-conll03-english",
+    "model_size_bytes": 260947500,
+    "inference_threads": 1,
+    "model_threads": 1,
+    "state": "started",
+    "allocation_status": {
+      "allocation_count": 2,
+      "target_allocation_count": 3,
+      "state": "started"
+    },
+    "nodes": [
+      {
+        "node": {
+          "3qIoLFnbSi-DwVrYioUCdw": {
+            "name": "node3",
+            "ephemeral_id": "WeA49KLuRPmJM_ulLx0ANg",
+            "transport_address": "10.142.0.2:9353",
+            "attributes": {
+              "ml.machine_memory": "15599742976",
+              "xpack.installed": "true",
+              "ml.max_jvm_size": "1073741824"
+            },
+            "roles": [
+              "data",
+              "ingest",
+              "master",
+              "ml",
+              "transform"
+            ]
           }
         },
-        {
-          "node" : {
-            "pt7s6lKHQJaP4QHKtU-Q0Q" : {
-              "name" : "node1",
-              "ephemeral_id" : "nMJBE9WSRQSWotk0zDPi_Q",
-              "transport_address" : "10.142.0.2:9351",
-              "attributes" : {
-                "ml.machine_memory" : "15599742976",
-                "xpack.installed" : "true",
-                "ml.max_jvm_size" : "1073741824"
-              },
-              "roles" : [
-                "data",
-                "master",
-                "ml"
-              ]
-            }
-          },
-          "routing_state" : {
-            "routing_state" : "started"
-          },
-          "inference_count" : 0,
-          "average_inference_time_ms" : 0.0
+        "routing_state": {
+          "routing_state": "started"
+        },
+        "inference_count": 0,
+        "average_inference_time_ms": 0.0
+      },
+      {
+        "node": {
+          "DpCy7SOBQla3pu0Dq-tnYw": {
+            "name": "node2",
+            "ephemeral_id": "17qcsXsNTYqbJ6uwSvdl9g",
+            "transport_address": "10.142.0.2:9352",
+            "attributes": {
+              "ml.machine_memory": "15599742976",
+              "xpack.installed": "true",
+              "ml.max_jvm_size": "1073741824"
+            },
+            "roles": [
+              "data",
+              "master",
+              "ml",
+              "transform"
+            ]
+          }
+        },
+        "routing_state": {
+          "routing_state": "failed",
+          "reason": "The object cannot be set twice!"
         }
-      ]
-    },
-    {
-      "model_id" : "sentence-transformers__msmarco-minilm-l-12-v3",
-      "model_size_bytes" : 133378867,
-      "inference_threads" : 1,
-      "model_threads" : 1,
-      "state" : "started",
-      "allocation_status" : {
-        "allocation_count" : 2,
-        "target_allocation_count" : 3,
-        "state" : "started"
       },
-      "nodes" : [
-        {
-          "node" : {
-            "3qIoLFnbSi-DwVrYioUCdw" : {
-              "name" : "node3",
-              "ephemeral_id" : "WeA49KLuRPmJM_ulLx0ANg",
-              "transport_address" : "10.142.0.2:9353",
-              "attributes" : {
-                "ml.machine_memory" : "15599742976",
-                "xpack.installed" : "true",
-                "ml.max_jvm_size" : "1073741824"
-              },
-              "roles" : [
-                "data",
-                "ingest",
-                "master",
-                "ml",
-                "transform"
-              ]
-            }
-          },
-          "routing_state" : {
-            "routing_state" : "started"
-          },
-          "inference_count" : 0,
-          "average_inference_time_ms" : 0.0
+      {
+        "node": {
+          "pt7s6lKHQJaP4QHKtU-Q0Q": {
+            "name": "node1",
+            "ephemeral_id": "nMJBE9WSRQSWotk0zDPi_Q",
+            "transport_address": "10.142.0.2:9351",
+            "attributes": {
+              "ml.machine_memory": "15599742976",
+              "xpack.installed": "true",
+              "ml.max_jvm_size": "1073741824"
+            },
+            "roles": [
+              "data",
+              "master",
+              "ml"
+            ]
+          }
         },
-        {
-          "node" : {
-            "DpCy7SOBQla3pu0Dq-tnYw" : {
-              "name" : "node2",
-              "ephemeral_id" : "17qcsXsNTYqbJ6uwSvdl9g",
-              "transport_address" : "10.142.0.2:9352",
-              "attributes" : {
-                "ml.machine_memory" : "15599742976",
-                "xpack.installed" : "true",
-                "ml.max_jvm_size" : "1073741824"
-              },
-              "roles" : [
-                "data",
-                "master",
-                "ml",
-                "transform"
-              ]
-            }
-          },
-          "routing_state" : {
-            "routing_state" : "failed",
-            "reason" : "The object cannot be set twice!"
+        "routing_state": {
+          "routing_state": "started"
+        },
+        "inference_count": 0,
+        "average_inference_time_ms": 0.0
+      }
+    ]
+  },
+  {
+    "model_id": "sentence-transformers__msmarco-minilm-l-12-v3",
+    "model_size_bytes": 133378867,
+    "inference_threads": 1,
+    "model_threads": 1,
+    "state": "started",
+    "allocation_status": {
+      "allocation_count": 2,
+      "target_allocation_count": 3,
+      "state": "started"
+    },
+    "nodes": [
+      {
+        "node": {
+          "3qIoLFnbSi-DwVrYioUCdw": {
+            "name": "node3",
+            "ephemeral_id": "WeA49KLuRPmJM_ulLx0ANg",
+            "transport_address": "10.142.0.2:9353",
+            "attributes": {
+              "ml.machine_memory": "15599742976",
+              "xpack.installed": "true",
+              "ml.max_jvm_size": "1073741824"
+            },
+            "roles": [
+              "data",
+              "ingest",
+              "master",
+              "ml",
+              "transform"
+            ]
           }
         },
-        {
-          "node" : {
-            "pt7s6lKHQJaP4QHKtU-Q0Q" : {
-              "name" : "node1",
-              "ephemeral_id" : "nMJBE9WSRQSWotk0zDPi_Q",
-              "transport_address" : "10.142.0.2:9351",
-              "attributes" : {
-                "ml.machine_memory" : "15599742976",
-                "xpack.installed" : "true",
-                "ml.max_jvm_size" : "1073741824"
-              },
-              "roles" : [
-                "data",
-                "master",
-                "ml"
-              ]
-            }
-          },
-          "routing_state" : {
-            "routing_state" : "started"
-          },
-          "inference_count" : 0,
-          "average_inference_time_ms" : 0.0
+        "routing_state": {
+          "routing_state": "started"
+        },
+        "inference_count": 0,
+        "average_inference_time_ms": 0.0
+      },
+      {
+        "node": {
+          "DpCy7SOBQla3pu0Dq-tnYw": {
+            "name": "node2",
+            "ephemeral_id": "17qcsXsNTYqbJ6uwSvdl9g",
+            "transport_address": "10.142.0.2:9352",
+            "attributes": {
+              "ml.machine_memory": "15599742976",
+              "xpack.installed": "true",
+              "ml.max_jvm_size": "1073741824"
+            },
+            "roles": [
+              "data",
+              "master",
+              "ml",
+              "transform"
+            ]
+          }
+        },
+        "routing_state": {
+          "routing_state": "failed",
+          "reason": "The object cannot be set twice!"
         }
-      ]
-    },
-    {
-      "model_id" : "typeform__mobilebert-uncased-mnli",
-      "model_size_bytes" : 100139008,
-      "inference_threads" : 1,
-      "model_threads" : 1,
-      "state" : "started",
-      "allocation_status" : {
-        "allocation_count" : 2,
-        "target_allocation_count" : 3,
-        "state" : "started"
       },
-      "nodes" : [
-        {
-          "node" : {
-            "3qIoLFnbSi-DwVrYioUCdw" : {
-              "name" : "node3",
-              "ephemeral_id" : "WeA49KLuRPmJM_ulLx0ANg",
-              "transport_address" : "10.142.0.2:9353",
-              "attributes" : {
-                "ml.machine_memory" : "15599742976",
-                "xpack.installed" : "true",
-                "ml.max_jvm_size" : "1073741824"
-              },
-              "roles" : [
-                "data",
-                "ingest",
-                "master",
-                "ml",
-                "transform"
-              ]
-            }
-          },
-          "routing_state" : {
-            "routing_state" : "started"
-          },
-          "inference_count" : 0,
-          "average_inference_time_ms" : 0.0
+      {
+        "node": {
+          "pt7s6lKHQJaP4QHKtU-Q0Q": {
+            "name": "node1",
+            "ephemeral_id": "nMJBE9WSRQSWotk0zDPi_Q",
+            "transport_address": "10.142.0.2:9351",
+            "attributes": {
+              "ml.machine_memory": "15599742976",
+              "xpack.installed": "true",
+              "ml.max_jvm_size": "1073741824"
+            },
+            "roles": [
+              "data",
+              "master",
+              "ml"
+            ]
+          }
+        },
+        "routing_state": {
+          "routing_state": "started"
+        },
+        "inference_count": 0,
+        "average_inference_time_ms": 0.0
+      }
+    ]
+  },
+  {
+    "model_id": "typeform__mobilebert-uncased-mnli",
+    "model_size_bytes": 100139008,
+    "inference_threads": 1,
+    "model_threads": 1,
+    "state": "started",
+    "allocation_status": {
+      "allocation_count": 2,
+      "target_allocation_count": 3,
+      "state": "started"
+    },
+    "nodes": [
+      {
+        "node": {
+          "3qIoLFnbSi-DwVrYioUCdw": {
+            "name": "node3",
+            "ephemeral_id": "WeA49KLuRPmJM_ulLx0ANg",
+            "transport_address": "10.142.0.2:9353",
+            "attributes": {
+              "ml.machine_memory": "15599742976",
+              "xpack.installed": "true",
+              "ml.max_jvm_size": "1073741824"
+            },
+            "roles": [
+              "data",
+              "ingest",
+              "master",
+              "ml",
+              "transform"
+            ]
+          }
         },
-        {
-          "node" : {
-            "DpCy7SOBQla3pu0Dq-tnYw" : {
-              "name" : "node2",
-              "ephemeral_id" : "17qcsXsNTYqbJ6uwSvdl9g",
-              "transport_address" : "10.142.0.2:9352",
-              "attributes" : {
-                "ml.machine_memory" : "15599742976",
-                "xpack.installed" : "true",
-                "ml.max_jvm_size" : "1073741824"
-              },
-              "roles" : [
-                "data",
-                "master",
-                "ml",
-                "transform"
-              ]
-            }
-          },
-          "routing_state" : {
-            "routing_state" : "failed",
-            "reason" : "The object cannot be set twice!"
+        "routing_state": {
+          "routing_state": "started"
+        },
+        "inference_count": 0,
+        "average_inference_time_ms": 0.0
+      },
+      {
+        "node": {
+          "DpCy7SOBQla3pu0Dq-tnYw": {
+            "name": "node2",
+            "ephemeral_id": "17qcsXsNTYqbJ6uwSvdl9g",
+            "transport_address": "10.142.0.2:9352",
+            "attributes": {
+              "ml.machine_memory": "15599742976",
+              "xpack.installed": "true",
+              "ml.max_jvm_size": "1073741824"
+            },
+            "roles": [
+              "data",
+              "master",
+              "ml",
+              "transform"
+            ]
           }
         },
-        {
-          "node" : {
-            "pt7s6lKHQJaP4QHKtU-Q0Q" : {
-              "name" : "node1",
-              "ephemeral_id" : "nMJBE9WSRQSWotk0zDPi_Q",
-              "transport_address" : "10.142.0.2:9351",
-              "attributes" : {
-                "ml.machine_memory" : "15599742976",
-                "xpack.installed" : "true",
-                "ml.max_jvm_size" : "1073741824"
-              },
-              "roles" : [
-                "data",
-                "master",
-                "ml"
-              ]
-            }
-          },
-          "routing_state" : {
-            "routing_state" : "started"
-          },
-          "inference_count" : 0,
-          "average_inference_time_ms" : 0.0
+        "routing_state": {
+          "routing_state": "failed",
+          "reason": "The object cannot be set twice!"
         }
-      ]
-    }
-  ]
-}
+      },
+      {
+        "node": {
+          "pt7s6lKHQJaP4QHKtU-Q0Q": {
+            "name": "node1",
+            "ephemeral_id": "nMJBE9WSRQSWotk0zDPi_Q",
+            "transport_address": "10.142.0.2:9351",
+            "attributes": {
+              "ml.machine_memory": "15599742976",
+              "xpack.installed": "true",
+              "ml.max_jvm_size": "1073741824"
+            },
+            "roles": [
+              "data",
+              "master",
+              "ml"
+            ]
+          }
+        },
+        "routing_state": {
+          "routing_state": "started"
+        },
+        "inference_count": 0,
+        "average_inference_time_ms": 0.0
+      }
+    ]
+  }
+]
+
diff --git a/x-pack/plugins/ml/server/models/data_frame_analytics/model_provider.test.ts b/x-pack/plugins/ml/server/models/data_frame_analytics/model_provider.test.ts
index 4f5e1ee9b230..c0d70aa47199 100644
--- a/x-pack/plugins/ml/server/models/data_frame_analytics/model_provider.test.ts
+++ b/x-pack/plugins/ml/server/models/data_frame_analytics/model_provider.test.ts
@@ -104,8 +104,16 @@ describe('Model service', () => {
     },
   } as unknown as jest.Mocked<IScopedClusterClient>;
   const mlClient = {
-    getTrainedModelDeploymentStats: jest.fn(() => {
-      return Promise.resolve({ body: mockResponse });
+    getTrainedModelsStats: jest.fn(() => {
+      return Promise.resolve({
+        body: {
+          trained_model_stats: mockResponse.map((v) => {
+            return {
+              deployment_stats: v,
+            };
+          }),
+        },
+      });
     }),
   } as unknown as jest.Mocked<MlClient>;
   const memoryOverviewService = {
@@ -214,9 +222,7 @@ describe('Model service', () => {
             'ml.max_jvm_size': '1073741824',
             'xpack.installed': 'true',
           },
-          host: '10.10.10.2',
           id: '3qIoLFnbSi-DwVrYioUCdw',
-          ip: '10.10.10.2:9353',
           memory_overview: {
             anomaly_detection: {
               total: 0,
@@ -251,7 +257,6 @@ describe('Model service', () => {
             },
           },
           roles: ['data', 'ingest', 'master', 'ml', 'transform'],
-          transport_address: '10.10.10.2:9353',
         },
         {
           name: 'node2',
@@ -334,9 +339,7 @@ describe('Model service', () => {
             'ml.max_jvm_size': '1073741824',
             'xpack.installed': 'true',
           },
-          host: '10.10.10.2',
           id: 'DpCy7SOBQla3pu0Dq-tnYw',
-          ip: '10.10.10.2:9352',
           memory_overview: {
             anomaly_detection: {
               total: 0,
@@ -371,7 +374,6 @@ describe('Model service', () => {
             },
           },
           roles: ['data', 'master', 'ml', 'transform'],
-          transport_address: '10.10.10.2:9352',
         },
         {
           allocated_models: [
@@ -457,9 +459,7 @@ describe('Model service', () => {
             'ml.max_jvm_size': '1073741824',
             'xpack.installed': 'true',
           },
-          host: '10.10.10.2',
           id: 'pt7s6lKHQJaP4QHKtU-Q0Q',
-          ip: '10.10.10.2:9351',
           memory_overview: {
             anomaly_detection: {
               total: 0,
@@ -495,7 +495,6 @@ describe('Model service', () => {
           },
           name: 'node1',
           roles: ['data', 'master', 'ml'],
-          transport_address: '10.10.10.2:9351',
         },
       ],
     });
diff --git a/x-pack/plugins/ml/server/models/data_frame_analytics/models_provider.ts b/x-pack/plugins/ml/server/models/data_frame_analytics/models_provider.ts
index 2f40081f1458..104e320e7fab 100644
--- a/x-pack/plugins/ml/server/models/data_frame_analytics/models_provider.ts
+++ b/x-pack/plugins/ml/server/models/data_frame_analytics/models_provider.ts
@@ -19,18 +19,11 @@ import {
   NATIVE_EXECUTABLE_CODE_OVERHEAD,
 } from '../memory_overview/memory_overview_service';
 import { TrainedModelDeploymentStatsResponse } from '../../../common/types/trained_models';
+import { isDefined } from '../../../common/types/guards';
 
 export type ModelService = ReturnType<typeof modelsProvider>;
 
-const NODE_FIELDS = [
-  'attributes',
-  'name',
-  'roles',
-  'ip',
-  'host',
-  'transport_address',
-  'version',
-] as const;
+const NODE_FIELDS = ['attributes', 'name', 'roles', 'version'] as const;
 
 export type RequiredNodeFields = Pick<NodesInfoNodeInfo, typeof NODE_FIELDS[number]>;
 
@@ -87,8 +80,11 @@ export function modelsProvider(
         throw new Error('Memory overview service is not provided');
       }
 
-      const { body: deploymentStats } = await mlClient.getTrainedModelDeploymentStats({
-        model_id: '*',
+      const {
+        body: { trained_model_stats: trainedModelStats },
+      } = await mlClient.getTrainedModelsStats({
+        model_id: '_all',
+        size: 10000,
       });
 
       const {
@@ -105,7 +101,12 @@ export function modelsProvider(
           const nodeFields = pick(node, NODE_FIELDS) as RequiredNodeFields;
 
           const allocatedModels = (
-            deploymentStats.deployment_stats as TrainedModelDeploymentStatsResponse[]
+            trainedModelStats
+              .map((v) => {
+                // @ts-ignore new prop
+                return v.deployment_stats;
+              })
+              .filter(isDefined) as TrainedModelDeploymentStatsResponse[]
           )
             .filter((v) => v.nodes.some((n) => Object.keys(n.node)[0] === nodeId))
             .map(({ nodes, ...rest }) => {
diff --git a/x-pack/plugins/ml/server/routes/apidoc.json b/x-pack/plugins/ml/server/routes/apidoc.json
index b7bd92c91389..e1a839b21f7b 100644
--- a/x-pack/plugins/ml/server/routes/apidoc.json
+++ b/x-pack/plugins/ml/server/routes/apidoc.json
@@ -160,7 +160,6 @@
     "TrainedModels",
     "GetTrainedModel",
     "GetTrainedModelStats",
-    "GetTrainedModelDeploymentStats",
     "GetTrainedModelsNodesOverview",
     "GetTrainedModelPipelines",
     "StartTrainedModelDeployment",
diff --git a/x-pack/plugins/ml/server/routes/trained_models.ts b/x-pack/plugins/ml/server/routes/trained_models.ts
index 1837f9e88edf..e7696861153f 100644
--- a/x-pack/plugins/ml/server/routes/trained_models.ts
+++ b/x-pack/plugins/ml/server/routes/trained_models.ts
@@ -198,7 +198,11 @@ export function trainedModelsRoutes({ router, routeGuard }: RouteInitialization)
       path: '/api/ml/trained_models/nodes_overview',
       validate: {},
       options: {
-        tags: ['access:ml:canGetDataFrameAnalytics'],
+        tags: [
+          'access:ml:canViewMlNodes',
+          'access:ml:canGetDataFrameAnalytics',
+          'access:ml:canGetJobs',
+        ],
       },
     },
     routeGuard.fullLicenseAPIGuard(async ({ client, mlClient, request, response }) => {
@@ -281,36 +285,4 @@ export function trainedModelsRoutes({ router, routeGuard }: RouteInitialization)
       }
     })
   );
-
-  /**
-   * @apiGroup TrainedModels
-   *
-   * @api {get} /api/ml/trained_models/:modelId/deployment/_stats Get trained model deployment stats
-   * @apiName GetTrainedModelDeploymentStats
-   * @apiDescription Gets trained model deployment stats.
-   */
-  router.get(
-    {
-      path: '/api/ml/trained_models/{modelId}/deployment/_stats',
-      validate: {
-        params: modelIdSchema,
-      },
-      options: {
-        tags: ['access:ml:canGetDataFrameAnalytics'],
-      },
-    },
-    routeGuard.fullLicenseAPIGuard(async ({ mlClient, request, response }) => {
-      try {
-        const { modelId } = request.params;
-        const { body } = await mlClient.getTrainedModelDeploymentStats({
-          model_id: modelId,
-        });
-        return response.ok({
-          body,
-        });
-      } catch (e) {
-        return response.customError(wrapError(e));
-      }
-    })
-  );
 }
diff --git a/x-pack/plugins/security_solution/common/machine_learning/empty_ml_capabilities.ts b/x-pack/plugins/security_solution/common/machine_learning/empty_ml_capabilities.ts
index 772c16fc9cb9..79a0084f9192 100644
--- a/x-pack/plugins/security_solution/common/machine_learning/empty_ml_capabilities.ts
+++ b/x-pack/plugins/security_solution/common/machine_learning/empty_ml_capabilities.ts
@@ -6,41 +6,10 @@
  */
 
 import { MlCapabilitiesResponse } from '../../../ml/common/types/capabilities';
+import { getDefaultMlCapabilities } from '../../../ml/common';
 
 export const emptyMlCapabilities: MlCapabilitiesResponse = {
-  capabilities: {
-    canAccessML: false,
-    canGetAnnotations: false,
-    canCreateAnnotation: false,
-    canDeleteAnnotation: false,
-    canGetJobs: false,
-    canCreateJob: false,
-    canDeleteJob: false,
-    canOpenJob: false,
-    canCloseJob: false,
-    canResetJob: false,
-    canForecastJob: false,
-    canGetDatafeeds: false,
-    canStartStopDatafeed: false,
-    canUpdateJob: false,
-    canUpdateDatafeed: false,
-    canPreviewDatafeed: false,
-    canGetCalendars: false,
-    canCreateCalendar: false,
-    canDeleteCalendar: false,
-    canGetFilters: false,
-    canCreateFilter: false,
-    canDeleteFilter: false,
-    canFindFileStructure: false,
-    canCreateDatafeed: false,
-    canDeleteDatafeed: false,
-    canGetDataFrameAnalytics: false,
-    canDeleteDataFrameAnalytics: false,
-    canCreateDataFrameAnalytics: false,
-    canStartStopDataFrameAnalytics: false,
-    canCreateMlAlerts: false,
-    canUseMlAlerts: false,
-  },
+  capabilities: getDefaultMlCapabilities(),
   isPlatinumOrTrialLicense: false,
   mlFeatureEnabledInSpace: false,
   upgradeInProgress: false,
diff --git a/x-pack/plugins/translations/translations/ja-JP.json b/x-pack/plugins/translations/translations/ja-JP.json
index 5645ebfebf34..a681581edbf8 100644
--- a/x-pack/plugins/translations/translations/ja-JP.json
+++ b/x-pack/plugins/translations/translations/ja-JP.json
@@ -14116,7 +14116,6 @@
     "xpack.lens.editorFrame.noColorIndicatorLabel": "このディメンションには個別の色がありません",
     "xpack.lens.editorFrame.paletteColorIndicatorLabel": "このディメンションはパレットを使用しています",
     "xpack.lens.editorFrame.previewErrorLabel": "レンダリングのプレビューに失敗しました",
-    "xpack.lens.editorFrame.requiredDimensionWarningLabel": "必要な次元",
     "xpack.lens.editorFrame.suggestionPanelTitle": "提案",
     "xpack.lens.editorFrame.workspaceLabel": "ワークスペース",
     "xpack.lens.embeddable.failure": "ビジュアライゼーションを表示できませんでした",
@@ -17555,7 +17554,7 @@
     "xpack.ml.ruleEditor.scopeSection.noPermissionToViewFilterListsTitle": "フィルターリストを表示するパーミッションがありません",
     "xpack.ml.ruleEditor.scopeSection.scopeTitle": "範囲",
     "xpack.ml.ruleEditor.selectRuleAction.createRuleLinkText": "ルールを作成",
-    "xpack.ml.ruleEditor.selectRuleAction.orText": "OR ",
+    "xpack.ml.ruleEditor.selectRuleAction.orText": "OR ",
     "xpack.ml.ruleEditor.typicalAppliesTypeText": "通常",
     "xpack.ml.sampleDataLinkLabel": "ML ジョブ",
     "xpack.ml.settings.anomalyDetection.anomalyDetectionTitle": "異常検知",
@@ -17806,8 +17805,6 @@
     "xpack.ml.trainedModels.modelsList.deleteModelsButtonLabel": "削除",
     "xpack.ml.trainedModels.modelsList.disableSelectableMessage": "モデルにはパイプラインが関連付けられています",
     "xpack.ml.trainedModels.modelsList.expandedRow.analyticsConfigTitle": "分析構成",
-    "xpack.ml.trainedModels.modelsList.expandedRow.byPipelineTitle": "パイプライン別",
-    "xpack.ml.trainedModels.modelsList.expandedRow.byProcessorTitle": "プロセッサー別",
     "xpack.ml.trainedModels.modelsList.expandedRow.configTabLabel": "構成",
     "xpack.ml.trainedModels.modelsList.expandedRow.detailsTabLabel": "詳細",
     "xpack.ml.trainedModels.modelsList.expandedRow.detailsTitle": "詳細",
@@ -23489,9 +23486,9 @@
     "xpack.securitySolution.open.timeline.showingLabel": "表示中:",
     "xpack.securitySolution.open.timeline.singleTemplateLabel": "テンプレート",
     "xpack.securitySolution.open.timeline.singleTimelineLabel": "タイムライン",
-    "xpack.securitySolution.open.timeline.successfullyDeletedTimelinesTitle": "{totalTimelines, plural, =0 {すべてのタイムライン} other {{totalTimelines} 個のタイムライン}}の削除が正常に完了しました",
+    "xpack.securitySolution.open.timeline.successfullyDeletedTimelinesTitle": "{totalTimelines, plural, =0 {すべてのタイムライン} other {{totalTimelines} 個のタイムライン}}の削除が正常に完了しました",
     "xpack.securitySolution.open.timeline.successfullyDeletedTimelineTemplatesTitle": "{totalTimelineTemplates, plural, =0 {すべてのタイムライン} other {{totalTimelineTemplates}個のタイムラインテンプレート}}が正常に削除されました",
-    "xpack.securitySolution.open.timeline.successfullyExportedTimelinesTitle": "{totalTimelines, plural, =0 {すべてのタイムライン} other {{totalTimelines} 個のタイムライン}}のエクスポートが正常に完了しました",
+    "xpack.securitySolution.open.timeline.successfullyExportedTimelinesTitle": "{totalTimelines, plural, =0 {すべてのタイムライン} other {{totalTimelines} 個のタイムライン}}のエクスポートが正常に完了しました",
     "xpack.securitySolution.open.timeline.successfullyExportedTimelineTemplatesTitle": "{totalTimelineTemplates, plural, =0 {すべてのタイムライン} other {{totalTimelineTemplates} タイムラインテンプレート}}が正常にエクスポートされました",
     "xpack.securitySolution.open.timeline.timelineNameTableHeader": "タイムライン名",
     "xpack.securitySolution.open.timeline.timelineTemplateNameTableHeader": "テンプレート名",
@@ -26568,9 +26565,7 @@
     "xpack.upgradeAssistant.overview.deprecationLogs.updateErrorMessage": "ログ状態を更新できませんでした。",
     "xpack.upgradeAssistant.overview.deprecationsCountCheckpointTitle": "廃止予定の問題を解決して変更を検証",
     "xpack.upgradeAssistant.overview.documentationLinkText": "ドキュメント",
-    "xpack.upgradeAssistant.overview.fixIssuesStepDescription": "Elastic 8.xとの互換になるように、ElasticsearchおよびKibanaデプロイを更新します。アップグレード前に、重大な問題を解決する必要があります。警告の問題は独自の裁量で無視できます。",
     "xpack.upgradeAssistant.overview.fixIssuesStepTitle": "廃止予定設定を確認し、問題を解決",
-    "xpack.upgradeAssistant.overview.identifyStepTitle": "廃止予定APIの使用を特定し、アプリケーションを更新",
     "xpack.upgradeAssistant.overview.loadingLogsLabel": "廃止予定ログ収集状態を読み込んでいます...",
     "xpack.upgradeAssistant.overview.observe.discoveryDescription": "廃止予定ログを検索およびフィルターし、必要な変更のタイプを把握します。",
     "xpack.upgradeAssistant.overview.observe.observabilityDescription": "使用中のAPIのうち廃止予定のAPIと、更新が必要なアプリケーションを特定できます。",
diff --git a/x-pack/plugins/translations/translations/zh-CN.json b/x-pack/plugins/translations/translations/zh-CN.json
index 97d68b36baaa..ab953dcdb49a 100644
--- a/x-pack/plugins/translations/translations/zh-CN.json
+++ b/x-pack/plugins/translations/translations/zh-CN.json
@@ -14304,7 +14304,6 @@
     "xpack.lens.editorFrame.noColorIndicatorLabel": "此维度没有单独的颜色",
     "xpack.lens.editorFrame.paletteColorIndicatorLabel": "此维度正在使用调色板",
     "xpack.lens.editorFrame.previewErrorLabel": "预览呈现失败",
-    "xpack.lens.editorFrame.requiredDimensionWarningLabel": "所需尺寸",
     "xpack.lens.editorFrame.suggestionPanelTitle": "建议",
     "xpack.lens.editorFrame.workspaceLabel": "工作区",
     "xpack.lens.embeddable.failure": "无法显示可视化",
@@ -17820,7 +17819,7 @@
     "xpack.ml.ruleEditor.scopeSection.noPermissionToViewFilterListsTitle": "您无权查看筛选列表",
     "xpack.ml.ruleEditor.scopeSection.scopeTitle": "范围",
     "xpack.ml.ruleEditor.selectRuleAction.createRuleLinkText": "创建规则",
-    "xpack.ml.ruleEditor.selectRuleAction.orText": "或 ",
+    "xpack.ml.ruleEditor.selectRuleAction.orText": "或 ",
     "xpack.ml.ruleEditor.typicalAppliesTypeText": "典型",
     "xpack.ml.sampleDataLinkLabel": "ML 作业",
     "xpack.ml.settings.anomalyDetection.anomalyDetectionTitle": "异常检测",
@@ -18081,8 +18080,6 @@
     "xpack.ml.trainedModels.modelsList.deleteModelsButtonLabel": "删除",
     "xpack.ml.trainedModels.modelsList.disableSelectableMessage": "模型有关联的管道",
     "xpack.ml.trainedModels.modelsList.expandedRow.analyticsConfigTitle": "分析配置",
-    "xpack.ml.trainedModels.modelsList.expandedRow.byPipelineTitle": "按管道",
-    "xpack.ml.trainedModels.modelsList.expandedRow.byProcessorTitle": "按处理器",
     "xpack.ml.trainedModels.modelsList.expandedRow.configTabLabel": "配置",
     "xpack.ml.trainedModels.modelsList.expandedRow.detailsTabLabel": "详情",
     "xpack.ml.trainedModels.modelsList.expandedRow.detailsTitle": "详情",
@@ -27026,9 +27023,7 @@
     "xpack.upgradeAssistant.overview.deprecationLogs.updateErrorMessage": "无法更新日志记录状态。",
     "xpack.upgradeAssistant.overview.deprecationsCountCheckpointTitle": "解决弃用问题并验证您的更改",
     "xpack.upgradeAssistant.overview.documentationLinkText": "文档",
-    "xpack.upgradeAssistant.overview.fixIssuesStepDescription": "更新您的 Elasticsearch 和 Kibana 部署以兼容 Elastic 8.x。在升级之前必须解决紧急问题。您可以酌情忽略警告问题。",
     "xpack.upgradeAssistant.overview.fixIssuesStepTitle": "复查已弃用设置并解决问题",
-    "xpack.upgradeAssistant.overview.identifyStepTitle": "识别已弃用 API 的使用并更新您的应用程序",
     "xpack.upgradeAssistant.overview.loadingLogsLabel": "正在加载弃用日志收集状态……",
     "xpack.upgradeAssistant.overview.observe.discoveryDescription": "搜索并筛选弃用日志以了解需要进行的更改类型。",
     "xpack.upgradeAssistant.overview.observe.observabilityDescription": "深入了解正在使用哪些已弃用 API 以及需要更新哪些应用程序。",
diff --git a/x-pack/plugins/upgrade_assistant/__jest__/client_integration/app/cluster_upgrade.test.tsx b/x-pack/plugins/upgrade_assistant/__jest__/client_integration/app/cluster_upgrade.test.tsx
index 043c649b39bc..7276d005844c 100644
--- a/x-pack/plugins/upgrade_assistant/__jest__/client_integration/app/cluster_upgrade.test.tsx
+++ b/x-pack/plugins/upgrade_assistant/__jest__/client_integration/app/cluster_upgrade.test.tsx
@@ -36,9 +36,14 @@ describe('Cluster upgrade', () => {
     });
   });
 
+  // The way we detect if we are currently upgrading or if the upgrade has been completed is if
+  // we ever get back a 426 error in *any* API response that UA makes. For that reason we can
+  // just mock one of the APIs that are being called from the overview page to return an error
+  // in order to trigger these interstitial states. In this case we're going to mock the
+  // `es deprecations` response.
   describe('when cluster is in the process of a rolling upgrade', () => {
     beforeEach(async () => {
-      httpRequestsMockHelpers.setLoadDeprecationLoggingResponse(undefined, {
+      httpRequestsMockHelpers.setLoadEsDeprecationsResponse(undefined, {
         statusCode: 426,
         message: '',
         attributes: {
@@ -62,7 +67,7 @@ describe('Cluster upgrade', () => {
 
   describe('when cluster has been upgraded', () => {
     beforeEach(async () => {
-      httpRequestsMockHelpers.setLoadDeprecationLoggingResponse(undefined, {
+      httpRequestsMockHelpers.setLoadEsDeprecationsResponse(undefined, {
         statusCode: 426,
         message: '',
         attributes: {
diff --git a/x-pack/plugins/upgrade_assistant/__jest__/client_integration/es_deprecation_logs/es_deprecation_logs.helpers.ts b/x-pack/plugins/upgrade_assistant/__jest__/client_integration/es_deprecation_logs/es_deprecation_logs.helpers.ts
new file mode 100644
index 000000000000..11784d026950
--- /dev/null
+++ b/x-pack/plugins/upgrade_assistant/__jest__/client_integration/es_deprecation_logs/es_deprecation_logs.helpers.ts
@@ -0,0 +1,80 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License
+ * 2.0; you may not use this file except in compliance with the Elastic License
+ * 2.0.
+ */
+
+import { act } from 'react-dom/test-utils';
+import { registerTestBed, TestBed, AsyncTestBedConfig } from '@kbn/test/jest';
+import { EsDeprecationLogs } from '../../../public/application/components/es_deprecation_logs';
+import { WithAppDependencies } from '../helpers';
+
+const testBedConfig: AsyncTestBedConfig = {
+  memoryRouter: {
+    initialEntries: ['/es_deprecation_logs'],
+    componentRoutePath: '/es_deprecation_logs',
+  },
+  doMountAsync: true,
+};
+
+export type EsDeprecationLogsTestBed = TestBed & {
+  actions: ReturnType<typeof createActions>;
+};
+
+const createActions = (testBed: TestBed) => {
+  /**
+   * User Actions
+   */
+
+  const clickDeprecationToggle = async () => {
+    const { find, component } = testBed;
+
+    await act(async () => {
+      find('deprecationLoggingToggle').simulate('click');
+    });
+
+    component.update();
+  };
+
+  const clickRetryButton = async () => {
+    const { find, component } = testBed;
+
+    await act(async () => {
+      find('retryButton').simulate('click');
+    });
+
+    component.update();
+  };
+
+  const clickResetButton = async () => {
+    const { find, component } = testBed;
+
+    await act(async () => {
+      find('resetLastStoredDate').simulate('click');
+    });
+
+    component.update();
+  };
+
+  return {
+    clickDeprecationToggle,
+    clickRetryButton,
+    clickResetButton,
+  };
+};
+
+export const setupESDeprecationLogsPage = async (
+  overrides?: Record<string, unknown>
+): Promise<EsDeprecationLogsTestBed> => {
+  const initTestBed = registerTestBed(
+    WithAppDependencies(EsDeprecationLogs, overrides),
+    testBedConfig
+  );
+  const testBed = await initTestBed();
+
+  return {
+    ...testBed,
+    actions: createActions(testBed),
+  };
+};
diff --git a/x-pack/plugins/upgrade_assistant/__jest__/client_integration/overview/fix_logs_step/fix_logs_step.test.tsx b/x-pack/plugins/upgrade_assistant/__jest__/client_integration/es_deprecation_logs/es_deprecation_logs.test.tsx
similarity index 80%
rename from x-pack/plugins/upgrade_assistant/__jest__/client_integration/overview/fix_logs_step/fix_logs_step.test.tsx
rename to x-pack/plugins/upgrade_assistant/__jest__/client_integration/es_deprecation_logs/es_deprecation_logs.test.tsx
index 8b68f5ee449a..8d97fc389736 100644
--- a/x-pack/plugins/upgrade_assistant/__jest__/client_integration/overview/fix_logs_step/fix_logs_step.test.tsx
+++ b/x-pack/plugins/upgrade_assistant/__jest__/client_integration/es_deprecation_logs/es_deprecation_logs.test.tsx
@@ -6,13 +6,24 @@
  */
 
 import { act } from 'react-dom/test-utils';
+import {
+  EsDeprecationLogsTestBed,
+  setupESDeprecationLogsPage,
+} from './es_deprecation_logs.helpers';
+import { setupEnvironment, advanceTime } from '../helpers';
+import { DeprecationLoggingStatus } from '../../../common/types';
+import {
+  DEPRECATION_LOGS_INDEX,
+  DEPRECATION_LOGS_SOURCE_ID,
+  DEPRECATION_LOGS_COUNT_POLL_INTERVAL_MS,
+} from '../../../common/constants';
 
 // Once the logs team register the kibana locators in their app, we should be able
 // to remove this mock and follow a similar approach to how discover link is tested.
 // See: https://github.com/elastic/kibana/issues/104855
 const MOCKED_TIME = '2021-09-05T10:49:01.805Z';
-jest.mock('../../../../public/application/lib/logs_checkpoint', () => {
-  const originalModule = jest.requireActual('../../../../public/application/lib/logs_checkpoint');
+jest.mock('../../../public/application/lib/logs_checkpoint', () => {
+  const originalModule = jest.requireActual('../../../public/application/lib/logs_checkpoint');
 
   return {
     __esModule: true,
@@ -21,83 +32,30 @@ jest.mock('../../../../public/application/lib/logs_checkpoint', () => {
   };
 });
 
-import { DeprecationLoggingStatus } from '../../../../common/types';
-import { OverviewTestBed, setupOverviewPage } from '../overview.helpers';
-import { setupEnvironment, advanceTime } from '../../helpers';
-import {
-  DEPRECATION_LOGS_INDEX,
-  DEPRECATION_LOGS_SOURCE_ID,
-  DEPRECATION_LOGS_COUNT_POLL_INTERVAL_MS,
-} from '../../../../common/constants';
-
 const getLoggingResponse = (toggle: boolean): DeprecationLoggingStatus => ({
   isDeprecationLogIndexingEnabled: toggle,
   isDeprecationLoggingEnabled: toggle,
 });
 
-describe('Overview - Fix deprecation logs step', () => {
-  let testBed: OverviewTestBed;
+describe('ES deprecation logs', () => {
+  let testBed: EsDeprecationLogsTestBed;
   const { server, httpRequestsMockHelpers } = setupEnvironment();
 
   beforeEach(async () => {
     httpRequestsMockHelpers.setLoadDeprecationLoggingResponse(getLoggingResponse(true));
-    testBed = await setupOverviewPage();
-
-    const { component } = testBed;
-    component.update();
+    testBed = await setupESDeprecationLogsPage();
+    testBed.component.update();
   });
 
   afterAll(() => {
     server.restore();
   });
 
-  describe('Step status', () => {
-    test(`It's complete when there are no deprecation logs since last checkpoint`, async () => {
-      httpRequestsMockHelpers.setLoadDeprecationLogsCountResponse({ count: 0 });
-
-      await act(async () => {
-        testBed = await setupOverviewPage();
-      });
-
-      const { exists, component } = testBed;
-
-      component.update();
-
-      expect(exists(`fixLogsStep-complete`)).toBe(true);
-    });
-
-    test(`It's incomplete when there are deprecation logs since last checkpoint`, async () => {
-      httpRequestsMockHelpers.setLoadDeprecationLogsCountResponse({ count: 5 });
-
-      await act(async () => {
-        testBed = await setupOverviewPage();
-      });
-
-      const { exists, component } = testBed;
-
-      component.update();
-
-      expect(exists(`fixLogsStep-incomplete`)).toBe(true);
-    });
-
-    test(`It's incomplete when log collection is disabled `, async () => {
-      httpRequestsMockHelpers.setLoadDeprecationLogsCountResponse({ count: 0 });
-
-      await act(async () => {
-        testBed = await setupOverviewPage();
-      });
-
-      const { actions, exists, component } = testBed;
-
-      component.update();
-
-      expect(exists(`fixLogsStep-complete`)).toBe(true);
-
-      httpRequestsMockHelpers.setUpdateDeprecationLoggingResponse(getLoggingResponse(false));
-
-      await actions.clickDeprecationToggle();
+  describe('Documentation link', () => {
+    test('Has a link for migration info api docs in page header', () => {
+      const { exists } = testBed;
 
-      expect(exists(`fixLogsStep-incomplete`)).toBe(true);
+      expect(exists('documentationLink')).toBe(true);
     });
   });
 
@@ -123,7 +81,7 @@ describe('Overview - Fix deprecation logs step', () => {
       });
 
       await act(async () => {
-        testBed = await setupOverviewPage();
+        testBed = await setupESDeprecationLogsPage();
       });
 
       const { exists, component } = testBed;
@@ -159,7 +117,7 @@ describe('Overview - Fix deprecation logs step', () => {
       httpRequestsMockHelpers.setLoadDeprecationLoggingResponse(undefined, error);
 
       await act(async () => {
-        testBed = await setupOverviewPage();
+        testBed = await setupESDeprecationLogsPage();
       });
 
       const { component, exists } = testBed;
@@ -176,7 +134,7 @@ describe('Overview - Fix deprecation logs step', () => {
       });
 
       await act(async () => {
-        testBed = await setupOverviewPage();
+        testBed = await setupESDeprecationLogsPage();
       });
 
       const { exists, component } = testBed;
@@ -196,7 +154,7 @@ describe('Overview - Fix deprecation logs step', () => {
 
     test('Has a link to see logs in observability app', async () => {
       await act(async () => {
-        testBed = await setupOverviewPage({
+        testBed = await setupESDeprecationLogsPage({
           http: {
             basePath: {
               prepend: (url: string) => url,
@@ -228,7 +186,7 @@ describe('Overview - Fix deprecation logs step', () => {
 
     test('Has a link to see logs in discover app', async () => {
       await act(async () => {
-        testBed = await setupOverviewPage();
+        testBed = await setupESDeprecationLogsPage();
       });
 
       const { exists, component, find } = testBed;
@@ -257,7 +215,7 @@ describe('Overview - Fix deprecation logs step', () => {
       });
 
       await act(async () => {
-        testBed = await setupOverviewPage();
+        testBed = await setupESDeprecationLogsPage();
       });
 
       const { find, exists, component } = testBed;
@@ -274,7 +232,7 @@ describe('Overview - Fix deprecation logs step', () => {
       });
 
       await act(async () => {
-        testBed = await setupOverviewPage();
+        testBed = await setupESDeprecationLogsPage();
       });
 
       const { find, exists, component } = testBed;
@@ -295,7 +253,7 @@ describe('Overview - Fix deprecation logs step', () => {
       httpRequestsMockHelpers.setLoadDeprecationLogsCountResponse(undefined, error);
 
       await act(async () => {
-        testBed = await setupOverviewPage();
+        testBed = await setupESDeprecationLogsPage();
       });
 
       const { exists, actions, component } = testBed;
@@ -319,7 +277,7 @@ describe('Overview - Fix deprecation logs step', () => {
       });
 
       await act(async () => {
-        testBed = await setupOverviewPage();
+        testBed = await setupESDeprecationLogsPage();
       });
 
       const { exists, actions, component } = testBed;
@@ -351,7 +309,7 @@ describe('Overview - Fix deprecation logs step', () => {
 
       const addDanger = jest.fn();
       await act(async () => {
-        testBed = await setupOverviewPage({
+        testBed = await setupESDeprecationLogsPage({
           services: {
             core: {
               notifications: {
@@ -389,17 +347,17 @@ describe('Overview - Fix deprecation logs step', () => {
           count: 0,
         });
 
-        testBed = await setupOverviewPage();
+        testBed = await setupESDeprecationLogsPage();
       });
 
       afterEach(() => {
         jest.useRealTimers();
       });
 
-      test('renders step as incomplete when a success state is followed by an error state', async () => {
+      test('success state is followed by an error state', async () => {
         const { exists } = testBed;
 
-        expect(exists('fixLogsStep-complete')).toBe(true);
+        expect(exists('resetLastStoredDate')).toBe(true);
 
         // second request will error
         const error = {
@@ -413,7 +371,7 @@ describe('Overview - Fix deprecation logs step', () => {
         await advanceTime(DEPRECATION_LOGS_COUNT_POLL_INTERVAL_MS);
         testBed.component.update();
 
-        expect(exists('fixLogsStep-incomplete')).toBe(true);
+        expect(exists('errorCallout')).toBe(true);
       });
     });
   });
@@ -425,7 +383,7 @@ describe('Overview - Fix deprecation logs step', () => {
 
     test('It shows copy with compatibility api header advice', async () => {
       await act(async () => {
-        testBed = await setupOverviewPage();
+        testBed = await setupESDeprecationLogsPage();
       });
 
       const { exists, component } = testBed;
@@ -449,7 +407,7 @@ describe('Overview - Fix deprecation logs step', () => {
 
     test(`doesn't show analyze and resolve logs if it doesn't have the right privileges`, async () => {
       await act(async () => {
-        testBed = await setupOverviewPage({
+        testBed = await setupESDeprecationLogsPage({
           privileges: {
             hasAllPrivileges: false,
             missingPrivileges: {
diff --git a/x-pack/plugins/upgrade_assistant/__jest__/client_integration/overview/overview.helpers.ts b/x-pack/plugins/upgrade_assistant/__jest__/client_integration/overview/overview.helpers.ts
index d8bd6a9ff5d8..34abaed727bd 100644
--- a/x-pack/plugins/upgrade_assistant/__jest__/client_integration/overview/overview.helpers.ts
+++ b/x-pack/plugins/upgrade_assistant/__jest__/client_integration/overview/overview.helpers.ts
@@ -27,36 +27,6 @@ const createActions = (testBed: TestBed) => {
    * User Actions
    */
 
-  const clickDeprecationToggle = async () => {
-    const { find, component } = testBed;
-
-    await act(async () => {
-      find('deprecationLoggingToggle').simulate('click');
-    });
-
-    component.update();
-  };
-
-  const clickRetryButton = async () => {
-    const { find, component } = testBed;
-
-    await act(async () => {
-      find('retryButton').simulate('click');
-    });
-
-    component.update();
-  };
-
-  const clickResetButton = async () => {
-    const { find, component } = testBed;
-
-    await act(async () => {
-      find('resetLastStoredDate').simulate('click');
-    });
-
-    component.update();
-  };
-
   const clickViewSystemIndicesState = async () => {
     const { find, component } = testBed;
 
@@ -78,9 +48,6 @@ const createActions = (testBed: TestBed) => {
   };
 
   return {
-    clickDeprecationToggle,
-    clickRetryButton,
-    clickResetButton,
     clickViewSystemIndicesState,
     clickRetrySystemIndicesButton,
   };
diff --git a/x-pack/plugins/upgrade_assistant/public/application/app.tsx b/x-pack/plugins/upgrade_assistant/public/application/app.tsx
index 9ac90e5d81f4..70350b6d56ec 100644
--- a/x-pack/plugins/upgrade_assistant/public/application/app.tsx
+++ b/x-pack/plugins/upgrade_assistant/public/application/app.tsx
@@ -17,7 +17,13 @@ import { ClusterUpgradeState } from '../../common/types';
 import { APP_WRAPPER_CLASS, GlobalFlyout, AuthorizationProvider } from '../shared_imports';
 import { AppDependencies } from '../types';
 import { AppContextProvider, useAppContext } from './app_context';
-import { EsDeprecations, ComingSoonPrompt, KibanaDeprecations, Overview } from './components';
+import {
+  EsDeprecations,
+  EsDeprecationLogs,
+  ComingSoonPrompt,
+  KibanaDeprecations,
+  Overview,
+} from './components';
 
 const { GlobalFlyoutProvider } = GlobalFlyout;
 
@@ -112,6 +118,7 @@ const AppHandlingClusterUpgradeState: React.FunctionComponent = () => {
     <Switch>
       <Route exact path="/overview" component={Overview} />
       <Route exact path="/es_deprecations" component={EsDeprecations} />
+      <Route exact path="/es_deprecation_logs" component={EsDeprecationLogs} />
       <Route exact path="/kibana_deprecations" component={KibanaDeprecations} />
       <Redirect from="/" to="/overview" />
     </Switch>
diff --git a/x-pack/plugins/upgrade_assistant/public/application/components/es_deprecation_logs/es_deprecation_logs.tsx b/x-pack/plugins/upgrade_assistant/public/application/components/es_deprecation_logs/es_deprecation_logs.tsx
new file mode 100644
index 000000000000..f77856675829
--- /dev/null
+++ b/x-pack/plugins/upgrade_assistant/public/application/components/es_deprecation_logs/es_deprecation_logs.tsx
@@ -0,0 +1,74 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License
+ * 2.0; you may not use this file except in compliance with the Elastic License
+ * 2.0.
+ */
+
+import React, { FunctionComponent, useEffect } from 'react';
+
+import {
+  EuiPageHeader,
+  EuiButtonEmpty,
+  EuiSpacer,
+  EuiPageBody,
+  EuiPageContentBody,
+} from '@elastic/eui';
+import { i18n } from '@kbn/i18n';
+import { METRIC_TYPE } from '@kbn/analytics';
+import { FormattedMessage } from '@kbn/i18n/react';
+
+import { useAppContext } from '../../app_context';
+import { uiMetricService, UIM_ES_DEPRECATION_LOGS_PAGE_LOAD } from '../../lib/ui_metric';
+import { FixDeprecationLogs } from './fix_deprecation_logs';
+
+export const EsDeprecationLogs: FunctionComponent = () => {
+  const {
+    services: {
+      breadcrumbs,
+      core: { docLinks },
+    },
+  } = useAppContext();
+
+  useEffect(() => {
+    uiMetricService.trackUiMetric(METRIC_TYPE.LOADED, UIM_ES_DEPRECATION_LOGS_PAGE_LOAD);
+  }, []);
+
+  useEffect(() => {
+    breadcrumbs.setBreadcrumbs('esDeprecationLogs');
+  }, [breadcrumbs]);
+
+  return (
+    <EuiPageBody restrictWidth={true} data-test-subj="esDeprecationLogs">
+      <EuiPageContentBody color="transparent" paddingSize="none">
+        <EuiPageHeader
+          bottomBorder
+          pageTitle={i18n.translate('xpack.upgradeAssistant.esDeprecationLogs.pageTitle', {
+            defaultMessage: 'Elasticsearch deprecation logs',
+          })}
+          description={i18n.translate('xpack.upgradeAssistant.esDeprecationLogs.pageDescription', {
+            defaultMessage:
+              'Review the deprecation logs to determine if your applications are using any deprecated APIs. Update your applications to prevent errors or changes in behavior after you upgrade.',
+          })}
+          rightSideItems={[
+            <EuiButtonEmpty
+              href={docLinks.links.elasticsearch.migrationApiDeprecation}
+              target="_blank"
+              iconType="help"
+              data-test-subj="documentationLink"
+            >
+              <FormattedMessage
+                id="xpack.upgradeAssistant.esDeprecationLogs.documentationLinkText"
+                defaultMessage="Documentation"
+              />
+            </EuiButtonEmpty>,
+          ]}
+        />
+
+        <EuiSpacer size="l" />
+
+        <FixDeprecationLogs />
+      </EuiPageContentBody>
+    </EuiPageBody>
+  );
+};
diff --git a/x-pack/plugins/upgrade_assistant/public/application/components/overview/fix_logs_step/deprecation_logging_toggle/_deprecation_logging_toggle.scss b/x-pack/plugins/upgrade_assistant/public/application/components/es_deprecation_logs/fix_deprecation_logs/deprecation_logging_toggle/_deprecation_logging_toggle.scss
similarity index 100%
rename from x-pack/plugins/upgrade_assistant/public/application/components/overview/fix_logs_step/deprecation_logging_toggle/_deprecation_logging_toggle.scss
rename to x-pack/plugins/upgrade_assistant/public/application/components/es_deprecation_logs/fix_deprecation_logs/deprecation_logging_toggle/_deprecation_logging_toggle.scss
diff --git a/x-pack/plugins/upgrade_assistant/public/application/components/overview/fix_logs_step/deprecation_logging_toggle/deprecation_logging_toggle.tsx b/x-pack/plugins/upgrade_assistant/public/application/components/es_deprecation_logs/fix_deprecation_logs/deprecation_logging_toggle/deprecation_logging_toggle.tsx
similarity index 100%
rename from x-pack/plugins/upgrade_assistant/public/application/components/overview/fix_logs_step/deprecation_logging_toggle/deprecation_logging_toggle.tsx
rename to x-pack/plugins/upgrade_assistant/public/application/components/es_deprecation_logs/fix_deprecation_logs/deprecation_logging_toggle/deprecation_logging_toggle.tsx
diff --git a/x-pack/plugins/upgrade_assistant/public/application/components/overview/fix_logs_step/deprecation_logging_toggle/index.ts b/x-pack/plugins/upgrade_assistant/public/application/components/es_deprecation_logs/fix_deprecation_logs/deprecation_logging_toggle/index.ts
similarity index 100%
rename from x-pack/plugins/upgrade_assistant/public/application/components/overview/fix_logs_step/deprecation_logging_toggle/index.ts
rename to x-pack/plugins/upgrade_assistant/public/application/components/es_deprecation_logs/fix_deprecation_logs/deprecation_logging_toggle/index.ts
diff --git a/x-pack/plugins/upgrade_assistant/public/application/components/overview/fix_logs_step/deprecations_count_checkpoint/deprecations_count_checkpoint.tsx b/x-pack/plugins/upgrade_assistant/public/application/components/es_deprecation_logs/fix_deprecation_logs/deprecations_count_checkpoint/deprecations_count_checkpoint.tsx
similarity index 89%
rename from x-pack/plugins/upgrade_assistant/public/application/components/overview/fix_logs_step/deprecations_count_checkpoint/deprecations_count_checkpoint.tsx
rename to x-pack/plugins/upgrade_assistant/public/application/components/es_deprecation_logs/fix_deprecation_logs/deprecations_count_checkpoint/deprecations_count_checkpoint.tsx
index 6ce1fec32d66..c958eb68c86e 100644
--- a/x-pack/plugins/upgrade_assistant/public/application/components/overview/fix_logs_step/deprecations_count_checkpoint/deprecations_count_checkpoint.tsx
+++ b/x-pack/plugins/upgrade_assistant/public/application/components/es_deprecation_logs/fix_deprecation_logs/deprecations_count_checkpoint/deprecations_count_checkpoint.tsx
@@ -5,7 +5,7 @@
  * 2.0.
  */
 
-import React, { FunctionComponent, useEffect, useState } from 'react';
+import React, { FunctionComponent, useState } from 'react';
 import moment from 'moment-timezone';
 import { FormattedDate, FormattedTime, FormattedMessage } from '@kbn/i18n/react';
 import { METRIC_TYPE } from '@kbn/analytics';
@@ -54,13 +54,11 @@ const i18nTexts = {
 interface Props {
   checkpoint: string;
   setCheckpoint: (value: string) => void;
-  setHasNoDeprecationLogs: (hasNoLogs: boolean) => void;
 }
 
 export const DeprecationsCountCheckpoint: FunctionComponent<Props> = ({
   checkpoint,
   setCheckpoint,
-  setHasNoDeprecationLogs,
 }) => {
   const [isDeletingCache, setIsDeletingCache] = useState(false);
   const {
@@ -96,16 +94,6 @@ export const DeprecationsCountCheckpoint: FunctionComponent<Props> = ({
     setCheckpoint(now);
   };
 
-  useEffect(() => {
-    // Loading shouldn't invalidate the previous state.
-    if (!isLoading) {
-      // An error should invalidate the previous state.
-      setHasNoDeprecationLogs(!error && !hasLogs);
-    }
-    // Depending upon setHasNoDeprecationLogs would create an infinite loop.
-    // eslint-disable-next-line react-hooks/exhaustive-deps
-  }, [error, isLoading, hasLogs]);
-
   if (isInitialRequest && isLoading) {
     return <EuiLoadingContent lines={6} />;
   }
diff --git a/x-pack/plugins/upgrade_assistant/public/application/components/overview/fix_logs_step/deprecations_count_checkpoint/index.ts b/x-pack/plugins/upgrade_assistant/public/application/components/es_deprecation_logs/fix_deprecation_logs/deprecations_count_checkpoint/index.ts
similarity index 100%
rename from x-pack/plugins/upgrade_assistant/public/application/components/overview/fix_logs_step/deprecations_count_checkpoint/index.ts
rename to x-pack/plugins/upgrade_assistant/public/application/components/es_deprecation_logs/fix_deprecation_logs/deprecations_count_checkpoint/index.ts
diff --git a/x-pack/plugins/upgrade_assistant/public/application/components/overview/fix_logs_step/external_links.test.ts b/x-pack/plugins/upgrade_assistant/public/application/components/es_deprecation_logs/fix_deprecation_logs/external_links.test.ts
similarity index 100%
rename from x-pack/plugins/upgrade_assistant/public/application/components/overview/fix_logs_step/external_links.test.ts
rename to x-pack/plugins/upgrade_assistant/public/application/components/es_deprecation_logs/fix_deprecation_logs/external_links.test.ts
diff --git a/x-pack/plugins/upgrade_assistant/public/application/components/overview/fix_logs_step/external_links.tsx b/x-pack/plugins/upgrade_assistant/public/application/components/es_deprecation_logs/fix_deprecation_logs/external_links.tsx
similarity index 100%
rename from x-pack/plugins/upgrade_assistant/public/application/components/overview/fix_logs_step/external_links.tsx
rename to x-pack/plugins/upgrade_assistant/public/application/components/es_deprecation_logs/fix_deprecation_logs/external_links.tsx
diff --git a/x-pack/plugins/upgrade_assistant/public/application/components/overview/fix_logs_step/fix_logs_step.tsx b/x-pack/plugins/upgrade_assistant/public/application/components/es_deprecation_logs/fix_deprecation_logs/fix_deprecation_logs.tsx
similarity index 80%
rename from x-pack/plugins/upgrade_assistant/public/application/components/overview/fix_logs_step/fix_logs_step.tsx
rename to x-pack/plugins/upgrade_assistant/public/application/components/es_deprecation_logs/fix_deprecation_logs/fix_deprecation_logs.tsx
index a3e81f6edcd3..ff1cfc172905 100644
--- a/x-pack/plugins/upgrade_assistant/public/application/components/overview/fix_logs_step/fix_logs_step.tsx
+++ b/x-pack/plugins/upgrade_assistant/public/application/components/es_deprecation_logs/fix_deprecation_logs/fix_deprecation_logs.tsx
@@ -9,7 +9,6 @@ import React, { FunctionComponent, useState, useEffect } from 'react';
 import { i18n } from '@kbn/i18n';
 import { FormattedMessage } from '@kbn/i18n/react';
 import { EuiText, EuiSpacer, EuiLink, EuiCallOut, EuiCode } from '@elastic/eui';
-import type { EuiStepProps } from '@elastic/eui/src/components/steps/step';
 
 import { useAppContext } from '../../../app_context';
 import { ExternalLinks } from './external_links';
@@ -17,14 +16,10 @@ import { DeprecationsCountCheckpoint } from './deprecations_count_checkpoint';
 import { useDeprecationLogging } from './use_deprecation_logging';
 import { DeprecationLoggingToggle } from './deprecation_logging_toggle';
 import { loadLogsCheckpoint, saveLogsCheckpoint } from '../../../lib/logs_checkpoint';
-import type { OverviewStepProps } from '../../types';
 import { DEPRECATION_LOGS_INDEX } from '../../../../../common/constants';
 import { WithPrivileges, MissingPrivileges } from '../../../../shared_imports';
 
 const i18nTexts = {
-  identifyStepTitle: i18n.translate('xpack.upgradeAssistant.overview.identifyStepTitle', {
-    defaultMessage: 'Identify deprecated API use and update your applications',
-  }),
   analyzeTitle: i18n.translate('xpack.upgradeAssistant.overview.analyzeTitle', {
     defaultMessage: 'Analyze deprecation logs',
   }),
@@ -93,16 +88,11 @@ const i18nTexts = {
 };
 
 interface Props {
-  setIsComplete: OverviewStepProps['setIsComplete'];
   hasPrivileges: boolean;
   privilegesMissing: MissingPrivileges;
 }
 
-const FixLogsStep: FunctionComponent<Props> = ({
-  setIsComplete,
-  hasPrivileges,
-  privilegesMissing,
-}) => {
+const FixDeprecationLogsUI: FunctionComponent<Props> = ({ hasPrivileges, privilegesMissing }) => {
   const {
     services: {
       core: { docLinks },
@@ -126,15 +116,6 @@ const FixLogsStep: FunctionComponent<Props> = ({
     saveLogsCheckpoint(checkpoint);
   }, [checkpoint]);
 
-  useEffect(() => {
-    if (!isDeprecationLogIndexingEnabled) {
-      setIsComplete(false);
-    }
-
-    // Depending upon setIsComplete would create an infinite loop.
-    // eslint-disable-next-line react-hooks/exhaustive-deps
-  }, [isDeprecationLogIndexingEnabled]);
-
   return (
     <>
       <DeprecationLoggingToggle
@@ -189,11 +170,7 @@ const FixLogsStep: FunctionComponent<Props> = ({
             <h4>{i18nTexts.deprecationsCountCheckpointTitle}</h4>
           </EuiText>
           <EuiSpacer size="m" />
-          <DeprecationsCountCheckpoint
-            checkpoint={checkpoint}
-            setCheckpoint={setCheckpoint}
-            setHasNoDeprecationLogs={setIsComplete}
-          />
+          <DeprecationsCountCheckpoint checkpoint={checkpoint} setCheckpoint={setCheckpoint} />
 
           <EuiSpacer size="xl" />
           <EuiText data-test-subj="apiCompatibilityNoteTitle">
@@ -213,23 +190,15 @@ const FixLogsStep: FunctionComponent<Props> = ({
   );
 };
 
-export const getFixLogsStep = ({ isComplete, setIsComplete }: OverviewStepProps): EuiStepProps => {
-  const status = isComplete ? 'complete' : 'incomplete';
-
-  return {
-    status,
-    title: i18nTexts.identifyStepTitle,
-    'data-test-subj': `fixLogsStep-${status}`,
-    children: (
-      <WithPrivileges privileges={`index.${DEPRECATION_LOGS_INDEX}`}>
-        {({ hasPrivileges, privilegesMissing, isLoading }) => (
-          <FixLogsStep
-            setIsComplete={setIsComplete}
-            hasPrivileges={!isLoading && hasPrivileges}
-            privilegesMissing={privilegesMissing}
-          />
-        )}
-      </WithPrivileges>
-    ),
-  };
+export const FixDeprecationLogs = () => {
+  return (
+    <WithPrivileges privileges={`index.${DEPRECATION_LOGS_INDEX}`}>
+      {({ hasPrivileges, privilegesMissing, isLoading }) => (
+        <FixDeprecationLogsUI
+          hasPrivileges={!isLoading && hasPrivileges}
+          privilegesMissing={privilegesMissing}
+        />
+      )}
+    </WithPrivileges>
+  );
 };
diff --git a/x-pack/plugins/upgrade_assistant/public/application/components/es_deprecation_logs/fix_deprecation_logs/index.ts b/x-pack/plugins/upgrade_assistant/public/application/components/es_deprecation_logs/fix_deprecation_logs/index.ts
new file mode 100644
index 000000000000..c0af5524e3a1
--- /dev/null
+++ b/x-pack/plugins/upgrade_assistant/public/application/components/es_deprecation_logs/fix_deprecation_logs/index.ts
@@ -0,0 +1,8 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License
+ * 2.0; you may not use this file except in compliance with the Elastic License
+ * 2.0.
+ */
+
+export { FixDeprecationLogs } from './fix_deprecation_logs';
diff --git a/x-pack/plugins/upgrade_assistant/public/application/components/overview/fix_logs_step/use_deprecation_logging.ts b/x-pack/plugins/upgrade_assistant/public/application/components/es_deprecation_logs/fix_deprecation_logs/use_deprecation_logging.ts
similarity index 100%
rename from x-pack/plugins/upgrade_assistant/public/application/components/overview/fix_logs_step/use_deprecation_logging.ts
rename to x-pack/plugins/upgrade_assistant/public/application/components/es_deprecation_logs/fix_deprecation_logs/use_deprecation_logging.ts
diff --git a/x-pack/plugins/upgrade_assistant/public/application/components/es_deprecation_logs/index.ts b/x-pack/plugins/upgrade_assistant/public/application/components/es_deprecation_logs/index.ts
new file mode 100644
index 000000000000..336aa14642f7
--- /dev/null
+++ b/x-pack/plugins/upgrade_assistant/public/application/components/es_deprecation_logs/index.ts
@@ -0,0 +1,8 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License
+ * 2.0; you may not use this file except in compliance with the Elastic License
+ * 2.0.
+ */
+
+export { EsDeprecationLogs } from './es_deprecation_logs';
diff --git a/x-pack/plugins/upgrade_assistant/public/application/components/index.ts b/x-pack/plugins/upgrade_assistant/public/application/components/index.ts
index 8924fa2d355a..b84af8695ddb 100644
--- a/x-pack/plugins/upgrade_assistant/public/application/components/index.ts
+++ b/x-pack/plugins/upgrade_assistant/public/application/components/index.ts
@@ -7,5 +7,6 @@
 
 export { KibanaDeprecations } from './kibana_deprecations';
 export { EsDeprecations } from './es_deprecations';
+export { EsDeprecationLogs } from './es_deprecation_logs';
 export { ComingSoonPrompt } from './coming_soon_prompt';
 export { Overview } from './overview';
diff --git a/x-pack/plugins/upgrade_assistant/public/application/components/overview/fix_issues_step/fix_issues_step.tsx b/x-pack/plugins/upgrade_assistant/public/application/components/overview/fix_issues_step/fix_issues_step.tsx
index 61d25404b2ae..410eb695240f 100644
--- a/x-pack/plugins/upgrade_assistant/public/application/components/overview/fix_issues_step/fix_issues_step.tsx
+++ b/x-pack/plugins/upgrade_assistant/public/application/components/overview/fix_issues_step/fix_issues_step.tsx
@@ -7,11 +7,13 @@
 
 import React, { FunctionComponent, useState, useEffect } from 'react';
 
-import { EuiText, EuiFlexItem, EuiFlexGroup, EuiSpacer } from '@elastic/eui';
+import { EuiText, EuiFlexItem, EuiFlexGroup, EuiSpacer, EuiLink } from '@elastic/eui';
 import { i18n } from '@kbn/i18n';
 import { FormattedMessage } from '@kbn/i18n/react';
 import type { EuiStepProps } from '@elastic/eui/src/components/steps/step';
 
+import { DEPRECATION_LOGS_INDEX } from '../../../../../common/constants';
+import { WithPrivileges } from '../../../../shared_imports';
 import type { OverviewStepProps } from '../../types';
 import { EsDeprecationIssuesPanel, KibanaDeprecationIssuesPanel } from './components';
 
@@ -49,10 +51,48 @@ const FixIssuesStep: FunctionComponent<Props> = ({ setIsComplete }) => {
   );
 };
 
+interface CustomProps {
+  navigateToEsDeprecationLogs: () => void;
+}
+
+const AccessDeprecationLogsMessage = ({ navigateToEsDeprecationLogs }: CustomProps) => {
+  return (
+    <WithPrivileges privileges={`index.${DEPRECATION_LOGS_INDEX}`}>
+      {({ hasPrivileges, isLoading }) => {
+        if (isLoading || !hasPrivileges) {
+          // Don't show the message with the link to access deprecation logs
+          // to users who can't access the UI anyways.
+          return null;
+        }
+
+        return (
+          <FormattedMessage
+            id="xpack.upgradeAssistant.overview.accessEsDeprecationLogsLabel"
+            defaultMessage="If you have application code that calls Elasticsearch APIs, review the {esDeprecationLogsLink} to make sure you are not using deprecated APIs."
+            values={{
+              esDeprecationLogsLink: (
+                <EuiLink
+                  onClick={navigateToEsDeprecationLogs}
+                  data-test-subj="viewElasticsearchDeprecationLogs"
+                >
+                  {i18n.translate('xpack.upgradeAssistant.overview.esDeprecationLogsLink', {
+                    defaultMessage: 'Elasticsearch deprecation logs',
+                  })}
+                </EuiLink>
+              ),
+            }}
+          />
+        );
+      }}
+    </WithPrivileges>
+  );
+};
+
 export const getFixIssuesStep = ({
   isComplete,
   setIsComplete,
-}: OverviewStepProps): EuiStepProps => {
+  navigateToEsDeprecationLogs,
+}: OverviewStepProps & CustomProps): EuiStepProps => {
   const status = isComplete ? 'complete' : 'incomplete';
 
   return {
@@ -65,7 +105,14 @@ export const getFixIssuesStep = ({
           <p>
             <FormattedMessage
               id="xpack.upgradeAssistant.overview.fixIssuesStepDescription"
-              defaultMessage="Update your Elasticsearch and Kibana deployments to be compatible with Elastic 8.x. Critical issues must be resolved before you upgrade. Warning issues can be ignored at your discretion."
+              defaultMessage="You must resolve any critical Elasticsearch and Kibana configuration issues before upgrading to Elastic 8.x. Ignoring warnings might result in differences in behavior after you upgrade. {accessDeprecationLogsMessage}"
+              values={{
+                accessDeprecationLogsMessage: (
+                  <AccessDeprecationLogsMessage
+                    navigateToEsDeprecationLogs={navigateToEsDeprecationLogs}
+                  />
+                ),
+              }}
             />
           </p>
         </EuiText>
diff --git a/x-pack/plugins/upgrade_assistant/public/application/components/overview/overview.tsx b/x-pack/plugins/upgrade_assistant/public/application/components/overview/overview.tsx
index 1e7961f8ea78..900c89671315 100644
--- a/x-pack/plugins/upgrade_assistant/public/application/components/overview/overview.tsx
+++ b/x-pack/plugins/upgrade_assistant/public/application/components/overview/overview.tsx
@@ -5,7 +5,7 @@
  * 2.0.
  */
 
-import React, { FunctionComponent, useEffect, useState } from 'react';
+import React, { useEffect, useState } from 'react';
 
 import {
   EuiSteps,
@@ -15,23 +15,23 @@ import {
   EuiSpacer,
   EuiLink,
   EuiPageBody,
-  EuiPageContent,
+  EuiPageContentBody,
 } from '@elastic/eui';
 import { i18n } from '@kbn/i18n';
 import { METRIC_TYPE } from '@kbn/analytics';
 import { FormattedMessage } from '@kbn/i18n/react';
+import { withRouter, RouteComponentProps } from 'react-router-dom';
 
 import { useAppContext } from '../../app_context';
 import { uiMetricService, UIM_OVERVIEW_PAGE_LOAD } from '../../lib/ui_metric';
 import { getBackupStep } from './backup_step';
 import { getFixIssuesStep } from './fix_issues_step';
-import { getFixLogsStep } from './fix_logs_step';
 import { getUpgradeStep } from './upgrade_step';
 import { getMigrateSystemIndicesStep } from './migrate_system_indices';
 
-type OverviewStep = 'backup' | 'migrate_system_indices' | 'fix_issues' | 'fix_logs';
+type OverviewStep = 'backup' | 'migrate_system_indices' | 'fix_issues';
 
-export const Overview: FunctionComponent = () => {
+export const Overview = withRouter(({ history }: RouteComponentProps) => {
   const {
     services: {
       breadcrumbs,
@@ -52,7 +52,6 @@ export const Overview: FunctionComponent = () => {
     backup: false,
     migrate_system_indices: false,
     fix_issues: false,
-    fix_logs: false,
   });
 
   const isStepComplete = (step: OverviewStep) => completedStepsMap[step];
@@ -65,7 +64,7 @@ export const Overview: FunctionComponent = () => {
 
   return (
     <EuiPageBody restrictWidth={true} data-test-subj="overview">
-      <EuiPageContent horizontalPosition="center" color="transparent" paddingSize="none">
+      <EuiPageContentBody color="transparent" paddingSize="none">
         <EuiPageHeader
           bottomBorder
           pageTitle={i18n.translate('xpack.upgradeAssistant.overview.pageTitle', {
@@ -114,15 +113,12 @@ export const Overview: FunctionComponent = () => {
             getFixIssuesStep({
               isComplete: isStepComplete('fix_issues'),
               setIsComplete: setCompletedStep.bind(null, 'fix_issues'),
-            }),
-            getFixLogsStep({
-              isComplete: isStepComplete('fix_logs'),
-              setIsComplete: setCompletedStep.bind(null, 'fix_logs'),
+              navigateToEsDeprecationLogs: () => history.push('/es_deprecation_logs'),
             }),
             getUpgradeStep(),
           ]}
         />
-      </EuiPageContent>
+      </EuiPageContentBody>
     </EuiPageBody>
   );
-};
+});
diff --git a/x-pack/plugins/upgrade_assistant/public/application/lib/breadcrumbs.ts b/x-pack/plugins/upgrade_assistant/public/application/lib/breadcrumbs.ts
index 3e30ffd06db1..dd1cc0135569 100644
--- a/x-pack/plugins/upgrade_assistant/public/application/lib/breadcrumbs.ts
+++ b/x-pack/plugins/upgrade_assistant/public/application/lib/breadcrumbs.ts
@@ -18,6 +18,9 @@ const i18nTexts = {
     esDeprecations: i18n.translate('xpack.upgradeAssistant.breadcrumb.esDeprecationsLabel', {
       defaultMessage: 'Elasticsearch deprecation issues',
     }),
+    esDeprecationLogs: i18n.translate('xpack.upgradeAssistant.breadcrumb.esDeprecationLogsLabel', {
+      defaultMessage: 'Elasticsearch deprecation logs',
+    }),
     kibanaDeprecations: i18n.translate(
       'xpack.upgradeAssistant.breadcrumb.kibanaDeprecationsLabel',
       {
@@ -48,6 +51,15 @@ export class BreadcrumbService {
         text: i18nTexts.breadcrumbs.esDeprecations,
       },
     ],
+    esDeprecationLogs: [
+      {
+        text: i18nTexts.breadcrumbs.overview,
+        href: '/',
+      },
+      {
+        text: i18nTexts.breadcrumbs.esDeprecationLogs,
+      },
+    ],
     kibanaDeprecations: [
       {
         text: i18nTexts.breadcrumbs.overview,
@@ -65,7 +77,9 @@ export class BreadcrumbService {
     this.setBreadcrumbsHandler = setBreadcrumbsHandler;
   }
 
-  public setBreadcrumbs(type: 'overview' | 'esDeprecations' | 'kibanaDeprecations'): void {
+  public setBreadcrumbs(
+    type: 'overview' | 'esDeprecations' | 'esDeprecationLogs' | 'kibanaDeprecations'
+  ): void {
     if (!this.setBreadcrumbsHandler) {
       throw new Error('Breadcrumb service has not been initialized');
     }
diff --git a/x-pack/plugins/upgrade_assistant/public/application/lib/ui_metric.ts b/x-pack/plugins/upgrade_assistant/public/application/lib/ui_metric.ts
index 394f046a8baf..1ac34ae53194 100644
--- a/x-pack/plugins/upgrade_assistant/public/application/lib/ui_metric.ts
+++ b/x-pack/plugins/upgrade_assistant/public/application/lib/ui_metric.ts
@@ -12,6 +12,7 @@ export const UIM_APP_NAME = 'upgrade_assistant';
 export const UIM_ES_DEPRECATIONS_PAGE_LOAD = 'es_deprecations_page_load';
 export const UIM_KIBANA_DEPRECATIONS_PAGE_LOAD = 'kibana_deprecations_page_load';
 export const UIM_OVERVIEW_PAGE_LOAD = 'overview_page_load';
+export const UIM_ES_DEPRECATION_LOGS_PAGE_LOAD = 'es_deprecation_logs_page_load';
 export const UIM_REINDEX_OPEN_FLYOUT_CLICK = 'reindex_open_flyout_click';
 export const UIM_REINDEX_CLOSE_FLYOUT_CLICK = 'reindex_close_flyout_click';
 export const UIM_REINDEX_START_CLICK = 'reindex_start_click';
diff --git a/x-pack/test/accessibility/apps/upgrade_assistant.ts b/x-pack/test/accessibility/apps/upgrade_assistant.ts
index 2c0e81a6fb83..829d0a2c4237 100644
--- a/x-pack/test/accessibility/apps/upgrade_assistant.ts
+++ b/x-pack/test/accessibility/apps/upgrade_assistant.ts
@@ -5,10 +5,10 @@
  * 2.0.
  */
 
-import type * as estypes from '@elastic/elasticsearch/lib/api/typesWithBodyKey';
+import type { IndicesCreateRequest } from '@elastic/elasticsearch/lib/api/typesWithBodyKey';
 import { FtrProviderContext } from '../ftr_provider_context';
 
-const translogSettingsIndexDeprecation: estypes.IndicesCreateRequest = {
+const translogSettingsIndexDeprecation: IndicesCreateRequest = {
   index: 'deprecated_settings',
   body: {
     settings: {
@@ -19,7 +19,7 @@ const translogSettingsIndexDeprecation: estypes.IndicesCreateRequest = {
   },
 };
 
-const multiFieldsIndexDeprecation: estypes.IndicesCreateRequest = {
+const multiFieldsIndexDeprecation: IndicesCreateRequest = {
   index: 'nested_multi_fields',
   body: {
     mappings: {
@@ -55,6 +55,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
   describe.skip('Upgrade Assistant', () => {
     before(async () => {
       await PageObjects.upgradeAssistant.navigateToPage();
+
       try {
         // Create two indices that will trigger deprecation warnings to test the ES deprecations page
         await es.indices.create(multiFieldsIndexDeprecation);
@@ -76,128 +77,113 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
       }
     });
 
-    describe('Upgrade Assistant - Overview', () => {
-      before(async () => {
+    describe('Overview page', () => {
+      beforeEach(async () => {
         await PageObjects.upgradeAssistant.navigateToPage();
-
-        try {
-          // Create two indices that will trigger deprecation warnings to test the ES deprecations page
-          await es.indices.create(multiFieldsIndexDeprecation);
-          await es.indices.create(translogSettingsIndexDeprecation);
-        } catch (e) {
-          log.debug('[Setup error] Error creating indices');
-          throw e;
-        }
+        await retry.waitFor('Upgrade Assistant overview page to be visible', async () => {
+          return testSubjects.exists('overview');
+        });
       });
 
-      after(async () => {
-        try {
-          await es.indices.delete({
-            index: [multiFieldsIndexDeprecation.index, translogSettingsIndexDeprecation.index],
-          });
-        } catch (e) {
-          log.debug('[Cleanup error] Error deleting indices');
-          throw e;
-        }
+      it('has no accessibility issues', async () => {
+        await a11y.testAppSnapshot();
       });
+    });
 
-      describe('Overview page', () => {
-        beforeEach(async () => {
-          await PageObjects.upgradeAssistant.navigateToPage();
-          await retry.waitFor('Upgrade Assistant overview page to be visible', async () => {
-            return testSubjects.exists('overview');
-          });
-        });
-
-        it('with logs collection disabled', async () => {
-          await a11y.testAppSnapshot();
-        });
+    describe('ES deprecations logs page', () => {
+      beforeEach(async () => {
+        await PageObjects.upgradeAssistant.navigateToEsDeprecationLogs();
+      });
 
-        it('with logs collection enabled', async () => {
-          await PageObjects.upgradeAssistant.clickDeprecationLoggingToggle();
+      it('with logs collection disabled', async () => {
+        await a11y.testAppSnapshot();
+      });
 
-          await retry.waitFor('UA external links title to be present', async () => {
-            return testSubjects.isDisplayed('externalLinksTitle');
-          });
+      it('with logs collection enabled', async () => {
+        await PageObjects.upgradeAssistant.clickDeprecationLoggingToggle();
 
-          await a11y.testAppSnapshot();
+        await retry.waitFor('UA external links title to be present', async () => {
+          return testSubjects.isDisplayed('externalLinksTitle');
         });
-      });
 
-      describe('Elasticsearch deprecations page', () => {
-        beforeEach(async () => {
-          await PageObjects.common.navigateToUrl(
-            'management',
-            'stack/upgrade_assistant/es_deprecations',
-            {
-              ensureCurrentUrl: false,
-              shouldLoginIfPrompted: false,
-              shouldUseHashForSubUrl: false,
-            }
-          );
-
-          await retry.waitFor('Elasticsearch deprecations table to be visible', async () => {
-            return testSubjects.exists('esDeprecationsTable');
-          });
-        });
+        await a11y.testAppSnapshot();
+      });
+    });
 
-        it('Deprecations table', async () => {
-          await a11y.testAppSnapshot();
+    describe('Elasticsearch deprecations page', () => {
+      beforeEach(async () => {
+        await PageObjects.common.navigateToUrl(
+          'management',
+          'stack/upgrade_assistant/es_deprecations',
+          {
+            ensureCurrentUrl: false,
+            shouldLoginIfPrompted: false,
+            shouldUseHashForSubUrl: false,
+          }
+        );
+
+        await retry.waitFor('Elasticsearch deprecations table to be visible', async () => {
+          return testSubjects.exists('esDeprecationsTable');
         });
+      });
 
-        it('Index settings deprecation flyout', async () => {
-          await PageObjects.upgradeAssistant.clickEsDeprecation(
-            'indexSettings' // An index setting deprecation was added in the before() hook so should be guaranteed
-          );
-          await retry.waitFor('ES index settings deprecation flyout to be visible', async () => {
-            return testSubjects.exists('indexSettingsDetails');
-          });
-          await a11y.testAppSnapshot();
-        });
+      it('Deprecations table', async () => {
+        await a11y.testAppSnapshot();
+      });
 
-        it('Default deprecation flyout', async () => {
-          await PageObjects.upgradeAssistant.clickEsDeprecation(
-            'default' // A default deprecation was added in the before() hook so should be guaranteed
-          );
-          await retry.waitFor('ES default deprecation flyout to be visible', async () => {
-            return testSubjects.exists('defaultDeprecationDetails');
-          });
-          await a11y.testAppSnapshot();
+      // Failing: See https://github.com/elastic/kibana/issues/115859
+      it.skip('Index settings deprecation flyout', async () => {
+        await PageObjects.upgradeAssistant.clickEsDeprecation(
+          'indexSettings' // An index setting deprecation was added in the before() hook so should be guaranteed
+        );
+        await retry.waitFor('ES index settings deprecation flyout to be visible', async () => {
+          return testSubjects.exists('indexSettingsDetails');
         });
+        await a11y.testAppSnapshot();
       });
 
-      describe('Kibana deprecations page', () => {
-        beforeEach(async () => {
-          await PageObjects.common.navigateToUrl(
-            'management',
-            'stack/upgrade_assistant/kibana_deprecations',
-            {
-              ensureCurrentUrl: false,
-              shouldLoginIfPrompted: false,
-              shouldUseHashForSubUrl: false,
-            }
-          );
-
-          await retry.waitFor('Kibana deprecations to be visible', async () => {
-            return testSubjects.exists('kibanaDeprecations');
-          });
+      it('Default deprecation flyout', async () => {
+        await PageObjects.upgradeAssistant.clickEsDeprecation(
+          'default' // A default deprecation was added in the before() hook so should be guaranteed
+        );
+        await retry.waitFor('ES default deprecation flyout to be visible', async () => {
+          return testSubjects.exists('defaultDeprecationDetails');
         });
+        await a11y.testAppSnapshot();
+      });
+    });
 
-        it('Deprecations table', async () => {
-          await a11y.testAppSnapshot();
+    describe('Kibana deprecations page', () => {
+      beforeEach(async () => {
+        await PageObjects.common.navigateToUrl(
+          'management',
+          'stack/upgrade_assistant/kibana_deprecations',
+          {
+            ensureCurrentUrl: false,
+            shouldLoginIfPrompted: false,
+            shouldUseHashForSubUrl: false,
+          }
+        );
+
+        await retry.waitFor('Kibana deprecations to be visible', async () => {
+          return testSubjects.exists('kibanaDeprecations');
         });
+      });
 
-        it('Deprecation details flyout', async () => {
-          await PageObjects.upgradeAssistant.clickKibanaDeprecation(
-            'xpack.securitySolution has a deprecated setting' // This deprecation was added to the test runner config so should be guaranteed
-          );
+      it('Deprecations table', async () => {
+        await a11y.testAppSnapshot();
+      });
 
-          await retry.waitFor('Kibana deprecation details flyout to be visible', async () => {
-            return testSubjects.exists('kibanaDeprecationDetails');
-          });
+      it('Deprecation details flyout', async () => {
+        await PageObjects.upgradeAssistant.clickKibanaDeprecation(
+          'xpack.securitySolution has a deprecated setting' // This deprecation was added to the test runner config so should be guaranteed
+        );
 
-          await a11y.testAppSnapshot();
+        await retry.waitFor('Kibana deprecation details flyout to be visible', async () => {
+          return testSubjects.exists('kibanaDeprecationDetails');
         });
+
+        await a11y.testAppSnapshot();
       });
     });
   });
diff --git a/x-pack/test/api_integration/apis/ml/system/capabilities.ts b/x-pack/test/api_integration/apis/ml/system/capabilities.ts
index 4eb040d031c2..d0df53dfee34 100644
--- a/x-pack/test/api_integration/apis/ml/system/capabilities.ts
+++ b/x-pack/test/api_integration/apis/ml/system/capabilities.ts
@@ -45,7 +45,7 @@ export default ({ getService }: FtrProviderContext) => {
 
       it('should have the right number of capabilities', async () => {
         const { capabilities } = await runRequest(USER.ML_POWERUSER);
-        expect(Object.keys(capabilities).length).to.eql(31);
+        expect(Object.keys(capabilities).length).to.eql(32);
       });
 
       it('should get viewer capabilities', async () => {
@@ -83,6 +83,7 @@ export default ({ getService }: FtrProviderContext) => {
           canGetAnnotations: true,
           canCreateAnnotation: true,
           canDeleteAnnotation: true,
+          canViewMlNodes: false,
         });
       });
 
@@ -121,6 +122,7 @@ export default ({ getService }: FtrProviderContext) => {
           canGetAnnotations: true,
           canCreateAnnotation: true,
           canDeleteAnnotation: true,
+          canViewMlNodes: true,
         });
       });
     });
diff --git a/x-pack/test/api_integration/apis/ml/system/space_capabilities.ts b/x-pack/test/api_integration/apis/ml/system/space_capabilities.ts
index 6d6a00e88268..b51b87457caa 100644
--- a/x-pack/test/api_integration/apis/ml/system/space_capabilities.ts
+++ b/x-pack/test/api_integration/apis/ml/system/space_capabilities.ts
@@ -71,11 +71,11 @@ export default ({ getService }: FtrProviderContext) => {
 
       it('should have the right number of capabilities - space with ML', async () => {
         const { capabilities } = await runRequest(USER.ML_POWERUSER, idSpaceWithMl);
-        expect(Object.keys(capabilities).length).to.eql(31);
+        expect(Object.keys(capabilities).length).to.eql(32);
       });
       it('should have the right number of capabilities - space without ML', async () => {
         const { capabilities } = await runRequest(USER.ML_POWERUSER, idSpaceNoMl);
-        expect(Object.keys(capabilities).length).to.eql(31);
+        expect(Object.keys(capabilities).length).to.eql(32);
       });
 
       it('should get viewer capabilities - space with ML', async () => {
@@ -112,6 +112,7 @@ export default ({ getService }: FtrProviderContext) => {
           canGetAnnotations: true,
           canCreateAnnotation: true,
           canDeleteAnnotation: true,
+          canViewMlNodes: false,
         });
       });
 
@@ -149,6 +150,7 @@ export default ({ getService }: FtrProviderContext) => {
           canGetAnnotations: false,
           canCreateAnnotation: false,
           canDeleteAnnotation: false,
+          canViewMlNodes: false,
         });
       });
 
@@ -186,6 +188,7 @@ export default ({ getService }: FtrProviderContext) => {
           canGetAnnotations: true,
           canCreateAnnotation: true,
           canDeleteAnnotation: true,
+          canViewMlNodes: true,
         });
       });
 
@@ -223,6 +226,7 @@ export default ({ getService }: FtrProviderContext) => {
           canGetAnnotations: false,
           canCreateAnnotation: false,
           canDeleteAnnotation: false,
+          canViewMlNodes: false,
         });
       });
     });
diff --git a/x-pack/test/fleet_api_integration/apis/epm/__snapshots__/install_by_upload.snap b/x-pack/test/fleet_api_integration/apis/epm/__snapshots__/install_by_upload.snap
index 04d189b5d59b..5eb4ae1808d7 100644
--- a/x-pack/test/fleet_api_integration/apis/epm/__snapshots__/install_by_upload.snap
+++ b/x-pack/test/fleet_api_integration/apis/epm/__snapshots__/install_by_upload.snap
@@ -446,7 +446,6 @@ Object {
           "type": "search",
         },
       ],
-      "keep_policies_up_to_date": false,
       "name": "apache",
       "package_assets": Array [
         Object {
diff --git a/x-pack/test/fleet_api_integration/apis/epm/install_remove_assets.ts b/x-pack/test/fleet_api_integration/apis/epm/install_remove_assets.ts
index f3f036107fc2..0915af7e25f0 100644
--- a/x-pack/test/fleet_api_integration/apis/epm/install_remove_assets.ts
+++ b/x-pack/test/fleet_api_integration/apis/epm/install_remove_assets.ts
@@ -673,7 +673,6 @@ const expectAssetsInstalled = ({
       install_status: 'installed',
       install_started_at: res.attributes.install_started_at,
       install_source: 'registry',
-      keep_policies_up_to_date: false,
     });
   });
 };
diff --git a/x-pack/test/fleet_api_integration/apis/epm/update_assets.ts b/x-pack/test/fleet_api_integration/apis/epm/update_assets.ts
index d55f6aad53aa..357345777e52 100644
--- a/x-pack/test/fleet_api_integration/apis/epm/update_assets.ts
+++ b/x-pack/test/fleet_api_integration/apis/epm/update_assets.ts
@@ -462,7 +462,6 @@ export default function (providerContext: FtrProviderContext) {
         install_status: 'installed',
         install_started_at: res.attributes.install_started_at,
         install_source: 'registry',
-        keep_policies_up_to_date: false,
       });
     });
   });
diff --git a/x-pack/test/functional/apps/ml/model_management/model_list.ts b/x-pack/test/functional/apps/ml/model_management/model_list.ts
index aac1ad5b1e50..955639dbe60a 100644
--- a/x-pack/test/functional/apps/ml/model_management/model_list.ts
+++ b/x-pack/test/functional/apps/ml/model_management/model_list.ts
@@ -10,8 +10,7 @@ import { FtrProviderContext } from '../../../ftr_provider_context';
 export default function ({ getService }: FtrProviderContext) {
   const ml = getService('ml');
 
-  // FAILING ES PROMOTION: https://github.com/elastic/kibana/issues/118251
-  describe.skip('trained models', function () {
+  describe('trained models', function () {
     before(async () => {
       await ml.trainedModels.createTestTrainedModels('classification', 15, true);
       await ml.trainedModels.createTestTrainedModels('regression', 15);
diff --git a/x-pack/test/functional/apps/monitoring/elasticsearch/nodes.js b/x-pack/test/functional/apps/monitoring/elasticsearch/nodes.js
index d7c4e5dd12f5..80c8c13b16dd 100644
--- a/x-pack/test/functional/apps/monitoring/elasticsearch/nodes.js
+++ b/x-pack/test/functional/apps/monitoring/elasticsearch/nodes.js
@@ -17,7 +17,8 @@ export default function ({ getService, getPageObjects }) {
     // FF issue: https://github.com/elastic/kibana/issues/35551
     this.tags(['skipFirefox']);
 
-    describe('with offline node', () => {
+    // FLAKY: https://github.com/elastic/kibana/issues/116533
+    describe.skip('with offline node', () => {
       const { setup, tearDown } = getLifecycleMethods(getService, getPageObjects);
 
       before(async () => {
diff --git a/x-pack/test/functional/apps/upgrade_assistant/es_deprecation_logs_page.ts b/x-pack/test/functional/apps/upgrade_assistant/es_deprecation_logs_page.ts
new file mode 100644
index 000000000000..c6b9b2921cfa
--- /dev/null
+++ b/x-pack/test/functional/apps/upgrade_assistant/es_deprecation_logs_page.ts
@@ -0,0 +1,45 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License
+ * 2.0; you may not use this file except in compliance with the Elastic License
+ * 2.0.
+ */
+
+import { FtrProviderContext } from '../../ftr_provider_context';
+
+export default function upgradeAssistantESDeprecationLogsPageFunctionalTests({
+  getService,
+  getPageObjects,
+}: FtrProviderContext) {
+  const PageObjects = getPageObjects(['upgradeAssistant', 'common']);
+  const security = getService('security');
+  const testSubjects = getService('testSubjects');
+  const es = getService('es');
+
+  describe.skip('ES deprecation logs page', function () {
+    this.tags('skipFirefox');
+
+    before(async () => {
+      await security.testUser.setRoles(['superuser']);
+      // Access to system indices will be deprecated and should generate a deprecation log
+      await es.indices.get({ index: '.kibana' });
+    });
+
+    after(async () => {
+      await security.testUser.restoreDefaults();
+    });
+
+    beforeEach(async () => {
+      await PageObjects.upgradeAssistant.navigateToEsDeprecationLogs();
+    });
+
+    it('Shows warnings callout if there are deprecations', async () => {
+      testSubjects.exists('hasWarningsCallout');
+    });
+
+    it('Shows no warnings callout if there are no deprecations', async () => {
+      await PageObjects.upgradeAssistant.clickResetLastCheckpointButton();
+      testSubjects.exists('noWarningsCallout');
+    });
+  });
+}
diff --git a/x-pack/test/functional/apps/upgrade_assistant/index.ts b/x-pack/test/functional/apps/upgrade_assistant/index.ts
index d99d1cd03332..d1ab46463e93 100644
--- a/x-pack/test/functional/apps/upgrade_assistant/index.ts
+++ b/x-pack/test/functional/apps/upgrade_assistant/index.ts
@@ -14,5 +14,6 @@ export default function upgradeCheckup({ loadTestFile }: FtrProviderContext) {
     loadTestFile(require.resolve('./feature_controls'));
     loadTestFile(require.resolve('./deprecation_pages'));
     loadTestFile(require.resolve('./overview_page'));
+    loadTestFile(require.resolve('./es_deprecation_logs_page'));
   });
 }
diff --git a/x-pack/test/functional/apps/upgrade_assistant/overview_page.ts b/x-pack/test/functional/apps/upgrade_assistant/overview_page.ts
index 0b8d15695689..a2ec7493cc3f 100644
--- a/x-pack/test/functional/apps/upgrade_assistant/overview_page.ts
+++ b/x-pack/test/functional/apps/upgrade_assistant/overview_page.ts
@@ -15,7 +15,6 @@ export default function upgradeAssistantOverviewPageFunctionalTests({
   const retry = getService('retry');
   const security = getService('security');
   const testSubjects = getService('testSubjects');
-  const es = getService('es');
 
   describe.skip('Overview Page', function () {
     this.tags('skipFirefox');
@@ -41,37 +40,7 @@ export default function upgradeAssistantOverviewPageFunctionalTests({
     it('Should render all steps', async () => {
       testSubjects.exists('backupStep-incomplete');
       testSubjects.exists('fixIssuesStep-incomplete');
-      testSubjects.exists('fixLogsStep-incomplete');
       testSubjects.exists('upgradeStep');
     });
-
-    describe('fixLogsStep', () => {
-      before(async () => {
-        await PageObjects.upgradeAssistant.navigateToPage();
-        // Access to system indices will be deprecated and should generate a deprecation log
-        await es.indices.get({ index: '.kibana' });
-        // Only click deprecation logging toggle if its not already enabled
-        if (!(await testSubjects.isDisplayed('externalLinksTitle'))) {
-          await PageObjects.upgradeAssistant.clickDeprecationLoggingToggle();
-        }
-
-        await retry.waitFor('UA external links title to be present', async () => {
-          return testSubjects.isDisplayed('externalLinksTitle');
-        });
-      });
-
-      beforeEach(async () => {
-        await PageObjects.upgradeAssistant.navigateToPage();
-      });
-
-      it('Shows warnings callout if there are deprecations', async () => {
-        testSubjects.exists('hasWarningsCallout');
-      });
-
-      it('Shows no warnings callout if there are no deprecations', async () => {
-        await PageObjects.upgradeAssistant.clickResetLastCheckpointButton();
-        testSubjects.exists('noWarningsCallout');
-      });
-    });
   });
 }
diff --git a/x-pack/test/functional/page_objects/upgrade_assistant_page.ts b/x-pack/test/functional/page_objects/upgrade_assistant_page.ts
index 54d7f3d45212..f795a5fd441c 100644
--- a/x-pack/test/functional/page_objects/upgrade_assistant_page.ts
+++ b/x-pack/test/functional/page_objects/upgrade_assistant_page.ts
@@ -29,6 +29,21 @@ export class UpgradeAssistantPageObject extends FtrService {
     });
   }
 
+  async navigateToEsDeprecationLogs() {
+    return await this.retry.try(async () => {
+      await this.common.navigateToApp('settings');
+      await this.testSubjects.click('upgrade_assistant');
+      await this.testSubjects.click('viewElasticsearchDeprecationLogs');
+      await this.retry.waitFor(
+        'url to contain /upgrade_assistant/es_deprecation_logs',
+        async () => {
+          const url = await this.browser.getCurrentUrl();
+          return url.includes('/es_deprecation_logs');
+        }
+      );
+    });
+  }
+
   async clickEsDeprecationsPanel() {
     return await this.retry.try(async () => {
       await this.testSubjects.click('esStatsPanel');
diff --git a/yarn.lock b/yarn.lock
index 9ae9d163a046..d1e1de94393a 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -12596,7 +12596,7 @@ elastic-apm-http-client@^10.3.0:
     readable-stream "^3.4.0"
     stream-chopper "^3.0.1"
 
-elastic-apm-node@3.24.0:
+elastic-apm-node@^3.24.0:
   version "3.24.0"
   resolved "https://registry.yarnpkg.com/elastic-apm-node/-/elastic-apm-node-3.24.0.tgz#d7acb3352f928a23c28ebabab2bd30098562814e"
   integrity sha512-Fmj/W2chWQa2zb1FfMYK2ypLB4TcnKNX+1klaJFbytRYDLgeSfo0EC7egvI3a+bLPZSRL5053PXOp7slVTPO6Q==