Rules Gotchas
rules:andonly:/except:can't mix — use one or the other per job- First matching rule wins — put specific rules before general ones
- Missing
when:defaults toon_success—rules: - if: $CI_COMMIT_TAGruns on tag - Empty rules array
rules: []means never run — different from no rules at all - Add
- when: neverat end to prevent fallthrough — otherwise unmatched conditions may run
Silent Failures
- Protected variables missing on non-protected branches — job runs but variable is empty
- Runner tag mismatch — job stays pending forever with no error
docker:dindon non-privileged runner — fails with cryptic Docker errors- Masked variable format invalid — variable exposed in logs anyway
YAML Merge Traps
extends:doesn't deep merge arrays — scripts, variables arrays get replaced, not appended- Use
!reference [.job, script]to reuse —script: [!reference [.base, script], "my command"] include:files can override each other — last one wins for same keys- Anchors
&/*don't work across files — useextends:for cross-file reuse
Artifacts vs Cache
- Cache not guaranteed between runs — treat as optimization, not requirement
- Artifacts auto-download by stage — add
dependencies: []to skip if not needed needs:downloads artifacts by default —needs: [{job: x, artifacts: false}]to skip
Docker-in-Docker
- Shared runners usually don't support privileged — need self-hosted or special config
DOCKER_HOST: tcp://docker:2375required — job uses wrong Docker otherwiseDOCKER_TLS_CERTDIR: ""or configure TLS properly — half-configured TLS breaks builds
Pipeline Triggers
CI_PIPELINE_SOURCEdiffers by trigger —push,merge_request_event,schedule,api,trigger- MR pipelines need
rules: - if: $CI_MERGE_REQUEST_IID— not just branch rules - Detached vs merged result pipelines — detached tests source, merged tests result of merge