diff --git a/app/models/virtual_registries/packages/maven/cache/entry.rb b/app/models/virtual_registries/packages/maven/cache/entry.rb
index 7a5b4a4a9b00a2be87f940699fd90fe2758ed8ff..da698c0c41777a0685cf7308724329f8096712fc 100644
--- a/app/models/virtual_registries/packages/maven/cache/entry.rb
+++ b/app/models/virtual_registries/packages/maven/cache/entry.rb
@@ -28,6 +28,7 @@ class Entry < ApplicationRecord
           enum :status, default: 0, processing: 1, pending_destruction: 2, error: 3
 
           ignore_column :downloaded_at, remove_with: '17.9', remove_after: '2025-01-23'
+          ignore_column :file_final_path, remove_with: '17.11', remove_after: '2025-03-23'
 
           sha_attribute :file_sha1
           sha_attribute :file_md5
@@ -39,18 +40,19 @@ class Entry < ApplicationRecord
             :file_sha1,
             presence: true
           validates :upstream_etag, :content_type, length: { maximum: 255 }
-          validates :relative_path, :object_storage_key, :file_final_path, length: { maximum: 1024 }
+          validates :relative_path, :object_storage_key, length: { maximum: 1024 }
           validates :file_md5, length: { is: 32 }, allow_nil: true
           validates :file_sha1, length: { is: 40 }
           validates :relative_path,
             uniqueness: { scope: [:upstream_id, :status] },
             if: :default?
+          validates :object_storage_key, uniqueness: { scope: :relative_path }
           validates :file, presence: true
 
           mount_file_store_uploader ::VirtualRegistries::Cache::EntryUploader
 
           before_validation :set_object_storage_key,
-            if: -> { object_storage_key.blank? && relative_path && upstream && upstream.registry }
+            if: -> { object_storage_key.blank? && upstream && upstream.registry }
           attr_readonly :object_storage_key
 
           scope :search_by_relative_path, ->(query) do
@@ -106,18 +108,7 @@ def mark_as_pending_destruction
           private
 
           def set_object_storage_key
-            self.object_storage_key = Gitlab::HashedPath.new(
-              'virtual_registries',
-              'packages',
-              'maven',
-              upstream.registry.id,
-              'upstream',
-              upstream.id,
-              'cache',
-              'entry',
-              OpenSSL::Digest::SHA256.hexdigest(relative_path),
-              root_hash: upstream.registry.id
-            ).to_s
+            self.object_storage_key = upstream.object_storage_key_for(registry_id: upstream.registry.id)
           end
         end
       end
diff --git a/app/models/virtual_registries/packages/maven/upstream.rb b/app/models/virtual_registries/packages/maven/upstream.rb
index ed02de1cc19372b46facff910a7703787c60e354..bfcdacbd19e827742312a3c591c343cda91a8255 100644
--- a/app/models/virtual_registries/packages/maven/upstream.rb
+++ b/app/models/virtual_registries/packages/maven/upstream.rb
@@ -59,6 +59,24 @@ def default_cache_entries
           cache_entries.default
         end
 
+        def object_storage_key_for(registry_id:)
+          hash = Digest::SHA2.hexdigest(SecureRandom.uuid)
+          Gitlab::HashedPath.new(
+            'virtual_registries',
+            'packages',
+            'maven',
+            registry_id.to_s,
+            'upstream',
+            id.to_s,
+            'cache',
+            'entry',
+            hash[0..1],
+            hash[2..3],
+            hash[4..],
+            root_hash: registry_id
+          ).to_s
+        end
+
         private
 
         def reset_credentials
diff --git a/app/services/ci/job_artifacts/create_service.rb b/app/services/ci/job_artifacts/create_service.rb
index 82f2c22adadc3979d1e054608efe8a4297a08ba3..6b1d8a71db1ab8f82c137f739de838ade699748f 100644
--- a/app/services/ci/job_artifacts/create_service.rb
+++ b/app/services/ci/job_artifacts/create_service.rb
@@ -27,7 +27,7 @@ def authorize(artifact_type:, filesize: nil)
           has_length: false,
           maximum_size: max_size(artifact_type),
           use_final_store_path: true,
-          final_store_path_root_id: project.id
+          final_store_path_config: { root_hash: project.id }
         )
 
         if lsif?(artifact_type)
diff --git a/app/uploaders/object_storage.rb b/app/uploaders/object_storage.rb
index 7f0ac00d0bc9ae4f3a07d3eab625271186d5e69a..5c0fc1a6c01c1e516e2b8753f8933f757f2c21bd 100644
--- a/app/uploaders/object_storage.rb
+++ b/app/uploaders/object_storage.rb
@@ -177,7 +177,7 @@ def generate_remote_id
         [CarrierWave.generate_cache_id, SecureRandom.hex].join('-')
       end
 
-      def generate_final_store_path(root_id:)
+      def generate_final_store_path(root_hash:)
         hash = Digest::SHA2.hexdigest(SecureRandom.uuid)
 
         # We prefix '@final' to prevent clashes and make the files easily recognizable
@@ -186,21 +186,26 @@ def generate_final_store_path(root_id:)
 
         # We generate a hashed path of the root ID (e.g. Project ID) to distribute directories instead of
         # filling up one root directory with a bunch of files.
-        Gitlab::HashedPath.new(sub_path, root_hash: root_id).to_s
+        Gitlab::HashedPath.new(sub_path, root_hash: root_hash).to_s
       end
 
+      # final_store_path_config is only used if use_final_store_path is set to true
+      # Two keys are available:
+      # - :root_hash. The root hash used in Gitlab::HashedPath for the path generation.
+      # - :override_path. If set, the path generation is skipped and this value is used instead.
+      #                   Make sure that this value is unique for each upload.
       def workhorse_authorize(
         has_length:,
         maximum_size: nil,
         use_final_store_path: false,
-        final_store_path_root_id: nil)
+        final_store_path_config: {})
         {}.tap do |hash|
           if self.direct_upload_to_object_store?
             hash[:RemoteObject] = workhorse_remote_upload_options(
               has_length: has_length,
               maximum_size: maximum_size,
               use_final_store_path: use_final_store_path,
-              final_store_path_root_id: final_store_path_root_id
+              final_store_path_config: final_store_path_config
             )
           else
             hash[:TempPath] = workhorse_local_upload_path
@@ -231,13 +236,18 @@ def workhorse_remote_upload_options(
         has_length:,
         maximum_size: nil,
         use_final_store_path: false,
-        final_store_path_root_id: nil)
+        final_store_path_config: {})
         return unless direct_upload_to_object_store?
 
         if use_final_store_path
-          raise MissingFinalStorePathRootId unless final_store_path_root_id.present?
+          id = if final_store_path_config[:override_path].present?
+                 final_store_path_config[:override_path]
+               else
+                 raise MissingFinalStorePathRootId unless final_store_path_config[:root_hash].present?
+
+                 generate_final_store_path(root_hash: final_store_path_config[:root_hash])
+               end
 
-          id = generate_final_store_path(root_id: final_store_path_root_id)
           upload_path = with_bucket_prefix(id)
           prepare_pending_direct_upload(id)
         else
diff --git a/app/uploaders/virtual_registries/cache/entry_uploader.rb b/app/uploaders/virtual_registries/cache/entry_uploader.rb
index 92e936be3136c4ccd473c8564fc6e910303187c2..a2c5ca9d189976a1dadc942decc3d7178bfbeae2 100644
--- a/app/uploaders/virtual_registries/cache/entry_uploader.rb
+++ b/app/uploaders/virtual_registries/cache/entry_uploader.rb
@@ -28,6 +28,11 @@ def sync_model_object_store?
         true
       end
 
+      override :direct_upload_final_path_attribute_name
+      def direct_upload_final_path_attribute_name
+        :object_storage_key
+      end
+
       private
 
       def set_content_type(file)
diff --git a/db/post_migrate/20250128153614_add_object_storage_key_unique_to_v_regs_packages_maven_cache_entries.rb b/db/post_migrate/20250128153614_add_object_storage_key_unique_to_v_regs_packages_maven_cache_entries.rb
new file mode 100644
index 0000000000000000000000000000000000000000..bf4047e4bb43027bf3705489a5123935c0cdcee2
--- /dev/null
+++ b/db/post_migrate/20250128153614_add_object_storage_key_unique_to_v_regs_packages_maven_cache_entries.rb
@@ -0,0 +1,22 @@
+# frozen_string_literal: true
+
+class AddObjectStorageKeyUniqueToVRegsPackagesMavenCacheEntries < Gitlab::Database::Migration[2.2]
+  include Gitlab::Database::PartitioningMigrationHelpers
+
+  milestone '17.9'
+
+  disable_ddl_transaction!
+
+  TABLE_NAME = :virtual_registries_packages_maven_cache_entries
+  COLUMNS = [:relative_path, :object_storage_key]
+  INDEX_NAME = :idx_vregs_pkgs_mvn_cache_entries_on_uniq_object_storage_key
+
+  def up
+    truncate_tables!(TABLE_NAME.to_s)
+    add_concurrent_partitioned_index TABLE_NAME, COLUMNS, unique: true, name: INDEX_NAME
+  end
+
+  def down
+    remove_concurrent_partitioned_index_by_name TABLE_NAME, INDEX_NAME
+  end
+end
diff --git a/db/schema_migrations/20250128153614 b/db/schema_migrations/20250128153614
new file mode 100644
index 0000000000000000000000000000000000000000..a54f2bbe26fc2b1fc900b7ab75d12de5c55e6eeb
--- /dev/null
+++ b/db/schema_migrations/20250128153614
@@ -0,0 +1 @@
+d43f2fa892bb1a4b268197cd6c83b6f1437c80677fae1caadc61b52caf45f2e5
\ No newline at end of file
diff --git a/db/structure.sql b/db/structure.sql
index 6517d9917006e5a8b00d75b9bf6e8fb9c3f94e63..591c15ae407fea6dd99cbd87211d63395fa6e09f 100644
--- a/db/structure.sql
+++ b/db/structure.sql
@@ -28872,6 +28872,10 @@ CREATE INDEX index_issue_stage_events_project_duration ON ONLY analytics_cycle_a
 
 CREATE INDEX index_000925dbd7 ON gitlab_partitions_static.analytics_cycle_analytics_issue_stage_events_11 USING btree (stage_event_hash_id, project_id, end_event_timestamp, issue_id, start_event_timestamp) WHERE (end_event_timestamp IS NOT NULL);
 
+CREATE UNIQUE INDEX idx_vregs_pkgs_mvn_cache_entries_on_uniq_object_storage_key ON ONLY virtual_registries_packages_maven_cache_entries USING btree (relative_path, object_storage_key);
+
+CREATE UNIQUE INDEX index_0051c4d20c ON gitlab_partitions_static.virtual_registries_packages_maven_cache_entries_15 USING btree (relative_path, object_storage_key);
+
 CREATE INDEX index_merge_request_stage_events_project_duration ON ONLY analytics_cycle_analytics_merge_request_stage_events USING btree (stage_event_hash_id, project_id, end_event_timestamp, merge_request_id, start_event_timestamp) WHERE (end_event_timestamp IS NOT NULL);
 
 CREATE INDEX index_006f943df6 ON gitlab_partitions_static.analytics_cycle_analytics_merge_request_stage_events_16 USING btree (stage_event_hash_id, project_id, end_event_timestamp, merge_request_id, start_event_timestamp) WHERE (end_event_timestamp IS NOT NULL);
@@ -28880,6 +28884,8 @@ CREATE INDEX index_issue_stage_events_for_consistency_check ON ONLY analytics_cy
 
 CREATE INDEX index_009e6c1133 ON gitlab_partitions_static.analytics_cycle_analytics_issue_stage_events_26 USING btree (stage_event_hash_id, group_id, end_event_timestamp, issue_id);
 
+CREATE UNIQUE INDEX index_0264b93cfb ON gitlab_partitions_static.virtual_registries_packages_maven_cache_entries_11 USING btree (relative_path, object_storage_key);
+
 CREATE INDEX index_02749b504c ON gitlab_partitions_static.analytics_cycle_analytics_merge_request_stage_events_11 USING btree (stage_event_hash_id, project_id, end_event_timestamp, merge_request_id, start_event_timestamp) WHERE (end_event_timestamp IS NOT NULL);
 
 CREATE INDEX index_merge_request_stage_events_group_duration ON ONLY analytics_cycle_analytics_merge_request_stage_events USING btree (stage_event_hash_id, group_id, end_event_timestamp, merge_request_id, start_event_timestamp) WHERE (end_event_timestamp IS NOT NULL);
@@ -28962,6 +28968,8 @@ CREATE INDEX index_1f6c3faabe ON gitlab_partitions_static.analytics_cycle_analyt
 
 CREATE INDEX index_1f8af04ed1 ON gitlab_partitions_static.analytics_cycle_analytics_issue_stage_events_19 USING btree (stage_event_hash_id, group_id, start_event_timestamp, issue_id) WHERE ((end_event_timestamp IS NULL) AND (state_id = 1));
 
+CREATE UNIQUE INDEX index_1fa613e160 ON gitlab_partitions_static.virtual_registries_packages_maven_cache_entries_08 USING btree (relative_path, object_storage_key);
+
 CREATE INDEX index_201c5ddbe9 ON gitlab_partitions_static.analytics_cycle_analytics_merge_request_stage_events_00 USING btree (stage_event_hash_id, group_id, start_event_timestamp, merge_request_id) WHERE ((end_event_timestamp IS NULL) AND (state_id = 1));
 
 CREATE INDEX index_20353089e0 ON gitlab_partitions_static.analytics_cycle_analytics_issue_stage_events_20 USING btree (stage_event_hash_id, group_id, end_event_timestamp, issue_id, start_event_timestamp) WHERE (end_event_timestamp IS NOT NULL);
@@ -28996,6 +29004,10 @@ CREATE INDEX index_23783dc748 ON gitlab_partitions_static.analytics_cycle_analyt
 
 CREATE INDEX index_241e9a574c ON gitlab_partitions_static.analytics_cycle_analytics_issue_stage_events_08 USING btree (stage_event_hash_id, project_id, start_event_timestamp, issue_id) WHERE ((end_event_timestamp IS NULL) AND (state_id = 1));
 
+CREATE UNIQUE INDEX index_2439930f8c ON gitlab_partitions_static.virtual_registries_packages_maven_cache_entries_01 USING btree (relative_path, object_storage_key);
+
+CREATE UNIQUE INDEX index_2442d1fbd9 ON gitlab_partitions_static.virtual_registries_packages_maven_cache_entries_05 USING btree (relative_path, object_storage_key);
+
 CREATE INDEX index_24ac321751 ON gitlab_partitions_static.analytics_cycle_analytics_merge_request_stage_events_02 USING btree (stage_event_hash_id, project_id, start_event_timestamp, merge_request_id) WHERE ((end_event_timestamp IS NULL) AND (state_id = 1));
 
 CREATE INDEX index_25e2aaee9b ON gitlab_partitions_static.analytics_cycle_analytics_issue_stage_events_12 USING btree (stage_event_hash_id, group_id, end_event_timestamp, issue_id, start_event_timestamp) WHERE (end_event_timestamp IS NOT NULL);
@@ -29004,6 +29016,8 @@ CREATE INDEX index_2653e7eeb8 ON gitlab_partitions_static.analytics_cycle_analyt
 
 CREATE INDEX index_2745f5a388 ON gitlab_partitions_static.analytics_cycle_analytics_merge_request_stage_events_31 USING btree (stage_event_hash_id, group_id, end_event_timestamp, merge_request_id, start_event_timestamp) WHERE (end_event_timestamp IS NOT NULL);
 
+CREATE UNIQUE INDEX index_27739b516b ON gitlab_partitions_static.virtual_registries_packages_maven_cache_entries_09 USING btree (relative_path, object_storage_key);
+
 CREATE INDEX index_27759556bc ON gitlab_partitions_static.analytics_cycle_analytics_issue_stage_events_01 USING btree (stage_event_hash_id, project_id, end_event_timestamp, issue_id, start_event_timestamp) WHERE (end_event_timestamp IS NOT NULL);
 
 CREATE INDEX index_27d7ad78d8 ON gitlab_partitions_static.analytics_cycle_analytics_issue_stage_events_31 USING btree (stage_event_hash_id, group_id, start_event_timestamp, issue_id) WHERE ((end_event_timestamp IS NULL) AND (state_id = 1));
@@ -29080,6 +29094,8 @@ CREATE INDEX index_435802dd01 ON gitlab_partitions_static.analytics_cycle_analyt
 
 CREATE INDEX index_436fa9ad5f ON gitlab_partitions_static.analytics_cycle_analytics_issue_stage_events_13 USING btree (stage_event_hash_id, project_id, end_event_timestamp, issue_id, start_event_timestamp) WHERE (end_event_timestamp IS NOT NULL);
 
+CREATE UNIQUE INDEX index_43aff761b5 ON gitlab_partitions_static.virtual_registries_packages_maven_cache_entries_00 USING btree (relative_path, object_storage_key);
+
 CREATE INDEX index_453a659cb6 ON gitlab_partitions_static.analytics_cycle_analytics_merge_request_stage_events_16 USING btree (stage_event_hash_id, project_id, start_event_timestamp, merge_request_id) WHERE ((end_event_timestamp IS NULL) AND (state_id = 1));
 
 CREATE INDEX index_46b989b294 ON gitlab_partitions_static.analytics_cycle_analytics_issue_stage_events_02 USING btree (stage_event_hash_id, group_id, start_event_timestamp, issue_id) WHERE ((end_event_timestamp IS NULL) AND (state_id = 1));
@@ -29116,6 +29132,8 @@ CREATE INDEX index_4e6ce1c371 ON gitlab_partitions_static.analytics_cycle_analyt
 
 CREATE INDEX index_4ea50d3a5b ON gitlab_partitions_static.issue_search_data_24 USING btree (namespace_id);
 
+CREATE UNIQUE INDEX index_4efb1529af ON gitlab_partitions_static.virtual_registries_packages_maven_cache_entries_10 USING btree (relative_path, object_storage_key);
+
 CREATE INDEX index_4f2eb7a06b ON gitlab_partitions_static.analytics_cycle_analytics_merge_request_stage_events_08 USING btree (stage_event_hash_id, group_id, start_event_timestamp, merge_request_id) WHERE ((end_event_timestamp IS NULL) AND (state_id = 1));
 
 CREATE INDEX index_4f6fc34e57 ON gitlab_partitions_static.analytics_cycle_analytics_issue_stage_events_31 USING btree (stage_event_hash_id, project_id, end_event_timestamp, issue_id, start_event_timestamp) WHERE (end_event_timestamp IS NOT NULL);
@@ -29216,6 +29234,8 @@ CREATE INDEX index_6bf2b9282c ON gitlab_partitions_static.issue_search_data_22 U
 
 CREATE INDEX index_6cfb391b86 ON gitlab_partitions_static.analytics_cycle_analytics_merge_request_stage_events_21 USING btree (stage_event_hash_id, project_id, end_event_timestamp, merge_request_id, start_event_timestamp) WHERE (end_event_timestamp IS NOT NULL);
 
+CREATE UNIQUE INDEX index_6daa12da84 ON gitlab_partitions_static.virtual_registries_packages_maven_cache_entries_03 USING btree (relative_path, object_storage_key);
+
 CREATE INDEX index_6e560c1a4d ON gitlab_partitions_static.analytics_cycle_analytics_merge_request_stage_events_23 USING btree (stage_event_hash_id, group_id, end_event_timestamp, merge_request_id);
 
 CREATE INDEX index_6e64aa1646 ON gitlab_partitions_static.analytics_cycle_analytics_issue_stage_events_18 USING btree (stage_event_hash_id, group_id, end_event_timestamp, issue_id);
@@ -29324,6 +29344,8 @@ CREATE INDEX index_8b1b6b03b4 ON gitlab_partitions_static.analytics_cycle_analyt
 
 CREATE INDEX index_8b9f9a19a4 ON gitlab_partitions_static.analytics_cycle_analytics_merge_request_stage_events_18 USING btree (stage_event_hash_id, group_id, end_event_timestamp, merge_request_id, start_event_timestamp) WHERE (end_event_timestamp IS NOT NULL);
 
+CREATE UNIQUE INDEX index_8c8835ac5e ON gitlab_partitions_static.virtual_registries_packages_maven_cache_entries_12 USING btree (relative_path, object_storage_key);
+
 CREATE INDEX index_8fb48e72ce ON gitlab_partitions_static.analytics_cycle_analytics_issue_stage_events_26 USING btree (stage_event_hash_id, group_id, end_event_timestamp, issue_id, start_event_timestamp) WHERE (end_event_timestamp IS NOT NULL);
 
 CREATE INDEX index_907e12b7ba ON gitlab_partitions_static.issue_search_data_54 USING btree (namespace_id);
@@ -29372,6 +29394,8 @@ CREATE INDEX index_a46b7b7f26 ON gitlab_partitions_static.analytics_cycle_analyt
 
 CREATE INDEX index_a4f5106804 ON gitlab_partitions_static.issue_search_data_11 USING btree (namespace_id);
 
+CREATE UNIQUE INDEX index_a5d8ab0218 ON gitlab_partitions_static.virtual_registries_packages_maven_cache_entries_06 USING btree (relative_path, object_storage_key);
+
 CREATE INDEX index_a6999c65c9 ON gitlab_partitions_static.analytics_cycle_analytics_issue_stage_events_09 USING btree (stage_event_hash_id, group_id, start_event_timestamp, issue_id) WHERE ((end_event_timestamp IS NULL) AND (state_id = 1));
 
 CREATE INDEX index_a6c68d16b2 ON gitlab_partitions_static.analytics_cycle_analytics_merge_request_stage_events_14 USING btree (stage_event_hash_id, group_id, end_event_timestamp, merge_request_id, start_event_timestamp) WHERE (end_event_timestamp IS NOT NULL);
@@ -29438,6 +29462,8 @@ CREATE INDEX index_b7f21460bb ON gitlab_partitions_static.analytics_cycle_analyt
 
 CREATE INDEX index_b83fe1306b ON gitlab_partitions_static.analytics_cycle_analytics_merge_request_stage_events_03 USING btree (stage_event_hash_id, group_id, end_event_timestamp, merge_request_id, start_event_timestamp) WHERE (end_event_timestamp IS NOT NULL);
 
+CREATE UNIQUE INDEX index_bb41d5837a ON gitlab_partitions_static.virtual_registries_packages_maven_cache_entries_02 USING btree (relative_path, object_storage_key);
+
 CREATE INDEX index_bb6defaa27 ON gitlab_partitions_static.issue_search_data_34 USING btree (namespace_id);
 
 CREATE INDEX index_bc189e47ab ON gitlab_partitions_static.analytics_cycle_analytics_issue_stage_events_11 USING btree (stage_event_hash_id, group_id, end_event_timestamp, issue_id, start_event_timestamp) WHERE (end_event_timestamp IS NOT NULL);
@@ -29490,6 +29516,8 @@ CREATE INDEX index_c6ea8a0e26 ON gitlab_partitions_static.analytics_cycle_analyt
 
 CREATE INDEX index_c7ac8595d3 ON gitlab_partitions_static.analytics_cycle_analytics_issue_stage_events_00 USING btree (stage_event_hash_id, group_id, end_event_timestamp, issue_id);
 
+CREATE UNIQUE INDEX index_c7fa6f402d ON gitlab_partitions_static.virtual_registries_packages_maven_cache_entries_04 USING btree (relative_path, object_storage_key);
+
 CREATE INDEX index_c8bbf2b334 ON gitlab_partitions_static.issue_search_data_26 USING btree (namespace_id);
 
 CREATE INDEX index_c8c4219c0a ON gitlab_partitions_static.analytics_cycle_analytics_issue_stage_events_26 USING btree (stage_event_hash_id, group_id, start_event_timestamp, issue_id) WHERE ((end_event_timestamp IS NULL) AND (state_id = 1));
@@ -29498,6 +29526,8 @@ CREATE INDEX index_c971e6c5ce ON gitlab_partitions_static.analytics_cycle_analyt
 
 CREATE INDEX index_c9b14a3d9f ON gitlab_partitions_static.analytics_cycle_analytics_issue_stage_events_08 USING btree (stage_event_hash_id, project_id, end_event_timestamp, issue_id, start_event_timestamp) WHERE (end_event_timestamp IS NOT NULL);
 
+CREATE UNIQUE INDEX index_cb0e4510aa ON gitlab_partitions_static.virtual_registries_packages_maven_cache_entries_07 USING btree (relative_path, object_storage_key);
+
 CREATE INDEX index_cb222425ed ON gitlab_partitions_static.analytics_cycle_analytics_issue_stage_events_29 USING btree (stage_event_hash_id, group_id, end_event_timestamp, issue_id);
 
 CREATE INDEX index_cbb61ea269 ON gitlab_partitions_static.analytics_cycle_analytics_issue_stage_events_12 USING btree (stage_event_hash_id, project_id, start_event_timestamp, issue_id) WHERE ((end_event_timestamp IS NULL) AND (state_id = 1));
@@ -29554,6 +29584,8 @@ CREATE INDEX index_db6477916f ON gitlab_partitions_static.issue_search_data_28 U
 
 CREATE INDEX index_dc571ba649 ON gitlab_partitions_static.analytics_cycle_analytics_issue_stage_events_01 USING btree (stage_event_hash_id, group_id, end_event_timestamp, issue_id, start_event_timestamp) WHERE (end_event_timestamp IS NOT NULL);
 
+CREATE UNIQUE INDEX index_dc7ca9eb1d ON gitlab_partitions_static.virtual_registries_packages_maven_cache_entries_13 USING btree (relative_path, object_storage_key);
+
 CREATE INDEX index_de0334da63 ON gitlab_partitions_static.analytics_cycle_analytics_issue_stage_events_12 USING btree (stage_event_hash_id, group_id, end_event_timestamp, issue_id);
 
 CREATE INDEX index_df62a8c50e ON gitlab_partitions_static.analytics_cycle_analytics_issue_stage_events_07 USING btree (stage_event_hash_id, project_id, start_event_timestamp, issue_id) WHERE ((end_event_timestamp IS NULL) AND (state_id = 1));
@@ -29624,6 +29656,8 @@ CREATE INDEX index_f415dc2abd ON gitlab_partitions_static.issue_search_data_18 U
 
 CREATE INDEX index_f47327ec1f ON gitlab_partitions_static.analytics_cycle_analytics_merge_request_stage_events_27 USING btree (stage_event_hash_id, project_id, end_event_timestamp, merge_request_id, start_event_timestamp) WHERE (end_event_timestamp IS NOT NULL);
 
+CREATE UNIQUE INDEX index_f586c952e6 ON gitlab_partitions_static.virtual_registries_packages_maven_cache_entries_14 USING btree (relative_path, object_storage_key);
+
 CREATE INDEX index_f5f0e8eefd ON gitlab_partitions_static.issue_search_data_37 USING btree (namespace_id);
 
 CREATE INDEX index_f6b0d458a3 ON gitlab_partitions_static.analytics_cycle_analytics_issue_stage_events_15 USING btree (stage_event_hash_id, project_id, end_event_timestamp, issue_id, start_event_timestamp) WHERE (end_event_timestamp IS NOT NULL);
@@ -35702,10 +35736,14 @@ ALTER INDEX analytics_cycle_analytics_merge_request_stage_events_pkey ATTACH PAR
 
 ALTER INDEX index_issue_stage_events_project_duration ATTACH PARTITION gitlab_partitions_static.index_000925dbd7;
 
+ALTER INDEX idx_vregs_pkgs_mvn_cache_entries_on_uniq_object_storage_key ATTACH PARTITION gitlab_partitions_static.index_0051c4d20c;
+
 ALTER INDEX index_merge_request_stage_events_project_duration ATTACH PARTITION gitlab_partitions_static.index_006f943df6;
 
 ALTER INDEX index_issue_stage_events_for_consistency_check ATTACH PARTITION gitlab_partitions_static.index_009e6c1133;
 
+ALTER INDEX idx_vregs_pkgs_mvn_cache_entries_on_uniq_object_storage_key ATTACH PARTITION gitlab_partitions_static.index_0264b93cfb;
+
 ALTER INDEX index_merge_request_stage_events_project_duration ATTACH PARTITION gitlab_partitions_static.index_02749b504c;
 
 ALTER INDEX index_merge_request_stage_events_group_duration ATTACH PARTITION gitlab_partitions_static.index_0287f5ba09;
@@ -35774,6 +35812,8 @@ ALTER INDEX index_issue_stage_events_project_in_progress_duration ATTACH PARTITI
 
 ALTER INDEX index_issue_stage_events_group_in_progress_duration ATTACH PARTITION gitlab_partitions_static.index_1f8af04ed1;
 
+ALTER INDEX idx_vregs_pkgs_mvn_cache_entries_on_uniq_object_storage_key ATTACH PARTITION gitlab_partitions_static.index_1fa613e160;
+
 ALTER INDEX index_merge_request_stage_events_group_in_progress_duration ATTACH PARTITION gitlab_partitions_static.index_201c5ddbe9;
 
 ALTER INDEX index_issue_stage_events_group_duration ATTACH PARTITION gitlab_partitions_static.index_20353089e0;
@@ -35806,6 +35846,10 @@ ALTER INDEX index_merge_request_stage_events_group_in_progress_duration ATTACH P
 
 ALTER INDEX index_issue_stage_events_project_in_progress_duration ATTACH PARTITION gitlab_partitions_static.index_241e9a574c;
 
+ALTER INDEX idx_vregs_pkgs_mvn_cache_entries_on_uniq_object_storage_key ATTACH PARTITION gitlab_partitions_static.index_2439930f8c;
+
+ALTER INDEX idx_vregs_pkgs_mvn_cache_entries_on_uniq_object_storage_key ATTACH PARTITION gitlab_partitions_static.index_2442d1fbd9;
+
 ALTER INDEX index_merge_request_stage_events_project_in_progress_duration ATTACH PARTITION gitlab_partitions_static.index_24ac321751;
 
 ALTER INDEX index_issue_stage_events_group_duration ATTACH PARTITION gitlab_partitions_static.index_25e2aaee9b;
@@ -35814,6 +35858,8 @@ ALTER INDEX index_merge_request_stage_events_project_duration ATTACH PARTITION g
 
 ALTER INDEX index_merge_request_stage_events_group_duration ATTACH PARTITION gitlab_partitions_static.index_2745f5a388;
 
+ALTER INDEX idx_vregs_pkgs_mvn_cache_entries_on_uniq_object_storage_key ATTACH PARTITION gitlab_partitions_static.index_27739b516b;
+
 ALTER INDEX index_issue_stage_events_project_duration ATTACH PARTITION gitlab_partitions_static.index_27759556bc;
 
 ALTER INDEX index_issue_stage_events_group_in_progress_duration ATTACH PARTITION gitlab_partitions_static.index_27d7ad78d8;
@@ -35890,6 +35936,8 @@ ALTER INDEX index_issue_stage_events_group_in_progress_duration ATTACH PARTITION
 
 ALTER INDEX index_issue_stage_events_project_duration ATTACH PARTITION gitlab_partitions_static.index_436fa9ad5f;
 
+ALTER INDEX idx_vregs_pkgs_mvn_cache_entries_on_uniq_object_storage_key ATTACH PARTITION gitlab_partitions_static.index_43aff761b5;
+
 ALTER INDEX index_merge_request_stage_events_project_in_progress_duration ATTACH PARTITION gitlab_partitions_static.index_453a659cb6;
 
 ALTER INDEX index_issue_stage_events_group_in_progress_duration ATTACH PARTITION gitlab_partitions_static.index_46b989b294;
@@ -35926,6 +35974,8 @@ ALTER INDEX index_merge_request_stage_events_project_in_progress_duration ATTACH
 
 ALTER INDEX index_issue_search_data_on_namespace_id ATTACH PARTITION gitlab_partitions_static.index_4ea50d3a5b;
 
+ALTER INDEX idx_vregs_pkgs_mvn_cache_entries_on_uniq_object_storage_key ATTACH PARTITION gitlab_partitions_static.index_4efb1529af;
+
 ALTER INDEX index_merge_request_stage_events_group_in_progress_duration ATTACH PARTITION gitlab_partitions_static.index_4f2eb7a06b;
 
 ALTER INDEX index_issue_stage_events_project_duration ATTACH PARTITION gitlab_partitions_static.index_4f6fc34e57;
@@ -36026,6 +36076,8 @@ ALTER INDEX index_issue_search_data_on_namespace_id ATTACH PARTITION gitlab_part
 
 ALTER INDEX index_merge_request_stage_events_project_duration ATTACH PARTITION gitlab_partitions_static.index_6cfb391b86;
 
+ALTER INDEX idx_vregs_pkgs_mvn_cache_entries_on_uniq_object_storage_key ATTACH PARTITION gitlab_partitions_static.index_6daa12da84;
+
 ALTER INDEX index_mr_stage_events_for_consistency_check ATTACH PARTITION gitlab_partitions_static.index_6e560c1a4d;
 
 ALTER INDEX index_issue_stage_events_for_consistency_check ATTACH PARTITION gitlab_partitions_static.index_6e64aa1646;
@@ -36134,6 +36186,8 @@ ALTER INDEX index_mr_stage_events_for_consistency_check ATTACH PARTITION gitlab_
 
 ALTER INDEX index_merge_request_stage_events_group_duration ATTACH PARTITION gitlab_partitions_static.index_8b9f9a19a4;
 
+ALTER INDEX idx_vregs_pkgs_mvn_cache_entries_on_uniq_object_storage_key ATTACH PARTITION gitlab_partitions_static.index_8c8835ac5e;
+
 ALTER INDEX index_issue_stage_events_group_duration ATTACH PARTITION gitlab_partitions_static.index_8fb48e72ce;
 
 ALTER INDEX index_issue_search_data_on_namespace_id ATTACH PARTITION gitlab_partitions_static.index_907e12b7ba;
@@ -36182,6 +36236,8 @@ ALTER INDEX index_mr_stage_events_for_consistency_check ATTACH PARTITION gitlab_
 
 ALTER INDEX index_issue_search_data_on_namespace_id ATTACH PARTITION gitlab_partitions_static.index_a4f5106804;
 
+ALTER INDEX idx_vregs_pkgs_mvn_cache_entries_on_uniq_object_storage_key ATTACH PARTITION gitlab_partitions_static.index_a5d8ab0218;
+
 ALTER INDEX index_issue_stage_events_group_in_progress_duration ATTACH PARTITION gitlab_partitions_static.index_a6999c65c9;
 
 ALTER INDEX index_merge_request_stage_events_group_duration ATTACH PARTITION gitlab_partitions_static.index_a6c68d16b2;
@@ -36248,6 +36304,8 @@ ALTER INDEX index_issue_stage_events_group_in_progress_duration ATTACH PARTITION
 
 ALTER INDEX index_merge_request_stage_events_group_duration ATTACH PARTITION gitlab_partitions_static.index_b83fe1306b;
 
+ALTER INDEX idx_vregs_pkgs_mvn_cache_entries_on_uniq_object_storage_key ATTACH PARTITION gitlab_partitions_static.index_bb41d5837a;
+
 ALTER INDEX index_issue_search_data_on_namespace_id ATTACH PARTITION gitlab_partitions_static.index_bb6defaa27;
 
 ALTER INDEX index_issue_stage_events_group_duration ATTACH PARTITION gitlab_partitions_static.index_bc189e47ab;
@@ -36300,6 +36358,8 @@ ALTER INDEX index_issue_stage_events_group_in_progress_duration ATTACH PARTITION
 
 ALTER INDEX index_issue_stage_events_for_consistency_check ATTACH PARTITION gitlab_partitions_static.index_c7ac8595d3;
 
+ALTER INDEX idx_vregs_pkgs_mvn_cache_entries_on_uniq_object_storage_key ATTACH PARTITION gitlab_partitions_static.index_c7fa6f402d;
+
 ALTER INDEX index_issue_search_data_on_namespace_id ATTACH PARTITION gitlab_partitions_static.index_c8bbf2b334;
 
 ALTER INDEX index_issue_stage_events_group_in_progress_duration ATTACH PARTITION gitlab_partitions_static.index_c8c4219c0a;
@@ -36308,6 +36368,8 @@ ALTER INDEX index_issue_stage_events_group_duration ATTACH PARTITION gitlab_part
 
 ALTER INDEX index_issue_stage_events_project_duration ATTACH PARTITION gitlab_partitions_static.index_c9b14a3d9f;
 
+ALTER INDEX idx_vregs_pkgs_mvn_cache_entries_on_uniq_object_storage_key ATTACH PARTITION gitlab_partitions_static.index_cb0e4510aa;
+
 ALTER INDEX index_issue_stage_events_for_consistency_check ATTACH PARTITION gitlab_partitions_static.index_cb222425ed;
 
 ALTER INDEX index_issue_stage_events_project_in_progress_duration ATTACH PARTITION gitlab_partitions_static.index_cbb61ea269;
@@ -36364,6 +36426,8 @@ ALTER INDEX index_issue_search_data_on_namespace_id ATTACH PARTITION gitlab_part
 
 ALTER INDEX index_issue_stage_events_group_duration ATTACH PARTITION gitlab_partitions_static.index_dc571ba649;
 
+ALTER INDEX idx_vregs_pkgs_mvn_cache_entries_on_uniq_object_storage_key ATTACH PARTITION gitlab_partitions_static.index_dc7ca9eb1d;
+
 ALTER INDEX index_issue_stage_events_for_consistency_check ATTACH PARTITION gitlab_partitions_static.index_de0334da63;
 
 ALTER INDEX index_issue_stage_events_project_in_progress_duration ATTACH PARTITION gitlab_partitions_static.index_df62a8c50e;
@@ -36434,6 +36498,8 @@ ALTER INDEX index_issue_search_data_on_namespace_id ATTACH PARTITION gitlab_part
 
 ALTER INDEX index_merge_request_stage_events_project_duration ATTACH PARTITION gitlab_partitions_static.index_f47327ec1f;
 
+ALTER INDEX idx_vregs_pkgs_mvn_cache_entries_on_uniq_object_storage_key ATTACH PARTITION gitlab_partitions_static.index_f586c952e6;
+
 ALTER INDEX index_issue_search_data_on_namespace_id ATTACH PARTITION gitlab_partitions_static.index_f5f0e8eefd;
 
 ALTER INDEX index_issue_stage_events_project_duration ATTACH PARTITION gitlab_partitions_static.index_f6b0d458a3;
diff --git a/lib/api/concerns/virtual_registries/packages/endpoint.rb b/lib/api/concerns/virtual_registries/packages/endpoint.rb
index e0474b94d9958b968c48a3fdb36d648d379900f2..adff635b930a4f76bc3c8a9dc0e8850bcad4b823 100644
--- a/lib/api/concerns/virtual_registries/packages/endpoint.rb
+++ b/lib/api/concerns/virtual_registries/packages/endpoint.rb
@@ -77,7 +77,9 @@ def authorized_upload_response
                   has_length: true,
                   maximum_size: MAX_FILE_SIZE,
                   use_final_store_path: true,
-                  final_store_path_root_id: registry.id
+                  final_store_path_config: {
+                    override_path: upstream.object_storage_key_for(registry_id: registry.id)
+                  }
                 )
               end
 
diff --git a/lib/api/helpers/packages_helpers.rb b/lib/api/helpers/packages_helpers.rb
index 479811e510cd53cd95a1e451413c7599dd793369..620753d9765bd06e1be8b9a92c711eef77ca564b 100644
--- a/lib/api/helpers/packages_helpers.rb
+++ b/lib/api/helpers/packages_helpers.rb
@@ -60,7 +60,7 @@ def authorize_workhorse!(
 
         params = { has_length: has_length, use_final_store_path: use_final_store_path }
         params[:maximum_size] = maximum_size unless has_length
-        params[:final_store_path_root_id] = subject.id if use_final_store_path
+        params[:final_store_path_config] = { root_hash: subject.id } if use_final_store_path
         ::Packages::PackageFileUploader.workhorse_authorize(**params)
       end
 
diff --git a/spec/factories/virtual_registries/packages/maven/cache/entries.rb b/spec/factories/virtual_registries/packages/maven/cache/entries.rb
index b39c7b530f207abf8ae8cc899e3a107c3a3e5b1a..feffe61f7cfe5e8259632c8747de2c807fc91532 100644
--- a/spec/factories/virtual_registries/packages/maven/cache/entries.rb
+++ b/spec/factories/virtual_registries/packages/maven/cache/entries.rb
@@ -9,7 +9,6 @@
     size { 1.kilobyte }
     upstream_etag { OpenSSL::Digest.hexdigest('SHA256', 'test') }
     content_type { 'text/plain' }
-    file_final_path { '5f/9c/5f9c/@final/c7/4c/240c' }
     file_md5 { 'd8e8fca2dc0f896fd7cb4cb0031ba249' }
     file_sha1 { '4e1243bd22c66e76c2ba9eddc1f91394e57f9f83' }
     status { :default }
diff --git a/spec/lib/api/helpers/packages_helpers_spec.rb b/spec/lib/api/helpers/packages_helpers_spec.rb
index cd516e2abe94dbd2338634900dcf131564e35d94..b1f241d0e3ff8da4afd58508e5556efa60196e9e 100644
--- a/spec/lib/api/helpers/packages_helpers_spec.rb
+++ b/spec/lib/api/helpers/packages_helpers_spec.rb
@@ -161,7 +161,7 @@
       let(:params) { super().merge(use_final_store_path: true) }
 
       it_behaves_like 'workhorse authorize' do
-        let(:workhorse_authorize_params) { { has_length: true, use_final_store_path: true, final_store_path_root_id: project.id } }
+        let(:workhorse_authorize_params) { { has_length: true, use_final_store_path: true, final_store_path_config: { root_hash: project.id } } }
       end
     end
   end
diff --git a/spec/models/virtual_registries/packages/maven/cache/entry_spec.rb b/spec/models/virtual_registries/packages/maven/cache/entry_spec.rb
index c37951a7cc4dc018d732fe78729f9056676a2450..a2aa801e95ae1006bc20ae54628b9673ff049c0c 100644
--- a/spec/models/virtual_registries/packages/maven/cache/entry_spec.rb
+++ b/spec/models/virtual_registries/packages/maven/cache/entry_spec.rb
@@ -22,7 +22,7 @@
       it { is_expected.to validate_length_of(attr).is_at_most(255) }
     end
 
-    %i[relative_path object_storage_key file_final_path].each do |attr|
+    %i[relative_path object_storage_key].each do |attr|
       it { is_expected.to validate_length_of(attr).is_at_most(1024) }
     end
 
@@ -35,6 +35,7 @@
       end
 
       it { is_expected.to validate_uniqueness_of(:relative_path).scoped_to(:upstream_id, :status) }
+      it { is_expected.to validate_uniqueness_of(:object_storage_key).scoped_to(:relative_path) }
 
       context 'with a similar cached response in a different status' do
         let!(:cache_entry_in_error) do
@@ -103,6 +104,7 @@
     it 'can not be null' do
       cache_entry.object_storage_key = nil
       cache_entry.relative_path = nil
+      cache_entry.upstream = nil
 
       expect(cache_entry).to be_invalid
       expect(cache_entry.errors.full_messages).to include("Object storage key can't be blank")
diff --git a/spec/models/virtual_registries/packages/maven/upstream_spec.rb b/spec/models/virtual_registries/packages/maven/upstream_spec.rb
index 13454398a53beba07286bf1577d3156a81e0241a..60ffb25dab4b821057061ea0176c8850c9c904cc 100644
--- a/spec/models/virtual_registries/packages/maven/upstream_spec.rb
+++ b/spec/models/virtual_registries/packages/maven/upstream_spec.rb
@@ -247,4 +247,23 @@
 
     it { is_expected.to contain_exactly(default_cache_entry) }
   end
+
+  describe '#object_storage_key_for' do
+    let_it_be(:upstream) { build_stubbed(:virtual_registries_packages_maven_upstream) }
+
+    let(:registry_id) { '555' }
+
+    subject { upstream.object_storage_key_for(registry_id: registry_id) }
+
+    it 'contains the expected terms' do
+      is_expected.to include("virtual_registries/packages/maven/#{registry_id}/upstream/#{upstream.id}/cache/entry")
+    end
+
+    it 'does not return the same value when called twice' do
+      first_value = upstream.object_storage_key_for(registry_id: registry_id)
+      second_value = upstream.object_storage_key_for(registry_id: registry_id)
+
+      expect(first_value).not_to eq(second_value)
+    end
+  end
 end
diff --git a/spec/requests/api/generic_packages_spec.rb b/spec/requests/api/generic_packages_spec.rb
index 23a45bc0b7f61e3be387d41ef43b34e4cea89ac0..948c0692c258ba4af3d42ad6ef5fb90142425d36 100644
--- a/spec/requests/api/generic_packages_spec.rb
+++ b/spec/requests/api/generic_packages_spec.rb
@@ -195,7 +195,7 @@ def deploy_token_header(value)
 
       it 'sends use_final_store_path with true' do
         expect(::Packages::PackageFileUploader).to receive(:workhorse_authorize).with(
-          hash_including(use_final_store_path: true, final_store_path_root_id: project.id)
+          hash_including(use_final_store_path: true, final_store_path_config: { root_hash: project.id })
         ).and_call_original
 
         authorize_upload_file(workhorse_headers.merge(personal_access_token_header))
diff --git a/spec/requests/api/terraform/modules/v1/project_packages_spec.rb b/spec/requests/api/terraform/modules/v1/project_packages_spec.rb
index eea4bd9d1580e8d89383f9bf0938e7a70e311c11..e7c2ac1bbd6b26effb73deb3eed4d2f9ab6e9091 100644
--- a/spec/requests/api/terraform/modules/v1/project_packages_spec.rb
+++ b/spec/requests/api/terraform/modules/v1/project_packages_spec.rb
@@ -89,7 +89,7 @@
 
       it 'sends use_final_store_path with true' do
         expect(::Packages::PackageFileUploader).to receive(:workhorse_authorize).with(
-          hash_including(use_final_store_path: true, final_store_path_root_id: project.id)
+          hash_including(use_final_store_path: true, final_store_path_config: { root_hash: project.id })
         ).and_call_original
 
         api_request
diff --git a/spec/requests/api/virtual_registries/packages/maven/endpoints_spec.rb b/spec/requests/api/virtual_registries/packages/maven/endpoints_spec.rb
index 2defc354f72c2e5b9460b4c3cc8cbcd78608ae9e..bd6c7882135839455de60563edb7c862453db5c0 100644
--- a/spec/requests/api/virtual_registries/packages/maven/endpoints_spec.rb
+++ b/spec/requests/api/virtual_registries/packages/maven/endpoints_spec.rb
@@ -39,7 +39,7 @@
         expect(::VirtualRegistries::Cache::EntryUploader).to receive(:workhorse_authorize).with(
           a_hash_including(
             use_final_store_path: true,
-            final_store_path_root_id: registry.id
+            final_store_path_config: { override_path: be_instance_of(String) }
           )
         ).and_call_original
 
diff --git a/spec/services/ci/job_artifacts/create_service_spec.rb b/spec/services/ci/job_artifacts/create_service_spec.rb
index 3bfd6ed6bed91366971fec5bd67271f0b1fe0e07..2601eb714dedd75dcffaf455a176918d8b515ad1 100644
--- a/spec/services/ci/job_artifacts/create_service_spec.rb
+++ b/spec/services/ci/job_artifacts/create_service_spec.rb
@@ -97,7 +97,7 @@
 
           allow(JobArtifactUploader)
             .to receive(:generate_final_store_path)
-            .with(root_id: project.id)
+            .with(root_hash: project.id)
             .and_return(final_store_path)
         end
 
diff --git a/spec/support/helpers/orphan_final_artifacts_cleanup_helpers.rb b/spec/support/helpers/orphan_final_artifacts_cleanup_helpers.rb
index ce67cf0e6b6d9cc72dc937b5f77ad815f49cf31d..7a0d9f7f172011b1c2c2458c57c2062ef3fe2fbc 100644
--- a/spec/support/helpers/orphan_final_artifacts_cleanup_helpers.rb
+++ b/spec/support/helpers/orphan_final_artifacts_cleanup_helpers.rb
@@ -3,7 +3,7 @@
 module OrphanFinalArtifactsCleanupHelpers
   def create_fog_file(final: true)
     path = if final
-             JobArtifactUploader.generate_final_store_path(root_id: 123)
+             JobArtifactUploader.generate_final_store_path(root_hash: 123)
            else
              JobArtifactUploader.generate_remote_id
            end
diff --git a/spec/uploaders/object_storage_spec.rb b/spec/uploaders/object_storage_spec.rb
index 20fa810b86b8a8c7ac436bb6ced7204e911a7a39..4da6aa48fa65e154c9fc39ce8d80ef2664ae2837 100644
--- a/spec/uploaders/object_storage_spec.rb
+++ b/spec/uploaders/object_storage_spec.rb
@@ -552,14 +552,15 @@ def when_file_is_in_use
     let(:has_length) { true }
     let(:maximum_size) { nil }
     let(:use_final_store_path) { false }
-    let(:final_store_path_root_id) { nil }
+    let(:final_store_path_root_hash) { nil }
+    let(:final_store_path_config) { { root_hash: final_store_path_root_hash } }
 
     subject do
       uploader_class.workhorse_authorize(
         has_length: has_length,
         maximum_size: maximum_size,
         use_final_store_path: use_final_store_path,
-        final_store_path_root_id: final_store_path_root_id
+        final_store_path_config: final_store_path_config
       )
     end
 
@@ -644,24 +645,24 @@ def when_file_is_in_use
     shared_examples 'handling object storage final upload path' do |multipart|
       context 'when use_final_store_path is true' do
         let(:use_final_store_path) { true }
-        let(:final_store_path_root_id) { 12345 }
+        let(:final_store_path_root_hash) { 12345 }
         let(:final_store_path) { File.join('@final', 'myprefix', 'abc', '123', 'somefilename') }
         let(:escaped_path) { escape_path(final_store_path) }
 
-        context 'and final_store_path_root_id was not given' do
-          let(:final_store_path_root_id) { nil }
+        context 'and final_store_path_root_hash was not given' do
+          let(:final_store_path_root_hash) { nil }
 
           it 'raises an error' do
             expect { subject }.to raise_error(ObjectStorage::MissingFinalStorePathRootId)
           end
         end
 
-        context 'and final_store_path_root_id was given' do
+        context 'and final_store_path_root_hash was given' do
           before do
             stub_object_storage_multipart_init_with_final_store_path("#{storage_url}#{final_store_path}") if multipart
 
             allow(uploader_class).to receive(:generate_final_store_path)
-              .with(root_id: final_store_path_root_id)
+              .with(root_hash: final_store_path_root_hash)
               .and_return(final_store_path)
           end
 
@@ -720,6 +721,35 @@ def when_file_is_in_use
             end
           end
         end
+
+        context 'and override_path was given' do
+          let(:override_path) { 'test_override_path' }
+          let(:final_store_path_config) { { override_path: override_path } }
+
+          before do
+            stub_object_storage_multipart_init_with_final_store_path("#{storage_url}#{override_path}") if multipart
+          end
+
+          it 'uses the override instead of generating a path' do
+            expect(uploader_class).not_to receive(:generate_final_store_path)
+
+            expect(subject[:RemoteObject][:ID]).to eq(override_path)
+            expect(subject[:RemoteObject][:GetURL]).to include(override_path)
+            expect(subject[:RemoteObject][:StoreURL]).to include(override_path)
+
+            if multipart
+              expect(subject[:RemoteObject][:MultipartUpload][:PartURLs]).to all(include(override_path))
+              expect(subject[:RemoteObject][:MultipartUpload][:CompleteURL]).to include(override_path)
+              expect(subject[:RemoteObject][:MultipartUpload][:AbortURL]).to include(override_path)
+            end
+
+            expect(subject[:RemoteObject][:SkipDelete]).to eq(true)
+
+            expect(
+              ObjectStorage::PendingDirectUpload.exists?(uploader_class.storage_location_identifier, override_path)
+            ).to eq(true)
+          end
+        end
       end
 
       def escape_path(path)
@@ -1334,10 +1364,10 @@ def escape_path(path)
   end
 
   describe '.generate_final_store_path' do
-    let(:root_id) { 12345 }
-    let(:expected_root_hashed_path) { Gitlab::HashedPath.new(root_hash: root_id) }
+    let(:root_hash) { 12345 }
+    let(:expected_root_hashed_path) { Gitlab::HashedPath.new(root_hash: root_hash) }
 
-    subject(:final_path) { uploader_class.generate_final_store_path(root_id: root_id) }
+    subject(:final_path) { uploader_class.generate_final_store_path(root_hash: root_hash) }
 
     before do
       allow(Digest::SHA2).to receive(:hexdigest).and_return('somehash1234')