diff --git a/substrate/.gitlab-ci.yml b/substrate/.gitlab-ci.yml
index f6347e51e62c5835ead67f91c205f4e25aaeecfb..ecc27d016a811a302bd3ad7abdd900f5b2cfa949 100644
--- a/substrate/.gitlab-ci.yml
+++ b/substrate/.gitlab-ci.yml
@@ -57,16 +57,30 @@ variables:
   NEXTEST_SUCCESS_OUTPUT:          final
   ZOMBIENET_IMAGE:                 "docker.io/paritytech/zombienet:v1.3.22"
 
-default:
+.shared-default:                   &shared-default
   retry:
     max: 2
     when:
       - runner_system_failure
       - unknown_failure
       - api_failure
-  interruptible:                   true
   cache:                           {}
 
+.default-pipeline-definitions:
+  default:
+    <<:                            *shared-default
+    interruptible:                 true
+
+.crate-publishing-pipeline-definitions:
+  default:
+    <<:                            *shared-default
+    # The crate-publishing pipeline defaults to `interruptible: false` so that we'll be able to
+    # reach and run the publishing jobs despite the "Auto-cancel redundant pipelines" CI setting.
+    # The setting is relevant because the crate-publishing pipeline runs on `master`, thus future
+    # pipelines on `master` (e.g. created for new commits or other schedules) might unintendedly
+    # cancel the publishing jobs or its dependencies before we get to actually publish the crates.
+    interruptible:                 false
+
 .collect-artifacts:
   artifacts:
     name:                          "${CI_JOB_NAME}_${CI_COMMIT_REF_NAME}"
@@ -215,9 +229,15 @@ default:
     # this job runs only on nightly pipeline with the mentioned variable, against `master` branch
     - if: $CI_COMMIT_REF_NAME == "master" && $CI_PIPELINE_SOURCE == "schedule" && $PIPELINE == "nightly"
 
+.crate-publishing-pipeline:
+  rules:
+    - if: $CI_COMMIT_REF_NAME != "master"
+      when: never
+
 .scheduled-crate-publishing-pipeline:
   rules:
-    - if: $CI_COMMIT_REF_NAME == "master" && $CI_PIPELINE_SOURCE == "schedule" && $PIPELINE == "automatic-crate-publishing"
+    - !reference [.crate-publishing-pipeline, rules]
+    - if: $CI_PIPELINE_SOURCE == "schedule" && $PIPELINE == "automatic-crate-publishing"
 
 .crates-publishing-template:
   stage:                           test
@@ -265,6 +285,22 @@ include:
   - scripts/ci/gitlab/pipeline/publish.yml
   # zombienet jobs
   - scripts/ci/gitlab/pipeline/zombienet.yml
+  # The crate-publishing pipeline requires a customized `interruptible` configuration. Unfortunately
+  # `interruptible` can't currently be dynamically set based on variables as per:
+  # - https://gitlab.com/gitlab-org/gitlab/-/issues/38349
+  # - https://gitlab.com/gitlab-org/gitlab/-/issues/194023
+  # Thus we work around that limitation by using conditional includes.
+  # For crate-publishing pipelines: run it with defaults + `interruptible: false`. The WHOLE
+  # pipeline is made uninterruptible to ensure that test jobs also get a chance to run to
+  # completion, because the publishing jobs depends on them AS INTENDED: crates should not be
+  # published before their source code is checked.
+  - local: scripts/ci/gitlab/crate-publishing-pipeline.yml
+    rules:
+      - if: $PIPELINE == "automatic-crate-publishing"
+  # For normal pipelines: run it with defaults + `interruptible: true`
+  - local: scripts/ci/gitlab/default-pipeline.yml
+    rules:
+      - if: $PIPELINE != "automatic-crate-publishing"
 
 #### stage:                        deploy
 
diff --git a/substrate/scripts/ci/gitlab/crate-publishing-pipeline.yml b/substrate/scripts/ci/gitlab/crate-publishing-pipeline.yml
new file mode 100644
index 0000000000000000000000000000000000000000..9d5303952e6efd041a331999116dc1440e4e4502
--- /dev/null
+++ b/substrate/scripts/ci/gitlab/crate-publishing-pipeline.yml
@@ -0,0 +1 @@
+default: !reference [.crate-publishing-pipeline-definitions, default]
diff --git a/substrate/scripts/ci/gitlab/default-pipeline.yml b/substrate/scripts/ci/gitlab/default-pipeline.yml
new file mode 100644
index 0000000000000000000000000000000000000000..19f6c320c3c24927b46452987857789105e6a33e
--- /dev/null
+++ b/substrate/scripts/ci/gitlab/default-pipeline.yml
@@ -0,0 +1 @@
+default: !reference [.default-pipeline-definitions, default]
diff --git a/substrate/scripts/ci/gitlab/pipeline/publish.yml b/substrate/scripts/ci/gitlab/pipeline/publish.yml
index cc7451a9fbb22836bf25fd1733592650d944f827..dff1b508625fbceb20c8c5ebe418941d6858a7b4 100644
--- a/substrate/scripts/ci/gitlab/pipeline/publish.yml
+++ b/substrate/scripts/ci/gitlab/pipeline/publish.yml
@@ -214,7 +214,7 @@ update-node-template:
   timeout:                         9h
   # A custom publishing environment is used for us to be able to set up protected secrets
   # specifically for it
-  environment: publish-crates
+  environment:                     publish-crates
   script:
     - rusty-cachier snapshot create
     - git clone
@@ -234,5 +234,6 @@ publish-crates:
 
 publish-crates-manual:
   extends:                         .publish-crates-template
+  rules:                           !reference [.crate-publishing-pipeline, rules]
   when:                            manual
   allow_failure:                   true