diff --git a/app/assets/javascripts/packages_and_registries/container_registry/explorer/index.js b/app/assets/javascripts/packages_and_registries/container_registry/explorer/index.js
index 7040f42398e487fbd1842c5c86d0c81058661ffd..0f92daf7db0b2f1c113240002071089f3712b8af 100644
--- a/app/assets/javascripts/packages_and_registries/container_registry/explorer/index.js
+++ b/app/assets/javascripts/packages_and_registries/container_registry/explorer/index.js
@@ -35,6 +35,7 @@ export default () => {
     expirationPolicy,
     isGroupPage,
     isAdmin,
+    isMetadataDatabaseEnabled,
     showCleanupPolicyLink,
     showContainerRegistrySettings,
     showUnfinishedTagCleanupCallout,
@@ -74,6 +75,7 @@ export default () => {
             showUnfinishedTagCleanupCallout: parseBoolean(showUnfinishedTagCleanupCallout),
             connectionError: parseBoolean(connectionError),
             invalidPathError: parseBoolean(invalidPathError),
+            isMetadataDatabaseEnabled: parseBoolean(isMetadataDatabaseEnabled),
           },
           /* eslint-disable @gitlab/require-i18n-strings */
           dockerBuildCommand: `docker build -t ${config.repositoryUrl} .`,
diff --git a/app/assets/javascripts/packages_and_registries/container_registry/explorer/pages/list.vue b/app/assets/javascripts/packages_and_registries/container_registry/explorer/pages/list.vue
index 89a8c4c2a2f86390947ef7b2b8e3aba41b4cf046..2ececeb606ed9fe0db4c479ec4ab2cd2f328c302 100644
--- a/app/assets/javascripts/packages_and_registries/container_registry/explorer/pages/list.vue
+++ b/app/assets/javascripts/packages_and_registries/container_registry/explorer/pages/list.vue
@@ -16,11 +16,11 @@ import { fetchPolicies } from '~/lib/graphql';
 import Tracking from '~/tracking';
 import PersistedPagination from '~/packages_and_registries/shared/components/persisted_pagination.vue';
 import PersistedSearch from '~/packages_and_registries/shared/components/persisted_search.vue';
+import MetadataDatabaseAlert from '~/packages_and_registries/shared/components/container_registry_metadata_database_alert.vue';
 import { FILTERED_SEARCH_TERM } from '~/vue_shared/components/filtered_search_bar/constants';
 import DeleteImage from '../components/delete_image.vue';
 import RegistryHeader from '../components/list_page/registry_header.vue';
 import DeleteModal from '../components/delete_modal.vue';
-
 import {
   DELETE_IMAGE_SUCCESS_MESSAGE,
   DELETE_IMAGE_ERROR_MESSAGE,
@@ -64,6 +64,7 @@ export default {
     GlSkeletonLoader,
     RegistryHeader,
     DeleteImage,
+    MetadataDatabaseAlert,
     PersistedPagination,
     PersistedSearch,
   },
@@ -233,6 +234,7 @@ export default {
 
 <template>
   <div>
+    <metadata-database-alert v-if="!config.isMetadataDatabaseEnabled" />
     <gl-alert
       v-if="showDeleteAlert"
       :variant="deleteAlertType"
diff --git a/app/assets/javascripts/packages_and_registries/settings/project/components/registry_settings_app.vue b/app/assets/javascripts/packages_and_registries/settings/project/components/registry_settings_app.vue
index ffc8d884cb84a26affeeeb39f47e5e414aa006b5..e6108b6cd346e30fe061eb13d3ff770804905788 100644
--- a/app/assets/javascripts/packages_and_registries/settings/project/components/registry_settings_app.vue
+++ b/app/assets/javascripts/packages_and_registries/settings/project/components/registry_settings_app.vue
@@ -9,6 +9,7 @@ import {
 import ContainerExpirationPolicy from '~/packages_and_registries/settings/project/components/container_expiration_policy.vue';
 import ContainerProtectionRules from '~/packages_and_registries/settings/project/components/container_protection_rules.vue';
 import PackagesCleanupPolicy from '~/packages_and_registries/settings/project/components/packages_cleanup_policy.vue';
+import MetadataDatabaseAlert from '~/packages_and_registries/shared/components/container_registry_metadata_database_alert.vue';
 import glFeatureFlagsMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
 
 export default {
@@ -20,6 +21,7 @@ export default {
         'ee_component/packages_and_registries/settings/project/components/dependency_proxy_packages_settings.vue'
       ),
     GlAlert,
+    MetadataDatabaseAlert,
     PackagesCleanupPolicy,
     PackagesProtectionRules: () =>
       import('~/packages_and_registries/settings/project/components/packages_protection_rules.vue'),
@@ -29,6 +31,7 @@ export default {
     'showContainerRegistrySettings',
     'showPackageRegistrySettings',
     'showDependencyProxySettings',
+    'isContainerRegistryMetadataDatabaseEnabled',
   ],
   i18n: {
     UPDATE_SETTINGS_SUCCESS_MESSAGE,
@@ -67,6 +70,7 @@ export default {
 
 <template>
   <div data-testid="packages-and-registries-project-settings">
+    <metadata-database-alert v-if="!isContainerRegistryMetadataDatabaseEnabled" class="gl-mt-5" />
     <gl-alert
       v-if="showAlert"
       variant="success"
diff --git a/app/assets/javascripts/packages_and_registries/settings/project/registry_settings_bundle.js b/app/assets/javascripts/packages_and_registries/settings/project/registry_settings_bundle.js
index 326265430d957913cc9bbb3f9acf67266704c91b..de5bd06e70cc58e44e5e6ed7374a9b466e2e3870 100644
--- a/app/assets/javascripts/packages_and_registries/settings/project/registry_settings_bundle.js
+++ b/app/assets/javascripts/packages_and_registries/settings/project/registry_settings_bundle.js
@@ -21,6 +21,7 @@ export default () => {
     cleanupSettingsPath,
     tagsRegexHelpPagePath,
     helpPagePath,
+    isContainerRegistryMetadataDatabaseEnabled,
     showContainerRegistrySettings,
     showPackageRegistrySettings,
     showDependencyProxySettings,
@@ -39,6 +40,9 @@ export default () => {
       cleanupSettingsPath,
       tagsRegexHelpPagePath,
       helpPagePath,
+      isContainerRegistryMetadataDatabaseEnabled: parseBoolean(
+        isContainerRegistryMetadataDatabaseEnabled,
+      ),
       showContainerRegistrySettings: parseBoolean(showContainerRegistrySettings),
       showPackageRegistrySettings: parseBoolean(showPackageRegistrySettings),
       showDependencyProxySettings: parseBoolean(showDependencyProxySettings),
diff --git a/app/assets/javascripts/packages_and_registries/shared/components/container_registry_metadata_database_alert.vue b/app/assets/javascripts/packages_and_registries/shared/components/container_registry_metadata_database_alert.vue
new file mode 100644
index 0000000000000000000000000000000000000000..68eea6e648f597b5041c53cd130814417b3bde7b
--- /dev/null
+++ b/app/assets/javascripts/packages_and_registries/shared/components/container_registry_metadata_database_alert.vue
@@ -0,0 +1,47 @@
+<script>
+import { GlAlert, GlLink, GlSprintf } from '@gitlab/ui';
+import { getCookie, parseBoolean, setCookie } from '~/lib/utils/common_utils';
+import { helpPagePath } from '~/helpers/help_page_helper';
+
+const HIDE_METADATA_DATABASE_ALERT_COOKIE = 'hide_metadata_database_alert';
+
+export default {
+  name: 'ContainerRegistryMetadataDatabaseAlert',
+  components: {
+    GlAlert,
+    GlLink,
+    GlSprintf,
+  },
+  data() {
+    return {
+      showAlert: !parseBoolean(getCookie(HIDE_METADATA_DATABASE_ALERT_COOKIE)),
+    };
+  },
+  metadataDatabaseHelpPagePath: helpPagePath(
+    'administration/packages/container_registry_metadata_database',
+  ),
+  methods: {
+    hideAlert() {
+      this.showAlert = false;
+      setCookie(HIDE_METADATA_DATABASE_ALERT_COOKIE, 'true');
+      this.$emit('dismiss');
+    },
+  },
+};
+</script>
+
+<template>
+  <gl-alert v-if="showAlert" class="gl-mt-5" @dismiss="hideAlert">
+    <gl-sprintf
+      :message="
+        s__(
+          'ContainerRegistry|The %{linkStart}next-generation container registry%{linkEnd} is now available for upgrade and testing on self-managed instances as a Beta feature. This upgraded registry supports online garbage collection, and has significant performance and reliability improvements.',
+        )
+      "
+    >
+      <template #link="{ content }">
+        <gl-link :href="$options.metadataDatabaseHelpPagePath"> {{ content }}</gl-link>
+      </template>
+    </gl-sprintf>
+  </gl-alert>
+</template>
diff --git a/app/helpers/packages_helper.rb b/app/helpers/packages_helper.rb
index 595bb69709f7a5ca3bd8fd0d86b3fda7d25bdd45..6fe6480e400e6c5b703b52fb44c81060278c2f24 100644
--- a/app/helpers/packages_helper.rb
+++ b/app/helpers/packages_helper.rb
@@ -80,27 +80,28 @@ def can_delete_group_packages?(group)
     Ability.allowed?(current_user, :destroy_package, group)
   end
 
-  def cleanup_settings_data
+  def cleanup_settings_data(project)
     {
-      project_id: @project.id,
-      project_path: @project.full_path,
+      project_id: project.id,
+      project_path: project.full_path,
       cadence_options: cadence_options.to_json,
       keep_n_options: keep_n_options.to_json,
       older_than_options: older_than_options.to_json,
       is_admin: current_user&.admin.to_s,
       admin_settings_path: ci_cd_admin_application_settings_path(anchor: 'js-registry-settings'),
-      project_settings_path: project_settings_packages_and_registries_path(@project),
+      project_settings_path: project_settings_packages_and_registries_path(project),
       enable_historic_entries: container_expiration_policies_historic_entry_enabled?.to_s,
       help_page_path: help_page_path('user/packages/container_registry/reduce_container_registry_storage', anchor: 'cleanup-policy'),
-      show_cleanup_policy_link: show_cleanup_policy_link(@project).to_s,
+      show_cleanup_policy_link: show_cleanup_policy_link(project).to_s,
       tags_regex_help_page_path: help_page_path('user/packages/container_registry/reduce_container_registry_storage', anchor: 'regex-pattern-examples')
     }
   end
 
   def settings_data(project)
-    cleanup_settings_data.merge(
+    cleanup_settings_data(project).merge(
       show_container_registry_settings: show_container_registry_settings(project).to_s,
       show_package_registry_settings: show_package_registry_settings(project).to_s,
+      is_container_registry_metadata_database_enabled: (show_container_registry_settings(project) && ContainerRegistry::GitlabApiClient.supports_gitlab_api?).to_s,
       cleanup_settings_path: cleanup_image_tags_project_settings_packages_and_registries_path(project)
     )
   end
diff --git a/app/views/groups/registry/repositories/index.html.haml b/app/views/groups/registry/repositories/index.html.haml
index c906beb631b97506332dc02c5bc2c38238cebada..3c9bbfce11fd947f0bf5d4030889f0223c96a2ba 100644
--- a/app/views/groups/registry/repositories/index.html.haml
+++ b/app/views/groups/registry/repositories/index.html.haml
@@ -19,4 +19,5 @@
     invalid_path_error: (!!@invalid_path_error).to_s,
     user_callouts_path: callouts_path,
     user_callout_id: Users::CalloutsHelper::UNFINISHED_TAG_CLEANUP_CALLOUT,
+    is_metadata_database_enabled: ContainerRegistry::GitlabApiClient.supports_gitlab_api?.to_s,
     show_unfinished_tag_cleanup_callout: show_unfinished_tag_cleanup_callout?.to_s } }
diff --git a/app/views/projects/registry/repositories/index.html.haml b/app/views/projects/registry/repositories/index.html.haml
index e1ec510d271a69b37ce26c64849362cc1e592923..7dc04486d13d638ebe8e2d9f441efbd4207f257a 100644
--- a/app/views/projects/registry/repositories/index.html.haml
+++ b/app/views/projects/registry/repositories/index.html.haml
@@ -24,4 +24,5 @@
     invalid_path_error: (!!@invalid_path_error).to_s,
     user_callouts_path: callouts_path,
     user_callout_id: Users::CalloutsHelper::UNFINISHED_TAG_CLEANUP_CALLOUT,
+    is_metadata_database_enabled: ContainerRegistry::GitlabApiClient.supports_gitlab_api?.to_s,
     show_unfinished_tag_cleanup_callout: show_unfinished_tag_cleanup_callout?.to_s, } }
diff --git a/app/views/projects/settings/packages_and_registries/cleanup_tags.html.haml b/app/views/projects/settings/packages_and_registries/cleanup_tags.html.haml
index ad9ba0b506cfe2e4d41bd71c19ade39ffc585454..2386d17d528db4daeed0d017d7ffd0399942f267 100644
--- a/app/views/projects/settings/packages_and_registries/cleanup_tags.html.haml
+++ b/app/views/projects/settings/packages_and_registries/cleanup_tags.html.haml
@@ -2,4 +2,4 @@
 - breadcrumb_title s_('ContainerRegistry|Cleanup policies')
 - page_title s_('ContainerRegistry|Cleanup policies'), _('Packages and registries settings')
 
-#js-registry-settings-cleanup-image-tags{ data: cleanup_settings_data }
+#js-registry-settings-cleanup-image-tags{ data: cleanup_settings_data(@project) }
diff --git a/ee/spec/features/security/project/internal_access_spec.rb b/ee/spec/features/security/project/internal_access_spec.rb
index 7ab67eb3ff606d5a18e8018a6a297f1bc4526939..ec6928392e9b77dd5421cdaeaa97e9694daa2108 100644
--- a/ee/spec/features/security/project/internal_access_spec.rb
+++ b/ee/spec/features/security/project/internal_access_spec.rb
@@ -231,6 +231,7 @@
       stub_container_registry_config(enabled: true)
       stub_container_registry_info
       project.container_repositories << container_repository
+      allow(ContainerRegistry::GitlabApiClient).to receive(:supports_gitlab_api?).and_return(true)
     end
 
     subject { project_container_registry_index_path(project) }
diff --git a/ee/spec/features/security/project/private_access_spec.rb b/ee/spec/features/security/project/private_access_spec.rb
index 342c890d44428ae9a83e83aab7de31e35db3cff7..87af60d916be31cb7fe481b8760d3c92455e6d97 100644
--- a/ee/spec/features/security/project/private_access_spec.rb
+++ b/ee/spec/features/security/project/private_access_spec.rb
@@ -187,6 +187,7 @@
       stub_container_registry_config(enabled: true)
       stub_container_registry_info
       project.container_repositories << container_repository
+      allow(ContainerRegistry::GitlabApiClient).to receive(:supports_gitlab_api?).and_return(true)
     end
 
     subject { project_container_registry_index_path(project) }
diff --git a/ee/spec/features/security/project/public_access_spec.rb b/ee/spec/features/security/project/public_access_spec.rb
index 5d8cb88ff7be2aec88c20aca9bc5cfc6fb1f6f00..c993c283817e6facd72c0f8553c445dded65073c 100644
--- a/ee/spec/features/security/project/public_access_spec.rb
+++ b/ee/spec/features/security/project/public_access_spec.rb
@@ -255,6 +255,7 @@
       stub_container_registry_config(enabled: true)
       stub_container_registry_info
       project.container_repositories << container_repository
+      allow(ContainerRegistry::GitlabApiClient).to receive(:supports_gitlab_api?).and_return(true)
     end
 
     subject { project_container_registry_index_path(project) }
diff --git a/ee/spec/helpers/ee/packages_helper_spec.rb b/ee/spec/helpers/ee/packages_helper_spec.rb
index 40f5ebfd35df1a3354abf947554ece69b6e28b3e..16829c14b3555e45685b3b96f0f685e5c15d3d0f 100644
--- a/ee/spec/helpers/ee/packages_helper_spec.rb
+++ b/ee/spec/helpers/ee/packages_helper_spec.rb
@@ -9,7 +9,6 @@
   describe '#settings_data' do
     before do
       allow(helper).to receive(:current_user).and_return(user)
-      instance_variable_set(:@project, project)
       allow(Ability).to receive(:allowed?).and_call_original
     end
 
@@ -49,7 +48,6 @@
 
     before do
       allow(helper).to receive(:current_user).and_return(user)
-      instance_variable_set(:@project, project)
     end
 
     it { is_expected.to include(full_path: project.full_path) }
diff --git a/locale/gitlab.pot b/locale/gitlab.pot
index 4dfb773b2c5a407fe3ab892cf05fbf0b59843893..da277161f23464234a87b1b81e9e020ef102c941 100644
--- a/locale/gitlab.pot
+++ b/locale/gitlab.pot
@@ -14053,6 +14053,9 @@ msgstr ""
 msgid "ContainerRegistry|Tags with names that match this regex pattern are removed. %{linkStart}View regex examples.%{linkEnd}"
 msgstr ""
 
+msgid "ContainerRegistry|The %{linkStart}next-generation container registry%{linkEnd} is now available for upgrade and testing on self-managed instances as a Beta feature. This upgraded registry supports online garbage collection, and has significant performance and reliability improvements."
+msgstr ""
+
 msgid "ContainerRegistry|The cleanup policy timed out before it could delete all tags. An administrator can %{adminLinkStart}run cleanup now manually%{adminLinkEnd} or you can wait for the next scheduled run of the cleanup policy. %{docLinkStart}More information%{docLinkEnd}"
 msgstr ""
 
diff --git a/spec/features/groups/container_registry_spec.rb b/spec/features/groups/container_registry_spec.rb
index 65edeb0798f75804289f85b7bc25e57e9967b7a4..c404d38252a549df39a9b24643cc62618d96b00c 100644
--- a/spec/features/groups/container_registry_spec.rb
+++ b/spec/features/groups/container_registry_spec.rb
@@ -11,12 +11,23 @@
     create(:container_repository, name: 'my/image')
   end
 
+  let(:help_page_href) { help_page_path('administration/packages/container_registry_metadata_database') }
+
   before do
     group.add_owner(user)
     sign_in(user)
     stub_container_registry_config(enabled: true)
     stub_container_registry_tags(repository: :any, tags: [])
     stub_container_registry_info
+    allow(ContainerRegistry::GitlabApiClient).to receive(:supports_gitlab_api?).and_return(true)
+  end
+
+  it 'has link to next generation container registry docs' do
+    allow(ContainerRegistry::GitlabApiClient).to receive(:supports_gitlab_api?).and_return(false)
+
+    visit_container_registry
+
+    expect(page).to have_link('next-generation container registry', href: help_page_href)
   end
 
   it 'has a page title set' do
diff --git a/spec/features/projects/container_registry_spec.rb b/spec/features/projects/container_registry_spec.rb
index c285d039d0867010fb0f2e1d8fa7d11ed2f60886..6ccbc3a26b2f0d8ebb9fc0d70863095091a555f2 100644
--- a/spec/features/projects/container_registry_spec.rb
+++ b/spec/features/projects/container_registry_spec.rb
@@ -16,12 +16,15 @@
     create(:container_repository, name: '')
   end
 
+  let(:help_page_href) { help_page_path('administration/packages/container_registry_metadata_database') }
+
   before do
     sign_in(user)
     project.add_developer(user)
     stub_container_registry_config(enabled: true)
     stub_container_registry_info
     stub_container_registry_tags(repository: :any, tags: [])
+    allow(ContainerRegistry::GitlabApiClient).to receive(:supports_gitlab_api?).and_return(true)
   end
 
   it 'has a page title set' do
@@ -30,6 +33,14 @@
     expect(page).to have_title _('Container Registry')
   end
 
+  it 'has link to next generation container registry docs' do
+    allow(ContainerRegistry::GitlabApiClient).to receive(:supports_gitlab_api?).and_return(false)
+
+    visit_container_registry
+
+    expect(page).to have_link('next-generation container registry', href: help_page_href)
+  end
+
   it 'does not have link to settings' do
     visit_container_registry
 
diff --git a/spec/features/projects/settings/registry_settings_cleanup_tags_spec.rb b/spec/features/projects/settings/registry_settings_cleanup_tags_spec.rb
index 11e3dd15cd80c3cb0b715930453c03a5fb16cf11..a907444c81bbceec3ffb09cf5809bc4af0ef54e3 100644
--- a/spec/features/projects/settings/registry_settings_cleanup_tags_spec.rb
+++ b/spec/features/projects/settings/registry_settings_cleanup_tags_spec.rb
@@ -18,6 +18,7 @@
 
     sign_in(user)
     stub_container_registry_config(enabled: container_registry_enabled)
+    allow(ContainerRegistry::GitlabApiClient).to receive(:supports_gitlab_api?).and_return(true)
   end
 
   context 'as owner', :js do
diff --git a/spec/features/projects/settings/registry_settings_spec.rb b/spec/features/projects/settings/registry_settings_spec.rb
index 084879a5abecd500711a9d3bdb6cca010ee7e14a..4b15cc87993a604e4cad8f56f78f00b293291288 100644
--- a/spec/features/projects/settings/registry_settings_spec.rb
+++ b/spec/features/projects/settings/registry_settings_spec.rb
@@ -10,6 +10,8 @@
   let(:container_registry_enabled) { true }
   let(:container_registry_enabled_on_project) { ProjectFeature::ENABLED }
 
+  let(:help_page_href) { help_page_path('administration/packages/container_registry_metadata_database') }
+
   subject { visit project_settings_packages_and_registries_path(project) }
 
   before do
@@ -18,6 +20,7 @@
 
     sign_in(user)
     stub_container_registry_config(enabled: container_registry_enabled)
+    allow(ContainerRegistry::GitlabApiClient).to receive(:supports_gitlab_api?).and_return(true)
   end
 
   context 'as owner', :js do
@@ -51,6 +54,14 @@
 
       expect(page).to have_link('Edit cleanup rules', href: cleanup_image_tags_project_settings_packages_and_registries_path(project))
     end
+
+    it 'has link to next generation container registry docs' do
+      allow(ContainerRegistry::GitlabApiClient).to receive(:supports_gitlab_api?).and_return(false)
+
+      subject
+
+      expect(page).to have_link('next-generation container registry', href: help_page_href)
+    end
   end
 
   context 'with a project without expiration policy', :js do
diff --git a/spec/features/security/project/internal_access_spec.rb b/spec/features/security/project/internal_access_spec.rb
index 8ad4bedfdf83cec4e3cd3c79c4ea23af1f1a6ea5..460080fa1a6e11602fed9b7d6f1b50e0445aff98 100644
--- a/spec/features/security/project/internal_access_spec.rb
+++ b/spec/features/security/project/internal_access_spec.rb
@@ -555,6 +555,7 @@
       stub_container_registry_config(enabled: true)
       stub_container_registry_info
       project.container_repositories << container_repository
+      allow(ContainerRegistry::GitlabApiClient).to receive(:supports_gitlab_api?).and_return(true)
     end
 
     subject { project_container_registry_index_path(project) }
diff --git a/spec/features/security/project/private_access_spec.rb b/spec/features/security/project/private_access_spec.rb
index d2d74ecf5c96ce84cbf7632ab39ac9a80d908379..50f03008ab16fabe04eccf92983f5625f5969a10 100644
--- a/spec/features/security/project/private_access_spec.rb
+++ b/spec/features/security/project/private_access_spec.rb
@@ -574,6 +574,7 @@
       stub_container_registry_config(enabled: true)
       stub_container_registry_info
       project.container_repositories << container_repository
+      allow(ContainerRegistry::GitlabApiClient).to receive(:supports_gitlab_api?).and_return(true)
     end
 
     subject { project_container_registry_index_path(project) }
diff --git a/spec/features/security/project/public_access_spec.rb b/spec/features/security/project/public_access_spec.rb
index 916f289b0b87fb37c9f519be5102296a1d1fb009..4b4c23c0bbb1700e0168587611c9888b566f9979 100644
--- a/spec/features/security/project/public_access_spec.rb
+++ b/spec/features/security/project/public_access_spec.rb
@@ -556,6 +556,7 @@
       stub_container_registry_config(enabled: true)
       stub_container_registry_info
       project.container_repositories << container_repository
+      allow(ContainerRegistry::GitlabApiClient).to receive(:supports_gitlab_api?).and_return(true)
     end
 
     subject { project_container_registry_index_path(project) }
diff --git a/spec/frontend/packages_and_registries/container_registry/explorer/pages/list_spec.js b/spec/frontend/packages_and_registries/container_registry/explorer/pages/list_spec.js
index 1f1f010e0c40c197dad6a05d650e009776a0665a..c217227c398851df82b24e4435c29f78127715da 100644
--- a/spec/frontend/packages_and_registries/container_registry/explorer/pages/list_spec.js
+++ b/spec/frontend/packages_and_registries/container_registry/explorer/pages/list_spec.js
@@ -25,6 +25,7 @@ import component from '~/packages_and_registries/container_registry/explorer/pag
 import Tracking from '~/tracking';
 import PersistedPagination from '~/packages_and_registries/shared/components/persisted_pagination.vue';
 import PersistedSearch from '~/packages_and_registries/shared/components/persisted_search.vue';
+import MetadataDatabaseAlert from '~/packages_and_registries/shared/components/container_registry_metadata_database_alert.vue';
 import { FILTERED_SEARCH_TERM } from '~/vue_shared/components/filtered_search_bar/constants';
 import TitleArea from '~/vue_shared/components/registry/title_area.vue';
 
@@ -57,6 +58,7 @@ describe('List Page', () => {
   const findRegistryHeader = () => wrapper.findComponent(RegistryHeader);
 
   const findDeleteAlert = () => wrapper.findComponent(GlAlert);
+  const findMetadataDatabaseAlert = () => wrapper.findComponent(MetadataDatabaseAlert);
   const findImageList = () => wrapper.findComponent(ImageList);
   const findPersistedSearch = () => wrapper.findComponent(PersistedSearch);
   const findEmptySearchMessage = () => wrapper.find('[data-testid="emptySearch"]');
@@ -139,6 +141,24 @@ describe('List Page', () => {
     });
   });
 
+  describe('metadata database alert', () => {
+    it('is rendered when metadata database is not enabled', () => {
+      mountComponent();
+
+      expect(findMetadataDatabaseAlert().exists()).toBe(true);
+    });
+
+    it('is not rendered when metadata database is enabled', () => {
+      mountComponent({
+        config: {
+          isMetadataDatabaseEnabled: true,
+        },
+      });
+
+      expect(findMetadataDatabaseAlert().exists()).toBe(false);
+    });
+  });
+
   describe('link to settings', () => {
     beforeEach(() => {
       const config = {
diff --git a/spec/frontend/packages_and_registries/settings/project/settings/components/registry_settings_app_spec.js b/spec/frontend/packages_and_registries/settings/project/settings/components/registry_settings_app_spec.js
index cc75e6151ad5668036c449d6a4ab4b29e4d91f3b..0c6097237eefbf590c541082bf9cc9818fe55511 100644
--- a/spec/frontend/packages_and_registries/settings/project/settings/components/registry_settings_app_spec.js
+++ b/spec/frontend/packages_and_registries/settings/project/settings/components/registry_settings_app_spec.js
@@ -9,6 +9,7 @@ import ContainerProtectionRules from '~/packages_and_registries/settings/project
 import PackagesCleanupPolicy from '~/packages_and_registries/settings/project/components/packages_cleanup_policy.vue';
 import PackagesProtectionRules from '~/packages_and_registries/settings/project/components/packages_protection_rules.vue';
 import DependencyProxyPackagesSettings from 'ee_component/packages_and_registries/settings/project/components/dependency_proxy_packages_settings.vue';
+import MetadataDatabaseAlert from '~/packages_and_registries/shared/components/container_registry_metadata_database_alert.vue';
 import {
   SHOW_SETUP_SUCCESS_ALERT,
   UPDATE_SETTINGS_SUCCESS_MESSAGE,
@@ -26,6 +27,7 @@ describe('Registry Settings app', () => {
   const findDependencyProxyPackagesSettings = () =>
     wrapper.findComponent(DependencyProxyPackagesSettings);
   const findAlert = () => wrapper.findComponent(GlAlert);
+  const findMetadataDatabaseAlert = () => wrapper.findComponent(MetadataDatabaseAlert);
 
   const defaultProvide = {
     projectPath: 'path',
@@ -37,6 +39,7 @@ describe('Registry Settings app', () => {
       containerRegistryProtectedContainers: true,
       packagesProtectedPackages: true,
     },
+    isContainerRegistryMetadataDatabaseEnabled: false,
   };
 
   const mountComponent = (provide = defaultProvide) => {
@@ -45,6 +48,23 @@ describe('Registry Settings app', () => {
     });
   };
 
+  describe('metadata database alert', () => {
+    it('is rendered when metadata database is not enabled', () => {
+      mountComponent();
+
+      expect(findMetadataDatabaseAlert().exists()).toBe(true);
+    });
+
+    it('is not rendered when metadata database is enabled', () => {
+      mountComponent({
+        ...defaultProvide,
+        isContainerRegistryMetadataDatabaseEnabled: true,
+      });
+
+      expect(findMetadataDatabaseAlert().exists()).toBe(false);
+    });
+  });
+
   describe('container policy success alert handling', () => {
     const originalLocation = window.location.href;
     const search = `?${SHOW_SETUP_SUCCESS_ALERT}=true`;
diff --git a/spec/frontend/packages_and_registries/shared/components/container_registry_metadata_database_alert_spec.js b/spec/frontend/packages_and_registries/shared/components/container_registry_metadata_database_alert_spec.js
new file mode 100644
index 0000000000000000000000000000000000000000..511a34e323fd928ed424ce36cac6f0df5affe24e
--- /dev/null
+++ b/spec/frontend/packages_and_registries/shared/components/container_registry_metadata_database_alert_spec.js
@@ -0,0 +1,56 @@
+import { GlAlert } from '@gitlab/ui';
+import { shallowMount } from '@vue/test-utils';
+import MetadataDatabaseAlert from '~/packages_and_registries/shared/components/container_registry_metadata_database_alert.vue';
+import * as utils from '~/lib/utils/common_utils';
+
+describe('container registry metadata database alert', () => {
+  let wrapper;
+
+  const mountComponent = () => {
+    wrapper = shallowMount(MetadataDatabaseAlert);
+  };
+
+  const findAlert = () => wrapper.findComponent(GlAlert);
+
+  describe('with no cookie set', () => {
+    beforeEach(() => {
+      utils.setCookie = jest.fn();
+
+      mountComponent();
+    });
+
+    it('displays the alert', () => {
+      expect(findAlert().exists()).toBe(true);
+    });
+
+    it('does not call setCookie', () => {
+      expect(utils.setCookie).not.toHaveBeenCalled();
+    });
+
+    describe('when the close button is clicked', () => {
+      beforeEach(() => {
+        findAlert().vm.$emit('dismiss');
+      });
+
+      it('sets the dismissed cookie', () => {
+        expect(utils.setCookie).toHaveBeenCalledWith('hide_metadata_database_alert', 'true');
+      });
+
+      it('does not display the alert', () => {
+        expect(findAlert().exists()).toBe(false);
+      });
+    });
+  });
+
+  describe('with the dismissed cookie set', () => {
+    beforeEach(() => {
+      jest.spyOn(utils, 'getCookie').mockReturnValue('true');
+
+      mountComponent();
+    });
+
+    it('does not display the alert', () => {
+      expect(findAlert().exists()).toBe(false);
+    });
+  });
+});
diff --git a/spec/helpers/packages_helper_spec.rb b/spec/helpers/packages_helper_spec.rb
index 6d6b8e4c70765b98e4e69d192d6c9f66a374c5b1..95d5b39ad3b1d3b5fff416fecac8e93e855f4d80 100644
--- a/spec/helpers/packages_helper_spec.rb
+++ b/spec/helpers/packages_helper_spec.rb
@@ -252,6 +252,36 @@
     end
   end
 
+  describe '#settings_data' do
+    let(:user) { build_stubbed(:user) }
+
+    subject { helper.settings_data(project) }
+
+    where(:config_registry, :permission, :supports_gitlab_api?, :expected_result) do
+      false | false | false | false
+      false | false | true  | false
+      false | true  | false | false
+      false | true  | true  | false
+      true  | false | false | false
+      true  | false | true  | false
+      true  | true  | false | false
+      true  | true  | true  | true
+    end
+
+    with_them do
+      before do
+        allow(helper).to receive(:current_user).and_return(user)
+        stub_config(registry: { enabled: config_registry })
+        allow(ContainerRegistry::GitlabApiClient).to receive(:supports_gitlab_api?).and_return(supports_gitlab_api?)
+        allow(Ability).to receive(:allowed?).and_call_original
+        allow(Ability).to receive(:allowed?).with(user, :admin_container_image, project).and_return(permission)
+        allow(Ability).to receive(:allowed?).with(user, :admin_package, project).and_return(true)
+      end
+
+      it { is_expected.to include(is_container_registry_metadata_database_enabled: expected_result.to_s) }
+    end
+  end
+
   describe '#can_delete_packages?' do
     let_it_be(:project) { create(:project) }
     let_it_be(:user) { create(:user) }
diff --git a/spec/requests/projects/settings/packages_and_registries_controller_spec.rb b/spec/requests/projects/settings/packages_and_registries_controller_spec.rb
index aa8d41cf5f81705137573c014124db471d06589c..03a4804115821844f18d9b6d8552cc0c548a2b91 100644
--- a/spec/requests/projects/settings/packages_and_registries_controller_spec.rb
+++ b/spec/requests/projects/settings/packages_and_registries_controller_spec.rb
@@ -24,6 +24,7 @@
 
       before do
         sign_in(user)
+        allow(ContainerRegistry::GitlabApiClient).to receive(:supports_gitlab_api?).and_return(true)
       end
 
       it 'pushes the feature flag "packages_protected_packages" to the view' do