diff --git a/app/controllers/groups/harbor/repositories_controller.rb b/app/controllers/groups/harbor/repositories_controller.rb
new file mode 100644
index 0000000000000000000000000000000000000000..364607f9b208541f1d9b38cb1ccdef884d6abd89
--- /dev/null
+++ b/app/controllers/groups/harbor/repositories_controller.rb
@@ -0,0 +1,24 @@
+# frozen_string_literal: true
+
+module Groups
+  module Harbor
+    class RepositoriesController < Groups::ApplicationController
+      feature_category :integrations
+
+      before_action :harbor_registry_enabled!
+      before_action do
+        push_frontend_feature_flag(:harbor_registry_integration)
+      end
+
+      def show
+        render :index
+      end
+
+      private
+
+      def harbor_registry_enabled!
+        render_404 unless Feature.enabled?(:harbor_registry_integration)
+      end
+    end
+  end
+end
diff --git a/app/controllers/projects/harbor/application_controller.rb b/app/controllers/projects/harbor/application_controller.rb
new file mode 100644
index 0000000000000000000000000000000000000000..e6e694783fa0d61805ff8d2b35eaf7856b0ff572
--- /dev/null
+++ b/app/controllers/projects/harbor/application_controller.rb
@@ -0,0 +1,22 @@
+# frozen_string_literal: true
+
+module Projects
+  module Harbor
+    class ApplicationController < Projects::ApplicationController
+      layout 'project'
+
+      before_action :harbor_registry_enabled!
+      before_action do
+        push_frontend_feature_flag(:harbor_registry_integration)
+      end
+
+      feature_category :integrations
+
+      private
+
+      def harbor_registry_enabled!
+        render_404 unless Feature.enabled?(:harbor_registry_integration)
+      end
+    end
+  end
+end
diff --git a/app/controllers/projects/harbor/repositories_controller.rb b/app/controllers/projects/harbor/repositories_controller.rb
new file mode 100644
index 0000000000000000000000000000000000000000..dd3e3dc19781e416b189e582c3d97d42a1044efc
--- /dev/null
+++ b/app/controllers/projects/harbor/repositories_controller.rb
@@ -0,0 +1,11 @@
+# frozen_string_literal: true
+
+module Projects
+  module Harbor
+    class RepositoriesController < ::Projects::Harbor::ApplicationController
+      def show
+        render :index
+      end
+    end
+  end
+end
diff --git a/app/views/groups/harbor/repositories/index.html.haml b/app/views/groups/harbor/repositories/index.html.haml
new file mode 100644
index 0000000000000000000000000000000000000000..1ee15557e21a8730d86c1834131251681632f15c
--- /dev/null
+++ b/app/views/groups/harbor/repositories/index.html.haml
@@ -0,0 +1,9 @@
+- page_title _("Harbor Registry")
+- @content_class = "limit-container-width" unless fluid_layout
+
+#js-harbor-registry-list-group{ data: { endpoint: group_harbor_registries_path(@group),
+  "no_containers_image" => image_path('illustrations/docker-empty-state.svg'),
+  "containers_error_image" => image_path('illustrations/docker-error-state.svg'),
+  "help_page_path" => help_page_path('user/packages/container_registry/index'),
+  connection_error: (!!@connection_error).to_s,
+  invalid_path_error: (!!@invalid_path_error).to_s, } }
diff --git a/app/views/projects/harbor/repositories/index.html.haml b/app/views/projects/harbor/repositories/index.html.haml
new file mode 100644
index 0000000000000000000000000000000000000000..b3f5b91596d461386fbb412feed7ff8dac87e783
--- /dev/null
+++ b/app/views/projects/harbor/repositories/index.html.haml
@@ -0,0 +1,9 @@
+- page_title _("Harbor Registry")
+- @content_class = "limit-container-width" unless fluid_layout
+
+#js-harbor-registry-list-project{ data: { endpoint: project_harbor_registry_index_path(@project),
+  "no_containers_image" => image_path('illustrations/docker-empty-state.svg'),
+  "containers_error_image" => image_path('illustrations/docker-error-state.svg'),
+  "help_page_path" => help_page_path('user/packages/container_registry/index'),
+  connection_error: (!!@connection_error).to_s,
+  invalid_path_error: (!!@invalid_path_error).to_s, } }
diff --git a/config/feature_flags/development/harbor_registry_integration.yml b/config/feature_flags/development/harbor_registry_integration.yml
new file mode 100644
index 0000000000000000000000000000000000000000..84d9709ca30bf7f4c08d49a1b2911972cffd843f
--- /dev/null
+++ b/config/feature_flags/development/harbor_registry_integration.yml
@@ -0,0 +1,8 @@
+---
+name: harbor_registry_integration
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/81593
+rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/353595
+milestone: '14.9'
+type: development
+group: group::package
+default_enabled: false
diff --git a/config/routes/group.rb b/config/routes/group.rb
index 41a165ab6e6c949bd7939ba99d438ebe439edf59..fecd3135cba85f666a35014ce9d9dbbabceb6ceb 100644
--- a/config/routes/group.rb
+++ b/config/routes/group.rb
@@ -118,6 +118,7 @@
     end
 
     resources :container_registries, only: [:index, :show], controller: 'registry/repositories'
+    resources :harbor_registries, only: [:index, :show], controller: 'harbor/repositories'
     resource :dependency_proxy, only: [:show, :update]
     resources :email_campaigns, only: :index
 
diff --git a/config/routes/project.rb b/config/routes/project.rb
index ccead5376fe3432cd1d75e793e22a49d4eb3efa6..eae30d3408afd4c8a935c6165b20b828ce0aeff8 100644
--- a/config/routes/project.rb
+++ b/config/routes/project.rb
@@ -545,6 +545,9 @@
       resources :container_registry, only: [:index, :destroy, :show], # rubocop: disable Cop/PutProjectRoutesUnderScope
                                      controller: 'registry/repositories'
 
+      resources :harbor_registry, only: [:index, :show], # rubocop: disable Cop/PutProjectRoutesUnderScope
+                                  controller: 'harbor/repositories'
+
       namespace :registry do
         resources :repository, only: [] do # rubocop: disable Cop/PutProjectRoutesUnderScope
           # We default to JSON format in the controller to avoid ambiguity.
diff --git a/ee/spec/features/groups/navbar_spec.rb b/ee/spec/features/groups/navbar_spec.rb
index 4fb610ed1afa4f7bea01fe1b51040f36266b2513..79f4edd5562ef2506922ea9010e4ed5fdb7ad4b4 100644
--- a/ee/spec/features/groups/navbar_spec.rb
+++ b/ee/spec/features/groups/navbar_spec.rb
@@ -16,6 +16,7 @@
     group.add_maintainer(user)
     stub_feature_flags(customer_relations: false)
     stub_group_wikis(false)
+    stub_feature_flags(harbor_registry_integration: false)
     sign_in(user)
 
     insert_package_nav(_('Kubernetes'))
@@ -231,4 +232,16 @@
 
     it_behaves_like 'verified navigation bar'
   end
+
+  context 'when harbor registry is available' do
+    before do
+      stub_feature_flags(harbor_registry_integration: true)
+
+      insert_harbor_registry_nav(_('Package Registry'))
+
+      visit group_path(group)
+    end
+
+    it_behaves_like 'verified navigation bar'
+  end
 end
diff --git a/ee/spec/features/projects/navbar_spec.rb b/ee/spec/features/projects/navbar_spec.rb
index 00383b8b4534852d8032e8b7ae7696c3e1342e62..3105e4d13bcb4be030faa09248e2f6c9412a4297 100644
--- a/ee/spec/features/projects/navbar_spec.rb
+++ b/ee/spec/features/projects/navbar_spec.rb
@@ -14,6 +14,7 @@
   before do
     sign_in(user)
 
+    stub_feature_flags(harbor_registry_integration: false)
     insert_package_nav(_('Infrastructure'))
     insert_infrastructure_registry_nav
     insert_infrastructure_google_cloud_nav
@@ -81,4 +82,16 @@
       it_behaves_like 'verified navigation bar'
     end
   end
+
+  context 'when harbor registry is available' do
+    before do
+      stub_feature_flags(harbor_registry_integration: true)
+
+      insert_harbor_registry_nav(_('Infrastructure Registry'))
+
+      visit project_path(project)
+    end
+
+    it_behaves_like 'verified navigation bar'
+  end
 end
diff --git a/lib/sidebars/groups/menus/packages_registries_menu.rb b/lib/sidebars/groups/menus/packages_registries_menu.rb
index 60d91c8fd104b9d3edeafba927dbf1d23ad28b0d..4c21845ef186b56d056bd4d4c95d2ac07c55f6c0 100644
--- a/lib/sidebars/groups/menus/packages_registries_menu.rb
+++ b/lib/sidebars/groups/menus/packages_registries_menu.rb
@@ -8,8 +8,8 @@ class PackagesRegistriesMenu < ::Sidebars::Menu
         def configure_menu_items
           add_item(packages_registry_menu_item)
           add_item(container_registry_menu_item)
+          add_item(harbor_registry__menu_item)
           add_item(dependency_proxy_menu_item)
-
           true
         end
 
@@ -49,6 +49,17 @@ def container_registry_menu_item
           )
         end
 
+        def harbor_registry__menu_item
+          return nil_menu_item(:harbor_registry) if Feature.disabled?(:harbor_registry_integration)
+
+          ::Sidebars::MenuItem.new(
+            title: _('Harbor Registry'),
+            link: group_harbor_registries_path(context.group),
+            active_routes: { controller: 'groups/harbor/repositories' },
+            item_id: :harbor_registry
+          )
+        end
+
         def dependency_proxy_menu_item
           setting_does_not_exist_or_is_enabled = !context.group.dependency_proxy_setting ||
                                                   context.group.dependency_proxy_setting.enabled
diff --git a/lib/sidebars/projects/menus/packages_registries_menu.rb b/lib/sidebars/projects/menus/packages_registries_menu.rb
index f5f0da2992eaec75e77a40e0cccf527cedd07ab2..77f09986b197967289644a9df72901956751403e 100644
--- a/lib/sidebars/projects/menus/packages_registries_menu.rb
+++ b/lib/sidebars/projects/menus/packages_registries_menu.rb
@@ -9,7 +9,7 @@ def configure_menu_items
           add_item(packages_registry_menu_item)
           add_item(container_registry_menu_item)
           add_item(infrastructure_registry_menu_item)
-
+          add_item(harbor_registry__menu_item)
           true
         end
 
@@ -65,6 +65,17 @@ def infrastructure_registry_menu_item
           )
         end
 
+        def harbor_registry__menu_item
+          return ::Sidebars::NilMenuItem.new(item_id: :harbor_registry) if Feature.disabled?(:harbor_registry_integration)
+
+          ::Sidebars::MenuItem.new(
+            title: _('Harbor Registry'),
+            link: project_harbor_registry_index_path(context.project),
+            active_routes: { controller: :harbor_registry },
+            item_id: :harbor_registry
+          )
+        end
+
         def packages_registry_disabled?
           !::Gitlab.config.packages.enabled || !can?(context.current_user, :read_package, context.project)
         end
diff --git a/locale/gitlab.pot b/locale/gitlab.pot
index 257a69adfd6a70f9e43f7cbb917dcac193c87591..ac015d382a75b53e1725b8fa296fda01be5b3e77 100644
--- a/locale/gitlab.pot
+++ b/locale/gitlab.pot
@@ -17932,6 +17932,9 @@ msgstr ""
 msgid "HTTP Basic: Access denied\\nYou must use a personal access token with 'api' scope for Git over HTTP.\\nYou can generate one at %{profile_personal_access_tokens_url}"
 msgstr ""
 
+msgid "Harbor Registry"
+msgstr ""
+
 msgid "Hashed Storage must be enabled to use Geo"
 msgstr ""
 
diff --git a/spec/features/groups/navbar_spec.rb b/spec/features/groups/navbar_spec.rb
index c5d2f5e6733023bc0bc3be8e92cd9bf7cb3f38ff..42b53d941c2d1d41e58db2693190812f90fe65dc 100644
--- a/spec/features/groups/navbar_spec.rb
+++ b/spec/features/groups/navbar_spec.rb
@@ -18,6 +18,7 @@
     stub_feature_flags(customer_relations: false)
     stub_config(dependency_proxy: { enabled: false })
     stub_config(registry: { enabled: false })
+    stub_feature_flags(harbor_registry_integration: false)
     stub_group_wikis(false)
     group.add_maintainer(user)
     sign_in(user)
@@ -70,4 +71,16 @@
 
     it_behaves_like 'verified navigation bar'
   end
+
+  context 'when harbor registry is available' do
+    before do
+      stub_feature_flags(harbor_registry_integration: true)
+
+      insert_harbor_registry_nav(_('Package Registry'))
+
+      visit group_path(group)
+    end
+
+    it_behaves_like 'verified navigation bar'
+  end
 end
diff --git a/spec/features/projects/navbar_spec.rb b/spec/features/projects/navbar_spec.rb
index 91e643ff258b33372b3680c43c04584c524ab064..5098908857a3cd7f8a016b7dd88b45062fb3b3f3 100644
--- a/spec/features/projects/navbar_spec.rb
+++ b/spec/features/projects/navbar_spec.rb
@@ -16,6 +16,7 @@
     sign_in(user)
 
     stub_config(registry: { enabled: false })
+    stub_feature_flags(harbor_registry_integration: false)
     insert_package_nav(_('Infrastructure'))
     insert_infrastructure_registry_nav
     insert_infrastructure_google_cloud_nav
@@ -76,4 +77,16 @@
 
     it_behaves_like 'verified navigation bar'
   end
+
+  context 'when harbor registry is available' do
+    before do
+      stub_feature_flags(harbor_registry_integration: true)
+
+      insert_harbor_registry_nav(_('Infrastructure Registry'))
+
+      visit project_path(project)
+    end
+
+    it_behaves_like 'verified navigation bar'
+  end
 end
diff --git a/spec/lib/sidebars/groups/menus/packages_registries_menu_spec.rb b/spec/lib/sidebars/groups/menus/packages_registries_menu_spec.rb
index bc1fa3e88ff72e944cbfb6b078c391058bd9f87a..d3cb18222b59108061d42db4e21151179ff19b7b 100644
--- a/spec/lib/sidebars/groups/menus/packages_registries_menu_spec.rb
+++ b/spec/lib/sidebars/groups/menus/packages_registries_menu_spec.rb
@@ -23,6 +23,7 @@
 
     context 'when menu does not have any menu item to show' do
       it 'returns false' do
+        stub_feature_flags(harbor_registry_integration: false)
         stub_container_registry_config(enabled: false)
         stub_config(packages: { enabled: false })
         stub_config(dependency_proxy: { enabled: false })
@@ -35,11 +36,13 @@
   describe '#link' do
     let(:registry_enabled) { true }
     let(:packages_enabled) { true }
+    let(:harbor_registry_integration) { true }
 
     before do
       stub_container_registry_config(enabled: registry_enabled)
       stub_config(packages: { enabled: packages_enabled })
       stub_config(dependency_proxy: { enabled: true })
+      stub_feature_flags(harbor_registry_integration: harbor_registry_integration)
     end
 
     subject { menu.link }
@@ -60,8 +63,16 @@
       context 'when Container Registry is not visible' do
         let(:registry_enabled) { false }
 
-        it 'menu link points to Dependency Proxy page' do
-          expect(subject).to eq find_menu(menu, :dependency_proxy).link
+        it 'menu link points to Harbor Registry page' do
+          expect(subject).to eq find_menu(menu, :harbor_registry).link
+        end
+
+        context 'when Harbor Registry is not visible' do
+          let(:harbor_registry_integration) { false }
+
+          it 'menu link points to Dependency Proxy page' do
+            expect(subject).to eq find_menu(menu, :dependency_proxy).link
+          end
         end
       end
     end
@@ -175,6 +186,26 @@
         it_behaves_like 'the menu entry is not available'
       end
     end
+
+    describe 'Harbor Registry' do
+      let(:item_id) { :harbor_registry }
+
+      before do
+        stub_feature_flags(harbor_registry_integration: harbor_registry_enabled)
+      end
+
+      context 'when config harbor registry setting is disabled' do
+        let(:harbor_registry_enabled) { false }
+
+        it_behaves_like 'the menu entry is not available'
+      end
+
+      context 'when config harbor registry setting is enabled' do
+        let(:harbor_registry_enabled) { true }
+
+        it_behaves_like 'the menu entry is available'
+      end
+    end
   end
 
   private
diff --git a/spec/lib/sidebars/projects/menus/packages_registries_menu_spec.rb b/spec/lib/sidebars/projects/menus/packages_registries_menu_spec.rb
index afe0b2a89511e5750eb0ff92e09eb9d099df9deb..9b78fc807bf8b348df539a6a45e46f4a260d43d2 100644
--- a/spec/lib/sidebars/projects/menus/packages_registries_menu_spec.rb
+++ b/spec/lib/sidebars/projects/menus/packages_registries_menu_spec.rb
@@ -33,6 +33,7 @@
     before do
       stub_container_registry_config(enabled: registry_enabled)
       stub_config(packages: { enabled: packages_enabled })
+      stub_feature_flags(harbor_registry_integration: false)
     end
 
     context 'when Packages Registry is visible' do
@@ -144,5 +145,25 @@
         end
       end
     end
+
+    describe 'Harbor Registry' do
+      let(:item_id) { :harbor_registry }
+
+      context 'when config harbor registry setting is disabled' do
+        it 'does not add the menu item to the list' do
+          stub_feature_flags(harbor_registry_integration: false)
+
+          is_expected.to be_nil
+        end
+      end
+
+      context 'when config harbor registry setting is enabled' do
+        it 'the menu item is added to list of menu items' do
+          stub_feature_flags(harbor_registry_integration: true)
+
+          is_expected.not_to be_nil
+        end
+      end
+    end
   end
 end
diff --git a/spec/requests/groups/harbor/repositories_controller_spec.rb b/spec/requests/groups/harbor/repositories_controller_spec.rb
new file mode 100644
index 0000000000000000000000000000000000000000..3e475dc410e3b65ba0c15a2ece8615694b4cc9d0
--- /dev/null
+++ b/spec/requests/groups/harbor/repositories_controller_spec.rb
@@ -0,0 +1,69 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Groups::Harbor::RepositoriesController do
+  let_it_be(:group, reload: true) { create(:group) }
+  let_it_be(:user) { create(:user) }
+
+  shared_examples 'responds with 404 status' do
+    it 'returns 404' do
+      subject
+
+      expect(response).to have_gitlab_http_status(:not_found)
+    end
+  end
+
+  shared_examples 'responds with 200 status' do
+    it 'renders the index template' do
+      subject
+
+      expect(response).to have_gitlab_http_status(:ok)
+      expect(response).to render_template(:index)
+    end
+  end
+
+  before do
+    stub_feature_flags(harbor_registry_integration: true)
+    group.add_reporter(user)
+    login_as(user)
+  end
+
+  describe 'GET #index' do
+    subject do
+      get group_harbor_registries_path(group)
+      response
+    end
+
+    context 'with harbor registry feature flag enabled' do
+      it_behaves_like 'responds with 200 status'
+    end
+
+    context 'with harbor registry feature flag disabled' do
+      before do
+        stub_feature_flags(harbor_registry_integration: false)
+      end
+
+      it_behaves_like 'responds with 404 status'
+    end
+  end
+
+  describe 'GET #show' do
+    subject do
+      get group_harbor_registry_path(group, 1)
+      response
+    end
+
+    context 'with harbor registry feature flag enabled' do
+      it_behaves_like 'responds with 200 status'
+    end
+
+    context 'with harbor registry feature flag disabled' do
+      before do
+        stub_feature_flags(harbor_registry_integration: false)
+      end
+
+      it_behaves_like 'responds with 404 status'
+    end
+  end
+end
diff --git a/spec/requests/projects/harbor/repositories_controller_spec.rb b/spec/requests/projects/harbor/repositories_controller_spec.rb
new file mode 100644
index 0000000000000000000000000000000000000000..cdb5a696d7ea8fab982defeb2a0ae1e028322933
--- /dev/null
+++ b/spec/requests/projects/harbor/repositories_controller_spec.rb
@@ -0,0 +1,69 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Projects::Harbor::RepositoriesController do
+  let_it_be(:project, reload: true) { create(:project) }
+  let_it_be(:user) { create(:user) }
+
+  shared_examples 'responds with 404 status' do
+    it 'returns 404' do
+      subject
+
+      expect(response).to have_gitlab_http_status(:not_found)
+    end
+  end
+
+  shared_examples 'responds with 200 status' do
+    it 'renders the index template' do
+      subject
+
+      expect(response).to have_gitlab_http_status(:ok)
+      expect(response).to render_template(:index)
+    end
+  end
+
+  before do
+    stub_feature_flags(harbor_registry_integration: true)
+    project.add_developer(user)
+    sign_in(user)
+  end
+
+  describe 'GET #index' do
+    subject do
+      get project_harbor_registry_index_path(project)
+      response
+    end
+
+    context 'with harbor registry feature flag enabled' do
+      it_behaves_like 'responds with 200 status'
+    end
+
+    context 'with harbor registry feature flag disabled' do
+      before do
+        stub_feature_flags(harbor_registry_integration: false)
+      end
+
+      it_behaves_like 'responds with 404 status'
+    end
+  end
+
+  describe 'GET #show' do
+    subject do
+      get project_harbor_registry_path(project, 1)
+      response
+    end
+
+    context 'with harbor registry feature flag enabled' do
+      it_behaves_like 'responds with 200 status'
+    end
+
+    context 'with harbor registry feature flag disabled' do
+      before do
+        stub_feature_flags(harbor_registry_integration: false)
+      end
+
+      it_behaves_like 'responds with 404 status'
+    end
+  end
+end
diff --git a/spec/support/helpers/navbar_structure_helper.rb b/spec/support/helpers/navbar_structure_helper.rb
index 6fa69cbd6ad02df0305914d8c5d142ba270021ee..fb06ebfdae27359109678464a841bf0c6f5fc445 100644
--- a/spec/support/helpers/navbar_structure_helper.rb
+++ b/spec/support/helpers/navbar_structure_helper.rb
@@ -77,6 +77,14 @@ def insert_infrastructure_registry_nav
     )
   end
 
+  def insert_harbor_registry_nav(within)
+    insert_after_sub_nav_item(
+      within,
+      within: _('Packages & Registries'),
+      new_sub_nav_item_name: _('Harbor Registry')
+    )
+  end
+
   def insert_infrastructure_google_cloud_nav
     insert_after_sub_nav_item(
       _('Terraform'),