diff --git a/db/post_migrate/20230811145848_ensure_commit_user_mentions_note_id_bigint_backfill_is_finished_for_self_managed.rb b/db/post_migrate/20230811145848_ensure_commit_user_mentions_note_id_bigint_backfill_is_finished_for_self_managed.rb
new file mode 100644
index 0000000000000000000000000000000000000000..9510e2673dcff5e0dac76b350fedecd94c94e0cb
--- /dev/null
+++ b/db/post_migrate/20230811145848_ensure_commit_user_mentions_note_id_bigint_backfill_is_finished_for_self_managed.rb
@@ -0,0 +1,23 @@
+# frozen_string_literal: true
+
+class EnsureCommitUserMentionsNoteIdBigintBackfillIsFinishedForSelfManaged < Gitlab::Database::Migration[2.1]
+  include Gitlab::Database::MigrationHelpers::ConvertToBigint
+
+  restrict_gitlab_migration gitlab_schema: :gitlab_main
+  disable_ddl_transaction!
+
+  def up
+    return if com_or_dev_or_test_but_not_jh?
+
+    ensure_batched_background_migration_is_finished(
+      job_class_name: 'CopyColumnUsingBackgroundMigrationJob',
+      table_name: 'commit_user_mentions',
+      column_name: 'id',
+      job_arguments: [['note_id'], ['note_id_convert_to_bigint']]
+    )
+  end
+
+  def down
+    # no-op
+  end
+end
diff --git a/db/post_migrate/20230811150636_swap_commit_user_mentions_note_id_to_bigint_for_self_managed.rb b/db/post_migrate/20230811150636_swap_commit_user_mentions_note_id_to_bigint_for_self_managed.rb
new file mode 100644
index 0000000000000000000000000000000000000000..e98cf61db989c4d8632f58129e3b4fb36c98d670
--- /dev/null
+++ b/db/post_migrate/20230811150636_swap_commit_user_mentions_note_id_to_bigint_for_self_managed.rb
@@ -0,0 +1,69 @@
+# frozen_string_literal: true
+
+class SwapCommitUserMentionsNoteIdToBigintForSelfManaged < Gitlab::Database::Migration[2.1]
+  include Gitlab::Database::MigrationHelpers::ConvertToBigint
+
+  disable_ddl_transaction!
+
+  TABLE_NAME = 'commit_user_mentions'
+
+  def up
+    return if com_or_dev_or_test_but_not_jh?
+    return if temp_column_removed?(TABLE_NAME, :note_id)
+    return if columns_swapped?(TABLE_NAME, :note_id)
+
+    swap
+  end
+
+  def down
+    return if com_or_dev_or_test_but_not_jh?
+    return if temp_column_removed?(TABLE_NAME, :note_id)
+    return unless columns_swapped?(TABLE_NAME, :note_id)
+
+    swap
+  end
+
+  # Same as db/post_migrate/20230321003252_swap_commit_user_mentions_note_id_to_bigint_for_gitlab_dot_com.rb
+  def swap
+    # This will replace the existing commit_user_mentions_on_commit_id_and_note_id_unique_index
+    add_concurrent_index TABLE_NAME, [:commit_id, :note_id_convert_to_bigint], unique: true,
+      name: 'commit_user_mentions_on_commit_id_and_note_id_convert_to_bigint'
+
+    # This will replace the existing index_commit_user_mentions_on_note_id
+    add_concurrent_index TABLE_NAME, :note_id_convert_to_bigint, unique: true,
+      name: 'index_commit_user_mentions_on_note_id_convert_to_bigint'
+
+    # This will replace the existing fk_rails_a6760813e0
+    add_concurrent_foreign_key TABLE_NAME, :notes, column: :note_id_convert_to_bigint,
+      name: 'fk_commit_user_mentions_note_id_convert_to_bigint',
+      on_delete: :cascade
+
+    with_lock_retries(raise_on_exhaustion: true) do
+      execute "LOCK TABLE notes, #{TABLE_NAME} IN ACCESS EXCLUSIVE MODE"
+
+      execute "ALTER TABLE #{TABLE_NAME} RENAME COLUMN note_id TO note_id_tmp"
+      execute "ALTER TABLE #{TABLE_NAME} RENAME COLUMN note_id_convert_to_bigint TO note_id"
+      execute "ALTER TABLE #{TABLE_NAME} RENAME COLUMN note_id_tmp TO note_id_convert_to_bigint"
+
+      function_name = Gitlab::Database::UnidirectionalCopyTrigger
+                        .on_table(TABLE_NAME, connection: connection)
+                        .name(:note_id, :note_id_convert_to_bigint)
+      execute "ALTER FUNCTION #{quote_table_name(function_name)} RESET ALL"
+
+      # Swap defaults
+      change_column_default TABLE_NAME, :note_id, nil
+      change_column_default TABLE_NAME, :note_id_convert_to_bigint, 0
+
+      execute 'DROP INDEX IF EXISTS commit_user_mentions_on_commit_id_and_note_id_unique_index'
+      rename_index TABLE_NAME, 'commit_user_mentions_on_commit_id_and_note_id_convert_to_bigint',
+        'commit_user_mentions_on_commit_id_and_note_id_unique_index'
+
+      execute 'DROP INDEX IF EXISTS index_commit_user_mentions_on_note_id'
+      rename_index TABLE_NAME, 'index_commit_user_mentions_on_note_id_convert_to_bigint',
+        'index_commit_user_mentions_on_note_id'
+
+      execute "ALTER TABLE #{TABLE_NAME} DROP CONSTRAINT IF EXISTS fk_rails_a6760813e0"
+      rename_constraint(TABLE_NAME, 'fk_commit_user_mentions_note_id_convert_to_bigint', 'fk_rails_a6760813e0')
+    end
+  end
+end
diff --git a/db/schema_migrations/20230811145848 b/db/schema_migrations/20230811145848
new file mode 100644
index 0000000000000000000000000000000000000000..8aac90ce7229645d5c3f0317d5acf372ef73d27c
--- /dev/null
+++ b/db/schema_migrations/20230811145848
@@ -0,0 +1 @@
+d97af224c290fa6a16b6c855e51e7f65a64301c70ec7e222e1db76a6501ca341
\ No newline at end of file
diff --git a/db/schema_migrations/20230811150636 b/db/schema_migrations/20230811150636
new file mode 100644
index 0000000000000000000000000000000000000000..d0e166030aa00456bc326ea181d9c7f7124e1418
--- /dev/null
+++ b/db/schema_migrations/20230811150636
@@ -0,0 +1 @@
+311166d35fd7d55c27146309637e8ac8dafc1ad2988a6da7f6d1105f0368248d
\ No newline at end of file
diff --git a/spec/migrations/ensure_commit_user_mentions_note_id_bigint_backfill_is_finished_for_self_managed_spec.rb b/spec/migrations/ensure_commit_user_mentions_note_id_bigint_backfill_is_finished_for_self_managed_spec.rb
new file mode 100644
index 0000000000000000000000000000000000000000..b281403204bf53882ebd41855f2d189d546d76fa
--- /dev/null
+++ b/spec/migrations/ensure_commit_user_mentions_note_id_bigint_backfill_is_finished_for_self_managed_spec.rb
@@ -0,0 +1,35 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+require_migration!
+
+RSpec.describe EnsureCommitUserMentionsNoteIdBigintBackfillIsFinishedForSelfManaged, feature_category: :database do
+  describe '#up' do
+    let(:migration_arguments) do
+      {
+        job_class_name: 'CopyColumnUsingBackgroundMigrationJob',
+        table_name: 'commit_user_mentions',
+        column_name: 'id',
+        job_arguments: [['note_id'], ['note_id_convert_to_bigint']]
+      }
+    end
+
+    it 'ensures the migration is completed for self-managed instances' do
+      expect_next_instance_of(described_class) do |instance|
+        expect(instance).to receive(:com_or_dev_or_test_but_not_jh?).and_return(false)
+        expect(instance).to receive(:ensure_batched_background_migration_is_finished).with(migration_arguments)
+      end
+
+      migrate!
+    end
+
+    it 'skips the check for GitLab.com, dev, or test' do
+      expect_next_instance_of(described_class) do |instance|
+        expect(instance).to receive(:com_or_dev_or_test_but_not_jh?).and_return(true)
+        expect(instance).not_to receive(:ensure_batched_background_migration_is_finished)
+      end
+
+      migrate!
+    end
+  end
+end
diff --git a/spec/migrations/swap_commit_user_mentions_note_id_to_bigint_for_self_managed_spec.rb b/spec/migrations/swap_commit_user_mentions_note_id_to_bigint_for_self_managed_spec.rb
new file mode 100644
index 0000000000000000000000000000000000000000..b35da2f78db445244775c24f67e9a4cc34ac3053
--- /dev/null
+++ b/spec/migrations/swap_commit_user_mentions_note_id_to_bigint_for_self_managed_spec.rb
@@ -0,0 +1,123 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+require_migration!
+
+RSpec.describe SwapCommitUserMentionsNoteIdToBigintForSelfManaged, feature_category: :database do
+  let(:connection) { described_class.new.connection }
+  let(:commit_user_mentions) { table(:commit_user_mentions) }
+
+  shared_examples 'column `note_id_convert_to_bigint` is already dropped' do
+    before do
+      connection.execute('ALTER TABLE commit_user_mentions ALTER COLUMN note_id TYPE bigint')
+      connection.execute('ALTER TABLE commit_user_mentions DROP COLUMN IF EXISTS note_id_convert_to_bigint')
+    end
+
+    it 'does not swaps the columns' do
+      disable_migrations_output do
+        reversible_migration do |migration|
+          migration.before -> {
+            commit_user_mentions.reset_column_information
+
+            expect(commit_user_mentions.columns.find { |c| c.name == 'note_id' }.sql_type).to eq('bigint')
+            expect(commit_user_mentions.columns.find { |c| c.name == 'note_id_convert_to_bigint' }).to be_nil
+          }
+
+          migration.after -> {
+            commit_user_mentions.reset_column_information
+
+            expect(commit_user_mentions.columns.find { |c| c.name == 'note_id' }.sql_type).to eq('bigint')
+            expect(commit_user_mentions.columns.find { |c| c.name == 'note_id_convert_to_bigint' }).to be_nil
+          }
+        end
+      end
+    end
+  end
+
+  describe '#up' do
+    before do
+      # rubocop:disable RSpec/AnyInstanceOf
+      allow_any_instance_of(described_class).to(
+        receive(:com_or_dev_or_test_but_not_jh?).and_return(com_or_dev_or_test_but_not_jh?)
+      )
+      # rubocop:enable RSpec/AnyInstanceOf
+    end
+
+    context 'when GitLab.com, dev, or test' do
+      let(:com_or_dev_or_test_but_not_jh?) { true }
+
+      it_behaves_like 'column `note_id_convert_to_bigint` is already dropped'
+    end
+
+    context 'when self-managed instance with the `note_id_convert_to_bigint` column already dropped' do
+      let(:com_or_dev_or_test_but_not_jh?) { false }
+
+      it_behaves_like 'column `note_id_convert_to_bigint` is already dropped'
+    end
+
+    context 'when self-managed instance columns already swapped' do
+      let(:com_or_dev_or_test_but_not_jh?) { false }
+
+      before do
+        connection.execute('ALTER TABLE commit_user_mentions ALTER COLUMN note_id TYPE bigint')
+        connection.execute(
+          'ALTER TABLE commit_user_mentions ADD COLUMN IF NOT EXISTS note_id_convert_to_bigint integer'
+        )
+
+        disable_migrations_output { migrate! }
+      end
+
+      after do
+        connection.execute('ALTER TABLE commit_user_mentions DROP COLUMN IF EXISTS note_id_convert_to_bigint')
+      end
+
+      it 'does not swaps the columns' do
+        expect(commit_user_mentions.columns.find { |c| c.name == 'note_id' }.sql_type).to eq('bigint')
+        expect(commit_user_mentions.columns.find { |c| c.name == 'note_id_convert_to_bigint' }.sql_type).to(
+          eq('integer')
+        )
+      end
+    end
+
+    context 'when self-managed instance' do
+      let(:com_or_dev_or_test_but_not_jh?) { false }
+
+      before do
+        connection.execute('ALTER TABLE commit_user_mentions ALTER COLUMN note_id TYPE integer')
+        connection.execute('ALTER TABLE commit_user_mentions ADD COLUMN IF NOT EXISTS note_id_convert_to_bigint bigint')
+        connection.execute('ALTER TABLE commit_user_mentions ALTER COLUMN note_id_convert_to_bigint TYPE bigint')
+        connection.execute('DROP INDEX IF EXISTS index_commit_user_mentions_on_note_id_convert_to_bigint CASCADE')
+        connection.execute('CREATE OR REPLACE FUNCTION trigger_17c3a95ee58a() RETURNS trigger LANGUAGE plpgsql AS $$
+          BEGIN NEW."note_id_convert_to_bigint" := NEW."note_id"; RETURN NEW; END; $$;')
+      end
+
+      after do
+        connection.execute('ALTER TABLE commit_user_mentions DROP COLUMN IF EXISTS note_id_convert_to_bigint')
+      end
+
+      it 'swaps the columns' do
+        disable_migrations_output do
+          reversible_migration do |migration|
+            migration.before -> {
+              commit_user_mentions.reset_column_information
+
+              expect(commit_user_mentions.columns.find { |c| c.name == 'note_id' }.sql_type).to eq('integer')
+              expect(commit_user_mentions.columns.find { |c| c.name == 'note_id_convert_to_bigint' }.sql_type).to(
+                eq('bigint')
+              )
+            }
+
+            migration.after -> {
+              commit_user_mentions.reset_column_information
+
+              expect(commit_user_mentions.columns.find { |c| c.name == 'note_id' }.sql_type).to eq('bigint')
+              expect(commit_user_mentions.columns.find { |c| c.name == 'note_id_convert_to_bigint' }.sql_type).to(
+                eq('integer')
+              )
+            }
+          end
+        end
+      end
+    end
+  end
+end