Intro
GitLab CI/CD est puissant, mais de petites erreurs peuvent bloquer une livraison. Ce guide se concentre sur les GitLab CI/CD common errors que vous êtes le plus susceptible de rencontrer dans de vrais pipelines, pourquoi elles arrivent, comment confirmer la cause racine, et des GitLab CI/CD fixes sûrs et copiables. Restez pratique : reproduisez avec le plus petit exemple, appliquez le changement minimum et généralisable, puis documentez.
Ce que vous obtiendrez :
- Des explications claires des GitLab CI/CD error messages les plus courants
- Des repros minimaux et des extraits de jobs adaptables
- Des correctifs « safe-by-default » testables localement avant déploiement
Aperçu du flux de travail
Utilisez un flux répétable qui transforme le bruit en signal :
- Reproduire et capturer le contexte
- Relancez le job en échec avec le même commit.
- Dépliez toutes les sections du log. Lisez du début : infos runner, pull d'image, checkout, before_script, script, after_script, artifacts/upload.
- Notez la ligne défaillante et la phase où elle survient.
- Confirmer le plus petit repro
- Commentez les étapes non essentielles.
- Figez les variables qui influencent le flux.
- Si un service est impliqué (Docker, base, Kubernetes), testez avec un job unique et un script minuscule.
- Appliquer le plus petit correctif sûr
- Préférez les réglages de configuration aux contournements scriptés.
- Ajoutez des garde-fous (rules, paths, timeouts explicites) pour éviter les récidives.
- Vérifier et documenter
- Relancez le job, puis un pipeline complet.
- Ajoutez un court commentaire dans .gitlab-ci.yml près du correctif.
Erreurs courantes et correctifs
Voici des échecs fréquents, leurs causes, comment vérifier, et des correctifs sûrs.
1) .gitlab-ci.yml ou structure YAML invalide
Symptômes
- Le pipeline ne démarre pas, ou : "This GitLab CI configuration is invalid".
- Messages au niveau job : "before_script config should be a string or a list of strings".
Pourquoi cela arrive
- Indentation ou listes YAML incorrectes.
- Utilisation d'une map là où une liste est attendue (ou l'inverse).
Comment diagnostiquer
- Réduisez à un .gitlab-ci.yml minimal avec un simple echo.
- Vérifiez que toutes les listes commencent par des tirets et que les scripts sont des listes de chaînes.
Correctif sûr - exemple
stages: [lint, test]
variables:
NODE_ENV: 'test'
lint:
stage: lint
image: node:20-alpine
script:
- node --version
- npm ci
- npm run lint
test:
stage: test
image: node:20-alpine
script:
- npm ci
- npm test
Conseils
- Évitez de mélanger tabulations et espaces.
- Laissez script sous forme de liste de chaînes, pas un bloc multilignes.
2) Le pipeline n'a aucun job
Symptômes
- La page pipeline affiche : "There are no stages/jobs for this pipeline." ou rien ne s'exécute pour votre branche/MR.
Pourquoi cela arrive
- rules ou only/except excluent votre ref.
- workflow: rules empêche la création du pipeline.
Comment diagnostiquer
- Inspectez l'événement (branche ou MR) et les variables d'environnement.
- Ajoutez temporairement un job filet de sécurité pour valider l'appariement.
Correctif sûr - exemple avec rules et repli
workflow:
rules:
- if: '$CI_PIPELINE_SOURCE == "merge_request_event"'
- if: '$CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH'
- when: always # crée un pipeline en cas de doute
build:
stage: build
image: alpine:3.20
rules:
- changes:
- src/**
when: on_success
- when: manual # repli visible si aucun changes ne correspond
script:
- echo 'Building...'
3) Job bloqué ou en attente : pas de runners
Symptômes
- Le job reste en pending : "This job is stuck because the project does not have any runners online assigned to it."
Pourquoi cela arrive
- Aucun runner en ligne, ou les tags runner ne correspondent pas au job.
- Le runner n'est pas autorisé pour ce projet ou branche protégée.
Comment diagnostiquer
- Comparez les tags du job et du runner (Settings > CI/CD > Runners).
- Confirmez que le runner est en ligne et non en pause.
Correctif sûr - aligner les tags ou supprimer l'exigence
build-image:
tags: ['docker']
image: docker:24
services: ['docker:24-dind']
variables:
DOCKER_HOST: 'tcp://docker:2375'
DOCKER_TLS_CERTDIR: ''
script:
- docker version
- docker build -t demo: ci .
Conseils
- Pour les refs protégées, marquez le runner comme protégé ou ne restreignez pas le job.
4) Docker-in-Docker : impossible de se connecter au daemon
Symptômes
- "Cannot connect to the Docker daemon at unix:///var/run/docker.sock. Is the docker daemon running?"
Pourquoi cela arrive
- Service Docker non démarré, hôte erroné, TLS mal configuré.
Comment diagnostiquer
- Affichez l'environnement et lancez docker info.
- Vérifiez l'alias du service et DOCKER_HOST.
Correctif sûr - exemple
image: docker:24
services:
- name: docker:24-dind
command: ['--mtu=1460'] # optionnel, corrige certains soucis réseau
variables:
DOCKER_HOST: 'tcp://docker:2375'
DOCKER_TLS_CERTDIR: ''
DOCKER_DRIVER: 'overlay2'
build:
stage: build
script:
- docker version
- docker build -t $CI_REGISTRY_IMAGE:ci-$CI_COMMIT_SHORT_SHA .
- echo 'Build complete'
Conseils
- Désactivez TLS côté job et isolez le réseau CI.
- Avec un runner partagé, préférez des images de base légères.
5) Envoi d'artefacts : fichier introuvable
Symptômes
- "Uploading artifacts... WARNING: No files to upload" ou "Could not find a file matching ..."
Pourquoi cela arrive
- Chemin erroné, fichiers générés ailleurs, ou job échoué avant production.
Comment diagnostiquer
- Ajoutez ls -R pour confirmer les chemins.
- Vérifiez le répertoire de travail ($CI_PROJECT_DIR).
Correctif sûr - exemple
test:
stage: test
image: node:20-alpine
script:
- npm ci
- npm test -- --reporter=junit --reporter-options output=reports/junit.xml
- mkdir -p dist && echo ok > dist/health.txt
artifacts:
when: always
paths:
- reports/junit.xml
- dist/
expire_in: '7 days'
6) Le cache ne persiste pas entre jobs
Symptômes
- Réinstallation des dépendances à chaque run ; 0 % de hit cache.
Pourquoi cela arrive
- Clé de cache trop large, trop étroite, ou changeant à chaque run.
Comment diagnostiquer
- Affichez la clé de cache et comparez entre jobs.
Correctif sûr - clé basée contenu avec repli
cache:
key:
files:
- package-lock.json
paths:
- node_modules/
policy: pull-push
install:
stage: .pre
image: node:20-alpine
script:
- npm ci --cache .npm --prefer-offline
artifacts:
paths: [.npm]
expire_in: '1 day'
7) Git submodules : échec du fetch
Symptômes
- "fatal: could not read Username for 'https://gitlab.com': No such device or address"
- "fatal: repository not found" côté submodules.
Pourquoi cela arrive
- Submodules en HTTPS sans identifiants, ou projet privé non accessible via token.
Comment diagnostiquer
- Affichez .gitmodules et vérifiez les URLs.
- Vérifiez l'accès via CI_JOB_TOKEN pour les projets concernés.
Correctif sûr - utiliser CI_JOB_TOKEN en HTTPS
variables:
GIT_SUBMODULE_STRATEGY: 'recursive'
GIT_SUBMODULE_UPDATE_FLAGS: '--jobs 4'
before_script:
- git config --global url."https://gitlab-ci-token:${CI_JOB_TOKEN}@gitlab.com/".insteadOf "https://gitlab.com/"
submodule_job:
stage: test
image: alpine:3.20
script:
- git submodule sync --recursive
- git submodule update --init --recursive
- echo 'Submodules ready'
8) Variables protégées manquantes hors branches protégées
Symptômes
- docker login ou un CLI cloud renvoie 403/erreurs d'auth seulement sur les feature branches.
Pourquoi cela arrive
- Variables marquées « protected », exposées uniquement sur refs protégées.
Comment diagnostiquer
- Comparez les drapeaux de protection des variables et celui de la branche.
Correctif sûr - restreindre le job aux refs protégées
docker_push:
stage: deploy
rules:
- if: '$CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH'
- if: '$CI_COMMIT_TAG'
- when: never
script:
- echo "$REGISTRY_PASSWORD" | docker login -u "$REGISTRY_USER" --password-stdin "$CI_REGISTRY"
- docker push $CI_REGISTRY_IMAGE:release
9) Kubernetes : erreurs kubectl ou Helm
Symptômes
- kubectl : "unable to recognize: no matches for kind ..."
- Helm : "UPGRADE FAILED: another operation is in progress" ou timeouts.
Pourquoi cela arrive
- Mauvaise apiVersion ou CRDs manquants ; permissions manquantes ; verrous Helm obsolètes ; kubeconfig absent.
Comment diagnostiquer
- kubectl version --client et versions serveur.
- kubectl auth can-i ... pour vérifier les permissions.
- helm list et helm history pour l'état des releases.
Correctif sûr - kubeconfig via variable et upgrades atomiques
deploy:
stage: deploy
image: alpine/helm:3.14.4
variables:
KUBECONFIG: $CI_PROJECT_DIR/.kube/config
before_script:
- mkdir -p $(dirname "$KUBECONFIG")
- echo "$KUBECONFIG_DATA" | base64 -d > "$KUBECONFIG"
- kubectl version --client
- kubectl config get-contexts
script:
- kubectl apply -f k8s/ns.yaml
- helm upgrade --install app charts/app \
--namespace app \
--create-namespace \
--atomic --wait --timeout 5m
Conseils
- Gardez les versions kubectl/Helm compatibles avec le cluster.
- Utilisez --atomic et --wait pour échouer vite et rollback.
10) Job au-delà de la limite de temps
Symptômes
- "script exceeded time limit" ou job annulé après une durée fixe.
Pourquoi cela arrive
- Timeout par défaut trop bas ; pulls d'images lents ; cache manqué ; tests longs.
Comment diagnostiquer
- Analysez la durée par phase dans les logs.
- Notez le temps des pulls et attentes réseau.
Correctif sûr - timeout par job et pulls optimisés
long_tests:
stage: test
image: python:3.12-slim
timeout: 45m
script:
- pip install -r requirements.txt
- pytest -m 'not slow' -n auto --dist loadscope
Conseils
- Utilisez des images plus petites et le cache côté registry.
- Scindez en shards parallèles et fusionnez les rapports via artefacts.
Plan pilote local
Objectif : valider vos correctifs et patrons avec le plus petit pipeline utile, observable localement avant généralisation.
Périmètre
- Un dépôt, un runner partagé ou spécifique.
- Stages : validate, test, build, package.
- Build Docker-in-Docker, cache basique et un artefact.
Critères de succès
- Pipeline < 10 minutes sur un runner propre.
- Cache « hit » au second run pour les dépendances.
- Aucun job en attente ; pas d'avertissement d'artefacts.
Pipeline pilote de référence
stages: [validate, test, build, package]
default:
image: docker:24
services: ['docker:24-dind']
variables:
DOCKER_HOST: 'tcp://docker:2375'
DOCKER_TLS_CERTDIR: ''
validate_yaml:
stage: validate
image: alpine:3.20
script:
- echo 'Validating YAML layout'
- grep -q 'stages:' .gitlab-ci.yml
unit_tests:
stage: test
image: node:20-alpine
cache:
key:
files: [package-lock.json]
paths: [node_modules/]
policy: pull-push
script:
- npm ci
- npm test -- --reporter=junit --reporter-options output=reports/junit.xml
artifacts:
when: always
paths: [reports/junit.xml]
expire_in: '7 days'
build_image:
stage: build
tags: ['docker'] # assurez-vous que cela correspond au runner
script:
- echo "$CI_REGISTRY_PASSWORD" | docker login -u "$CI_REGISTRY_USER" --password-stdin "$CI_REGISTRY"
- docker build -t $CI_REGISTRY_IMAGE:ci-$CI_COMMIT_SHORT_SHA .
- docker image ls | head -n 5
package_sbom:
stage: package
image: alpine:3.20
script:
- mkdir -p out && echo '{"sbom":"demo"}' > out/sbom.json
artifacts:
paths: [out/sbom.json]
expire_in: '7 days'
Comment exécuter et inspecter
- Poussez une feature branch et ouvrez une petite MR.
- Confirmez l'alignement des tags runner et le « cache hit » au second run.
- Cassez volontairement un chemin (ex. artefacts), observez l'erreur, puis revenez en arrière.
Déploiement
- Copiez ces patrons dans d'autres dépôts en ajustant images, tags, et clés de cache.
- Conservez les jobs pilote comme templates réutilisables.
Conclusion
Vous disposez désormais de schémas pratiques pour reconnaître et corriger les erreurs GitLab CI/CD les plus courantes :
- Validez le YAML tôt et gardez des scripts simples.
- Assurez la disponibilité des runners et l'alignement des tags.
- Configurez Docker-in-Docker de manière prédictible.
- Produisez des artefacts de façon fiable et mettez en cache ce qui coûte à reconstruire.
- Authentifiez correctement submodules et registries.
- Déployez sur Kubernetes avec kubeconfig explicite, attentes et rollback atomique.
- Contrôlez les timeouts et parallélisez les longues tâches.
Prochaines étapes
- Mettez en place le pipeline pilote et enregistrez les métriques de base.
- Corrigez d'abord l'erreur la plus pénible dans votre contexte, puis élargissez.
- Gardez des changements petits, observables, et faciles à revenir en arrière.