diff --git a/.gitlab-ci/lint.yml b/.gitlab-ci/lint.yml
index 658bd35aa325c506930f47ec006887ce7a50dfef..a407fd384450ee85b33d99e84786fbf0272f6d08 100644
--- a/.gitlab-ci/lint.yml
+++ b/.gitlab-ci/lint.yml
@@ -1,4 +1,25 @@
 ---
+generate-pre-commit:
+  image: 'mikefarah/yq@sha256:bcb889a1f9bdb0613c8a054542d02360c2b1b35521041be3e1bd8fbd0534d411'
+  stage: build
+  before_script: []
+  script:
+    - >
+      yq -r < .pre-commit-config.yaml '.repos[].hooks[].id' |
+      sed 's/^/      - /' |
+      cat .gitlab-ci/pre-commit-dynamic-stub.yml - > pre-commit-generated.yml
+  artifacts:
+    paths:
+      - pre-commit-generated.yml
+
+run-pre-commit:
+  stage: unit-tests
+  trigger:
+    include:
+      - artifact: pre-commit-generated.yml
+        job: generate-pre-commit
+    strategy: depend
+
 yamllint:
   extends: .job
   stage: unit-tests
diff --git a/.gitlab-ci/pre-commit-dynamic-stub.yml b/.gitlab-ci/pre-commit-dynamic-stub.yml
new file mode 100644
index 0000000000000000000000000000000000000000..55f81b513a21ffa987f003b09574f6931ae9e132
--- /dev/null
+++ b/.gitlab-ci/pre-commit-dynamic-stub.yml
@@ -0,0 +1,17 @@
+---
+# stub pipeline for dynamic generation
+pre-commit:
+  tags:
+  - light
+  image: 'ghcr.io/pre-commit-ci/runner-image@sha256:aaf2c7b38b22286f2d381c11673bec571c28f61dd086d11b43a1c9444a813cef'
+  variables:
+    PRE_COMMIT_HOME: /pre-commit-cache
+  script:
+  - pre-commit run -a $HOOK_ID
+  cache:
+    key: pre-commit-$HOOK_ID
+    paths:
+    - /pre-commit-cache
+  parallel:
+    matrix:
+    - HOOK_ID: