diff --git a/app/controllers/projects/settings/operations_controller.rb b/app/controllers/projects/settings/operations_controller.rb
new file mode 100644
index 0000000000000000000000000000000000000000..ae8ac61ad467ccf3432165ce2d3c7650a67fae0b
--- /dev/null
+++ b/app/controllers/projects/settings/operations_controller.rb
@@ -0,0 +1,39 @@
+# frozen_string_literal: true
+
+module Projects
+  module Settings
+    class OperationsController < Projects::ApplicationController
+      before_action :check_license
+      before_action :authorize_update_environment!
+
+      def show
+      end
+
+      def update
+        result = ::Projects::Operations::UpdateService.new(project, current_user, update_params).execute
+
+        if result[:status] == :success
+          flash[:notice] = _('Your changes have been saved')
+          redirect_to project_settings_operations_path(@project)
+        else
+          render 'show'
+        end
+      end
+
+      private
+
+      def update_params
+        params.require(:project).permit(permitted_project_params)
+      end
+
+      # overridden in EE
+      def permitted_project_params
+        {}
+      end
+
+      def check_license
+        render_404 unless helpers.settings_operations_available?
+      end
+    end
+  end
+end
diff --git a/app/helpers/projects_helper.rb b/app/helpers/projects_helper.rb
index 2a673915a61bc17bcb6b1c1b35338c5dc4ec5059..fcab8cc3380a69506547cbc2b79c54d0cb32b66b 100644
--- a/app/helpers/projects_helper.rb
+++ b/app/helpers/projects_helper.rb
@@ -283,6 +283,11 @@ def show_issue_count?(disabled: false, compact_mode: false)
     !disabled && !compact_mode && Feature.enabled?(:project_list_show_issue_count, default_enabled: true)
   end
 
+  # overridden in EE
+  def settings_operations_available?
+    false
+  end
+
   private
 
   def get_project_nav_tabs(project, current_user)
diff --git a/app/services/projects/operations/update_service.rb b/app/services/projects/operations/update_service.rb
new file mode 100644
index 0000000000000000000000000000000000000000..7ff0599ee954d6c4c0a8bd1732b7357a237c68f3
--- /dev/null
+++ b/app/services/projects/operations/update_service.rb
@@ -0,0 +1,19 @@
+# frozen_string_literal: true
+
+module Projects
+  module Operations
+    class UpdateService < BaseService
+      def execute
+        Projects::UpdateService
+          .new(project, current_user, project_update_params)
+          .execute
+      end
+
+      private
+
+      def project_update_params
+        {}
+      end
+    end
+  end
+end
diff --git a/app/views/layouts/nav/sidebar/_project.html.haml b/app/views/layouts/nav/sidebar/_project.html.haml
index d8017742c90b087314088db8dbf94a61ad925831..e516c76400a24dc5c8364ec94a43854719065486 100644
--- a/app/views/layouts/nav/sidebar/_project.html.haml
+++ b/app/views/layouts/nav/sidebar/_project.html.haml
@@ -339,7 +339,10 @@
                   = link_to project_settings_ci_cd_path(@project), title: _('CI / CD') do
                     %span
                       = _('CI / CD')
-              = render_if_exists 'projects/sidebar/settings_operations'
+              - if settings_operations_available?
+                = nav_link(controller: [:operations]) do
+                  = link_to project_settings_operations_path(@project), title: _('Operations') do
+                    = _('Operations')
               - if @project.pages_available?
                 = nav_link(controller: :pages) do
                   = link_to project_pages_path(@project), title: _('Pages') do
diff --git a/app/views/projects/settings/operations/show.html.haml b/app/views/projects/settings/operations/show.html.haml
new file mode 100644
index 0000000000000000000000000000000000000000..0782029dbcddaca9e288a86feee0e4958573420f
--- /dev/null
+++ b/app/views/projects/settings/operations/show.html.haml
@@ -0,0 +1,4 @@
+- @content_class = 'limit-container-width' unless fluid_layout
+- page_title _('Operations')
+
+= render_if_exists 'projects/settings/operations/tracing'
diff --git a/config/routes/project.rb b/config/routes/project.rb
index 03c95b61e51ee22b4ff76a004109cb6be2398184..f50bf5ab76f74a3e7e77c36053bf01afd2849b85 100644
--- a/config/routes/project.rb
+++ b/config/routes/project.rb
@@ -445,6 +445,10 @@
       # its preferable to keep it below all other project routes
       draw :wiki
       draw :repository
+
+      namespace :settings do
+        resource :operations, only: [:show, :update]
+      end
     end
 
     resources(:projects,
diff --git a/locale/gitlab.pot b/locale/gitlab.pot
index ed8d4e81e9042e011f4d496d0815d3607d93b61c..1bb94c5a61a97681cb173d2bde64920e4508e445 100644
--- a/locale/gitlab.pot
+++ b/locale/gitlab.pot
@@ -7818,6 +7818,9 @@ msgstr ""
 msgid "Your changes have been committed. Commit %{commitId} %{commitStats}"
 msgstr ""
 
+msgid "Your changes have been saved"
+msgstr ""
+
 msgid "Your comment will not be visible to the public."
 msgstr ""
 
diff --git a/spec/controllers/projects/settings/operations_controller_spec.rb b/spec/controllers/projects/settings/operations_controller_spec.rb
new file mode 100644
index 0000000000000000000000000000000000000000..fbb26de76d1c80aecc650d5f8f7cfbea0f3cbc60
--- /dev/null
+++ b/spec/controllers/projects/settings/operations_controller_spec.rb
@@ -0,0 +1,35 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe Projects::Settings::OperationsController do
+  set(:user) { create(:user) }
+  set(:project) { create(:project) }
+
+  before do
+    sign_in(user)
+    project.add_maintainer(user)
+  end
+
+  describe 'GET #show' do
+    it 'returns 404' do
+      get :show, params: project_params(project)
+
+      expect(response).to have_gitlab_http_status(:not_found)
+    end
+  end
+
+  describe 'PATCH #update' do
+    it 'returns 404' do
+      patch :update, params: project_params(project)
+
+      expect(response).to have_gitlab_http_status(:not_found)
+    end
+  end
+
+  private
+
+  def project_params(project)
+    { namespace_id: project.namespace, project_id: project }
+  end
+end
diff --git a/spec/services/projects/operations/update_service_spec.rb b/spec/services/projects/operations/update_service_spec.rb
new file mode 100644
index 0000000000000000000000000000000000000000..332c1600cde9dfbbd51a16de2fa9353c156341e5
--- /dev/null
+++ b/spec/services/projects/operations/update_service_spec.rb
@@ -0,0 +1,25 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe Projects::Operations::UpdateService do
+  set(:user) { create(:user) }
+  set(:project) { create(:project) }
+
+  let(:result) { subject.execute }
+
+  subject { described_class.new(project, user, params) }
+
+  describe '#execute' do
+    context 'with inappropriate params' do
+      let(:params) { { name: '' } }
+
+      let!(:original_name) { project.name }
+
+      it 'ignores params' do
+        expect(result[:status]).to eq(:success)
+        expect(project.reload.name).to eq(original_name)
+      end
+    end
+  end
+end