diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index 723c0d4c38aee6a1fb1de451f2e58f75fe60173a..25ba711c12720720f64379ee63663f9e37531a5b 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -3,22 +3,12 @@ workflow:
     - if: $CI_PIPELINE_SOURCE == 'merge_request_event'
     - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH
     - if: $CI_COMMIT_TAG
+    - if: $RUST_REBUILD_IMAGE
 
 variables:
-  RUST_VERSION: "1.79" # slim or alpine not adapted because of openSSL dependency
+  RUST_VERSION: "1.79-1"
   RUST_IMAGE: "registry.gitlab-forge.din.developpement-durable.gouv.fr/pub/numeco/gitlab-project-doctor/rust-tools"
-  TARGET_ARCH: default
-  CARGO_HOME: .cargo
-  TEST_OPTIONS: --url $CI_PROJECT_URL -a
-  CLEAN_OPTIONS: --url $CI_PROJECT_URL -b
-
-default:
-  cache:
-    key: rust-deps
-    paths:
-      - .cargo
-    policy: pull
-
+  RUST_BASE_VERSION: "1.79" # slim or alpine not adapted because of openSSL dependency
 stages:
   - prepare
   - build_test
@@ -27,182 +17,8 @@ stages:
   - deploy_test
 
 include:
-  - component: $CI_SERVER_FQDN/share/components/oci-builder/podman@~latest
-    inputs:
-      as: build-rust-image
-      stage: .pre
-      targetImageAppName: rust-tools
-      targetImageAppTag: $RUST_VERSION
-      targetImageRegistryDigestName: "RUST"
-      DockerfileContent: |-
-        FROM rust:$RUST_VERSION
-        RUN rustup component add rustfmt
-        RUN rustup component add clippy
-        RUN rustup target add x86_64-unknown-linux-musl
-        RUN rustup target add x86_64-pc-windows-gnu
-        RUN rustup target add aarch64-apple-darwin
-        RUN rustup target add x86_64-apple-darwin
-
-  - component: $CI_SERVER_FQDN/share/components/oci-builder/podman@~latest
-    inputs:
-      as: release-gpd-image
-      stage: deploy
-      targetImageAppName: ${CI_PROJECT_NAME}
-      targetImageAppTag: $VERSION
-      targetImageRegistry: ${CI_REGISTRY}/${CI_PROJECT_NAMESPACE}
-      targetImageRegistryDigestName: "GPD"
-      forceBuild: true
-      Dockerfile: ${CI_PROJECT_DIR}/Dockerfile
-
-build-rust-image:
-  cache: [ ]
-  rules:
-    - if: $RUST_REBUILD_IMAGE
-
-release-gpd-image:
-  needs:
-    - release-linux
-  cache: [ ]
-  rules:
-    - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH
-    - if: $CI_COMMIT_TAG
-
-cache-dependencies:
-  stage: prepare
-  image: ${RUST_IMAGE}:${RUST_VERSION}
-  script:
-    - cargo fetch
-  cache:
-    key: rust-deps
-    paths:
-      - .cargo
-    policy: pull-push
-
-check-format:
-  stage: build_test
-  image: ${RUST_IMAGE}:${RUST_VERSION}
-  script:
-    - cargo fmt --all -- --check
-    - cargo clippy -- -D warnings
-
-test-rust-current:
-  stage: build_test
-  image: ${RUST_IMAGE}:${RUST_VERSION}
-  script:
-    - cargo test --verbose
-    - cargo run -- $TEST_OPTIONS
-    - cargo run -- $TEST_OPTIONS --token $TEST_GL_TOKEN
-    - cargo run -- $CLEAN_OPTIONS
-    - cargo run -- $CLEAN_OPTIONS --token $TEST_GL_TOKEN
-  rules:
-    - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH
-    - if: $CI_PIPELINE_SOURCE == 'merge_request_event'
-
-test-rust-nightly:
-  extends: test-rust-current
-  image: rustlang/rust:nightly
-  allow_failure: true
-  rules:
-    - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH
-
-.rust-release:
-  image: ${RUST_IMAGE}:${RUST_VERSION}
-  stage: release
-  variables:
-    APP_NAME: gitlab-project-doctor
-    TARGET: target/$TARGET_ARCH/release/$APP_NAME
-  script:
-    - rustup target add $TARGET_ARCH
-    - cargo build $CARGO_OPTS --target $TARGET_ARCH --release
-    - if [ -z "$NO_POSTPROCESS" ]; then strip $TARGET; $LDD_CMD $TARGET; $TARGET $TEST_OPTIONS; fi
-    - export VERSION=${CI_COMMIT_TAG:-latest}
-    - echo "VERSION=${VERSION}" > release.env
-    - 'if [ -z "$DEBUG" ]; then curl --header "JOB-TOKEN: $CI_JOB_TOKEN" --upload-file $TARGET "${CI_API_V4_URL}/projects/${CI_PROJECT_ID}/packages/generic/$TARGET_ARCH/${VERSION}/$APP_NAME"; fi'
-  allow_failure: true
-  rules:
-    - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH
-    - if: $CI_COMMIT_TAG
-  needs:
-    - job: build-rust-image
-      optional: true
-    - job: test-rust-current
-
-release-linux:
-  extends: .rust-release
-  variables:
-    TARGET_ARCH: x86_64-unknown-linux-musl
-    LDD_CMD: ldd
-  before_script:
-    - apt update -y
-    - apt install -y musl-dev musl-tools build-essential
-  artifacts:
-    paths:
-      - $TARGET
-    reports:
-      dotenv: release.env
-
-release-macos-silicon:
-  extends: .rust-release
-  variables:
-    TARGET_ARCH: aarch64-apple-darwin
-    CARGO_OPTS: -j 1 # Excessive parallelism causes OpenSSL build to fail
-    LDD_CMD: otool -L
-  tags:
-    - macos_mx
-  rules:
-    - if: $CI_COMMIT_TAG
-
-release-macos-x86:
-  extends: .rust-release
-  variables:
-    TARGET_ARCH: x86_64-apple-darwin
-    CARGO_OPTS: -j 1 # Excessive parallelism causes OpenSSL build to fail
-    LDD_CMD: otool -L
-  tags:
-    - macos_mx
-  rules:
-    - if: $CI_COMMIT_TAG
-
-release-windows:
-  extends: .rust-release
-  variables:
-    TARGET_ARCH: x86_64-pc-windows-gnu
-    APP_NAME: gitlab-project-doctor.exe
-    NO_POSTPROCESS: "true"
-  before_script:
-    - apt-get update
-    - apt-get install -y lld gcc-mingw-w64-x86-64
-  rules:
-    - if: $CI_COMMIT_TAG
-
-test-release-docker:
-  stage: deploy_test
-  image: $CI_REGISTRY/pub/numeco/gitlab-project-doctor:$VERSION
-  script:
-    - gitlab-project-doctor $TEST_OPTIONS
-    - gitlab-project-doctor $TEST_OPTIONS --token $TEST_GL_TOKEN
-    - gitlab-project-doctor $CLEAN_OPTIONS
-    - gitlab-project-doctor $CLEAN_OPTIONS --token $TEST_GL_TOKEN
-  cache: [ ]
-  needs:
-    - release-linux
-    - release-gpd-image
-  rules:
-    - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH
-    - if: $CI_COMMIT_TAG
-
-test-release-linux:
-  stage: deploy_test
-  image: curlimages/curl:8.6.0
-  needs:
-    - release-linux
-  script:
-    - 'curl -O --header "JOB-TOKEN: $CI_JOB_TOKEN" "${CI_API_V4_URL}/projects/${CI_PROJECT_ID}/packages/generic/x86_64-unknown-linux-musl/latest/gitlab-project-doctor"'
-    - chmod +x gitlab-project-doctor
-    - ./gitlab-project-doctor $TEST_OPTIONS
-    - ./gitlab-project-doctor $TEST_OPTIONS --token $TEST_GL_TOKEN
-    - ./gitlab-project-doctor $CLEAN_OPTIONS
-    - ./gitlab-project-doctor $CLEAN_OPTIONS --token $TEST_GL_TOKEN
-  rules:
-    - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH
-    - if: $CI_COMMIT_TAG
+  - local: gitlab/rust_image_workflow.yml
+  - local: gitlab/build_workflow.yml
+    rules:
+      - if: $RUST_REBUILD_IMAGE
+        when: never
\ No newline at end of file
diff --git a/gitlab/build_workflow.yml b/gitlab/build_workflow.yml
new file mode 100644
index 0000000000000000000000000000000000000000..b818d9e1c8ac85685f8e7f23f1e4e79c5177b7c1
--- /dev/null
+++ b/gitlab/build_workflow.yml
@@ -0,0 +1,170 @@
+variables:
+  TARGET_ARCH: default
+  CARGO_HOME: .cargo
+  TEST_OPTIONS: --url $CI_PROJECT_URL -a
+  CLEAN_OPTIONS: --url $CI_PROJECT_URL -b
+
+default:
+  cache:
+    key: rust-deps
+    paths:
+      - .cargo
+    policy: pull
+
+include:
+  - component: $CI_SERVER_FQDN/share/components/oci-builder/podman@~latest
+    inputs:
+      as: release-gpd-image
+      stage: deploy
+      targetImageAppName: ${CI_PROJECT_NAME}
+      targetImageAppTag: $VERSION
+      targetImageRegistry: ${CI_REGISTRY}/${CI_PROJECT_NAMESPACE}
+      targetImageRegistryDigestName: "GPD"
+      forceBuild: true
+      Dockerfile: ${CI_PROJECT_DIR}/Dockerfile
+
+release-gpd-image:
+  needs:
+    - release-linux
+  cache: [ ]
+  rules:
+    - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH
+    - if: $CI_COMMIT_TAG
+
+cache-dependencies:
+  stage: prepare
+  image: ${RUST_IMAGE}:${RUST_VERSION}
+  script:
+    - cargo fetch
+  cache:
+    key: rust-deps
+    paths:
+      - .cargo
+    policy: pull-push
+
+check-format:
+  stage: build_test
+  image: ${RUST_IMAGE}:${RUST_VERSION}
+  script:
+    - cargo fmt --all -- --check
+    - cargo clippy -- -D warnings
+
+test-rust-current:
+  stage: build_test
+  image: ${RUST_IMAGE}:${RUST_VERSION}
+  script:
+    - cargo test --verbose
+    - cargo run -- $TEST_OPTIONS
+    - cargo run -- $TEST_OPTIONS --token $TEST_GL_TOKEN
+    - cargo run -- $CLEAN_OPTIONS
+    - cargo run -- $CLEAN_OPTIONS --token $TEST_GL_TOKEN
+  rules:
+    - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH
+    - if: $CI_PIPELINE_SOURCE == 'merge_request_event'
+
+test-rust-nightly:
+  extends: test-rust-current
+  image: rustlang/rust:nightly
+  allow_failure: true
+  rules:
+    - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH
+
+.rust-release:
+  image: ${RUST_IMAGE}:${RUST_VERSION}
+  stage: release
+  variables:
+    APP_NAME: gitlab-project-doctor
+    TARGET: target/$TARGET_ARCH/release/$APP_NAME
+  script:
+    - rustup target add $TARGET_ARCH
+    - cargo build $CARGO_OPTS --target $TARGET_ARCH --release
+    - if [ -z "$NO_POSTPROCESS" ]; then strip $TARGET; $LDD_CMD $TARGET; $TARGET $TEST_OPTIONS; fi
+    - export VERSION=${CI_COMMIT_TAG:-latest}
+    - echo "VERSION=${VERSION}" > release.env
+    - 'if [ -z "$DEBUG" ]; then curl --header "JOB-TOKEN: $CI_JOB_TOKEN" --upload-file $TARGET "${CI_API_V4_URL}/projects/${CI_PROJECT_ID}/packages/generic/$TARGET_ARCH/${VERSION}/$APP_NAME"; fi'
+  allow_failure: true
+  rules:
+    - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH
+    - if: $CI_COMMIT_TAG
+  needs:
+    - test-rust-current
+
+release-linux:
+  extends: .rust-release
+  variables:
+    TARGET_ARCH: x86_64-unknown-linux-musl
+    LDD_CMD: ldd
+  before_script:
+    - apt update -y
+    - apt install -y musl-dev musl-tools build-essential
+  artifacts:
+    paths:
+      - $TARGET
+    reports:
+      dotenv: release.env
+
+release-macos-silicon:
+  extends: .rust-release
+  variables:
+    TARGET_ARCH: aarch64-apple-darwin
+    CARGO_OPTS: -j 1 # Excessive parallelism causes OpenSSL build to fail
+    LDD_CMD: otool -L
+  tags:
+    - macos_mx
+  rules:
+    - if: $CI_COMMIT_TAG
+
+release-macos-x86:
+  extends: .rust-release
+  variables:
+    TARGET_ARCH: x86_64-apple-darwin
+    CARGO_OPTS: -j 1 # Excessive parallelism causes OpenSSL build to fail
+    LDD_CMD: otool -L
+  tags:
+    - macos_mx
+  rules:
+    - if: $CI_COMMIT_TAG
+
+release-windows:
+  extends: .rust-release
+  variables:
+    TARGET_ARCH: x86_64-pc-windows-gnu
+    APP_NAME: gitlab-project-doctor.exe
+    NO_POSTPROCESS: "true"
+  before_script:
+    - apt-get update
+    - apt-get install -y lld gcc-mingw-w64-x86-64
+  rules:
+    - if: $CI_COMMIT_TAG
+
+test-release-docker:
+  stage: deploy_test
+  image: $CI_REGISTRY/pub/numeco/gitlab-project-doctor:$VERSION
+  script:
+    - gitlab-project-doctor $TEST_OPTIONS
+    - gitlab-project-doctor $TEST_OPTIONS --token $TEST_GL_TOKEN
+    - gitlab-project-doctor $CLEAN_OPTIONS
+    - gitlab-project-doctor $CLEAN_OPTIONS --token $TEST_GL_TOKEN
+  cache: [ ]
+  needs:
+    - release-linux
+    - release-gpd-image
+  rules:
+    - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH
+    - if: $CI_COMMIT_TAG
+
+test-release-linux:
+  stage: deploy_test
+  image: curlimages/curl:8.6.0
+  needs:
+    - release-linux
+  script:
+    - 'curl -O --header "JOB-TOKEN: $CI_JOB_TOKEN" "${CI_API_V4_URL}/projects/${CI_PROJECT_ID}/packages/generic/x86_64-unknown-linux-musl/latest/gitlab-project-doctor"'
+    - chmod +x gitlab-project-doctor
+    - ./gitlab-project-doctor $TEST_OPTIONS
+    - ./gitlab-project-doctor $TEST_OPTIONS --token $TEST_GL_TOKEN
+    - ./gitlab-project-doctor $CLEAN_OPTIONS
+    - ./gitlab-project-doctor $CLEAN_OPTIONS --token $TEST_GL_TOKEN
+  rules:
+    - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH
+    - if: $CI_COMMIT_TAG
diff --git a/gitlab/rust_image_workflow.yml b/gitlab/rust_image_workflow.yml
new file mode 100644
index 0000000000000000000000000000000000000000..b1bb699c8ad14009d29ef8d7bc26d371db90f1d7
--- /dev/null
+++ b/gitlab/rust_image_workflow.yml
@@ -0,0 +1,19 @@
+include:
+  - component: $CI_SERVER_FQDN/share/components/oci-builder/podman@~latest
+    inputs:
+      as: build-rust-image
+      stage: .pre
+      targetImageAppName: rust-tools
+      targetImageAppTag: $RUST_VERSION
+      targetImageRegistryDigestName: "RUST"
+      DockerfileContent: |-
+        FROM rust:$RUST_BASE_VERSION
+        RUN rustup component add rustfmt
+        RUN rustup component add clippy
+        RUN rustup target add x86_64-unknown-linux-musl
+
+build-rust-image:
+  stage: prepare
+  cache: [ ]
+  rules:
+    - if: $RUST_REBUILD_IMAGE
\ No newline at end of file