diff --git a/db/docs/batched_background_migrations/backfill_secret_push_protection_enabled.yml b/db/docs/batched_background_migrations/backfill_secret_push_protection_enabled.yml
new file mode 100644
index 0000000000000000000000000000000000000000..95c96b49564b90465ae3ab357a8d00eff59a44ee
--- /dev/null
+++ b/db/docs/batched_background_migrations/backfill_secret_push_protection_enabled.yml
@@ -0,0 +1,8 @@
+---
+migration_job_name: BackfillSecretPushProtectionEnabled
+description: Backfill missing secret_push_protection_enabled values from pre_receive_secret_detection_enabled
+feature_category: secret_detection
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/181717
+milestone: '17.10'
+queued_migration_version: 20250217193806
+finalized_by: # version of the migration that finalized this BBM
diff --git a/db/post_migrate/20250217193806_queue_backfill_secret_push_protection_enabled.rb b/db/post_migrate/20250217193806_queue_backfill_secret_push_protection_enabled.rb
new file mode 100644
index 0000000000000000000000000000000000000000..6f6d382600f8b94ee8548df99e3448f3bb63d568
--- /dev/null
+++ b/db/post_migrate/20250217193806_queue_backfill_secret_push_protection_enabled.rb
@@ -0,0 +1,33 @@
+# frozen_string_literal: true
+
+class QueueBackfillSecretPushProtectionEnabled < Gitlab::Database::Migration[2.2]
+  milestone '17.10'
+
+  MIGRATION = 'BackfillSecretPushProtectionEnabled'
+  DELAY_INTERVAL = 2.minutes
+  BATCH_SIZE = 1_000
+  SUB_BATCH_SIZE = 100
+  MAX_BATCH_SIZE = 10_000
+
+  restrict_gitlab_migration gitlab_schema: :gitlab_main
+
+  def up
+    queue_batched_background_migration(
+      MIGRATION,
+      :project_security_settings,
+      :project_id,
+      job_interval: DELAY_INTERVAL,
+      batch_size: BATCH_SIZE,
+      sub_batch_size: SUB_BATCH_SIZE,
+      max_batch_size: MAX_BATCH_SIZE
+    )
+  end
+
+  def down
+    delete_batched_background_migration(MIGRATION,
+      :project_security_settings,
+      :project_id,
+      []
+    )
+  end
+end
diff --git a/db/schema_migrations/20250217193806 b/db/schema_migrations/20250217193806
new file mode 100644
index 0000000000000000000000000000000000000000..ed590f06db4b51016a7183b42cba8648fdb07f42
--- /dev/null
+++ b/db/schema_migrations/20250217193806
@@ -0,0 +1 @@
+9b63a0d59836f068b8883f34c9f925b0f2eeae91d59a355cb9b94552b9c55a81
\ No newline at end of file
diff --git a/lib/gitlab/background_migration/backfill_secret_push_protection_enabled.rb b/lib/gitlab/background_migration/backfill_secret_push_protection_enabled.rb
new file mode 100644
index 0000000000000000000000000000000000000000..536e245d3d5767234f595cb3946fc2b01b03b307
--- /dev/null
+++ b/lib/gitlab/background_migration/backfill_secret_push_protection_enabled.rb
@@ -0,0 +1,20 @@
+# frozen_string_literal: true
+
+module Gitlab
+  module BackgroundMigration
+    class BackfillSecretPushProtectionEnabled < BatchedMigrationJob
+      extend ActiveSupport::Concern
+
+      operation_name :backfill_secret_push_protection_enabled
+      feature_category :secret_detection
+
+      def perform
+        each_sub_batch do |sub_batch|
+          sub_batch
+            .where(secret_push_protection_enabled: nil)
+            .update_all('secret_push_protection_enabled = pre_receive_secret_detection_enabled')
+        end
+      end
+    end
+  end
+end
diff --git a/spec/lib/gitlab/background_migration/backfill_secret_push_protection_enabled_spec.rb b/spec/lib/gitlab/background_migration/backfill_secret_push_protection_enabled_spec.rb
new file mode 100644
index 0000000000000000000000000000000000000000..c4fa3da8256dc38e5d551ca4942307147e1f54a3
--- /dev/null
+++ b/spec/lib/gitlab/background_migration/backfill_secret_push_protection_enabled_spec.rb
@@ -0,0 +1,84 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Gitlab::BackgroundMigration::BackfillSecretPushProtectionEnabled, feature_category: :secret_detection do
+  let(:project_security_settings) { table(:project_security_settings) }
+  let!(:connection) { table(:project_security_settings).connection }
+  let!(:starting_id) { table(:project_security_settings).pluck(:project_id).min }
+  let!(:end_id) { table(:project_security_settings).pluck(:project_id).max }
+
+  let!(:migration) do
+    described_class.new(
+      start_id: starting_id,
+      end_id: end_id,
+      batch_table: :project_security_settings,
+      batch_column: :project_id,
+      sub_batch_size: 10,
+      pause_ms: 2,
+      connection: connection,
+      job_arguments: []
+    )
+  end
+
+  let(:organizations) { table(:organizations) }
+  let(:namespaces) { table(:namespaces) }
+  let(:projects) { table(:projects) }
+
+  let!(:organization) { organizations.create!(name: 'organization', path: 'organization') }
+  let!(:namespace) do
+    namespaces
+      .create!(name: 'root-group', path: 'root', type: 'Group', organization_id: organization.id)
+      .tap do |new_group|
+        new_group.update!(traversal_ids: [new_group.id])
+      end
+  end
+
+  let!(:group_1) do
+    namespaces.create!(name: 'random-group', path: 'random', type: 'Group', organization_id: organization.id)
+  end
+
+  let!(:group_2) do
+    namespaces.create!(name: 'random-group-2', path: 'random-2', type: 'Group', organization_id: organization.id)
+  end
+
+  let!(:project_1) do
+    projects.create!(
+      organization_id: organization.id,
+      namespace_id: group_1.id,
+      project_namespace_id: group_1.id,
+      name: 'test project',
+      path: 'test-project'
+    )
+  end
+
+  let!(:project_2) do
+    projects.create!(
+      organization_id: organization.id,
+      namespace_id: group_2.id,
+      project_namespace_id: group_2.id,
+      name: 'test project-2',
+      path: 'test-project-2'
+    )
+  end
+
+  before do
+    project_security_settings.create!(project_id: project_1.id, pre_receive_secret_detection_enabled: true,
+      secret_push_protection_enabled: false)
+    project_security_settings.create!(project_id: project_2.id, pre_receive_secret_detection_enabled: false,
+      secret_push_protection_enabled: false)
+  end
+
+  it 'performs without error' do
+    expect { migration.perform }.not_to raise_error
+  end
+
+  it 'updates secret_push_protection_enabled to match pre_receive_secret_detection_enabled' do
+    migration.perform
+
+    security_settings_1 = project_security_settings.find_by(project_id: project_1.id)
+    security_settings_2 = project_security_settings.find_by(project_id: project_2.id)
+    expect(security_settings_1.secret_push_protection_enabled).to be(true)
+    expect(security_settings_2.secret_push_protection_enabled).to be(false)
+  end
+end
diff --git a/spec/migrations/20250217193806_queue_backfill_secret_push_protection_enabled_spec.rb b/spec/migrations/20250217193806_queue_backfill_secret_push_protection_enabled_spec.rb
new file mode 100644
index 0000000000000000000000000000000000000000..5ef6786f76206b4adb05c9d49127f373383ed567
--- /dev/null
+++ b/spec/migrations/20250217193806_queue_backfill_secret_push_protection_enabled_spec.rb
@@ -0,0 +1,28 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+require_migration!
+
+RSpec.describe QueueBackfillSecretPushProtectionEnabled, feature_category: :secret_detection do
+  let!(:batched_migration) { described_class::MIGRATION }
+
+  it 'schedules a new batched migration' do
+    reversible_migration do |migration|
+      migration.before -> {
+        expect(batched_migration).not_to have_scheduled_batched_migration
+      }
+
+      migration.after -> {
+        expect(batched_migration).to have_scheduled_batched_migration(
+          table_name: :project_security_settings,
+          column_name: :project_id,
+          interval: described_class::DELAY_INTERVAL,
+          batch_size: described_class::BATCH_SIZE,
+          sub_batch_size: described_class::SUB_BATCH_SIZE,
+          max_batch_size: described_class::MAX_BATCH_SIZE,
+          job_arguments: []
+        )
+      }
+    end
+  end
+end