Mastering GitLab CI/CD Pipeline Fundamentals: From Basics to Advanced
GitLab CI/CD: Controlling Pipeline Job Execution with when Keyword
In GitLab CI/CD, pipelines execute jobs in stages sequentially by default, moving from one stage to the next only when all jobs in the preceding stage succeed. However, real-world deployment and testing scenarios demand granular control over job execution flows—such as requiring manual approvals, forcing jobs to execute despite failures, delaying execution by a specific time window, or triggering fallback recovery jobs.
This chapter explores the when keyword in .gitlab-ci.yml, detailing its core parameters (manual, always, on_success, on_failure, and delayed) and how they govern job states in complex CI/CD architectures.
1. Why Do We Need Pipeline Execution Control? (Status Quo vs. Advanced Control)
By default, GitLab CI/CD follows a strict stage-based progression model:
- Default Stage Order:
compilestage runs firstteststage runs secondpackagestage runs last. - Default Dependency Rule: If any job in a preceding stage fails or is aborted, all subsequent dependent stages are automatically skipped, and the pipeline halts.
The Real-World Problem
While automated pipelines are ideal for continuous delivery, certain scenarios require human intervention or fault-tolerant execution paths:
- Production Deployments: You do not want a production deployment job to run automatically. You want a developer or release manager to manually click a "Play" button.
- Cleanup or Notification Jobs: If a test suite fails, you might still want a cleanup, rollback, or notification job to execute regardless of the failure.
- Timed Rollouts: You may want to introduce a deliberate delay (e.g., wait 5 minutes) before running a specific staging test or validation task.
2. Core Mental Model: The Flow Controller Analogy
Think of the when keyword as a traffic signal controller positioned between pipeline stages.
Without the when keyword, the pipeline operates on strict, rigid traffic rules (on_success). The when keyword allows you to override these traffic rules: turning an automated green light into a manual tollbooth (when: manual), installing an emergency bypass lane that opens even if the bridge collapses (when: always), or setting a timer before the light turns green (when: delayed).
3. The Default Behavior: on_success
By default, if you do not specify a when clause in your job configuration, GitLab assigns the implicit behavior: when: on_success.
How It Works
- A job configured with
on_success(or left unspecified) will execute only if all jobs in all preceding stages have succeeded. - If any preceding job fails or is canceled, the dependent job is marked as
skipped.
stages:
- compile
- test
- package
compile_job:
stage: compile
script:
- echo "Compiling code..."
test_job:
stage: test
script:
- echo "Running tests..."
# Implicitly behaves as when: on_success
package_job:
stage: package
script:
- echo "Packaging application..."
# Implicitly behaves as when: on_success
4. Manual Triggers: The when: manual Keyword
When you want to pause pipeline execution and require human verification before a job proceeds, you use when: manual.
Practical Use Case
Preventing accidental production deployments or forcing manual sign-offs before resource-expensive staging builds execute.
YAML Configuration Example
stages:
- compile
- test
- package
compile_job:
stage: compile
script:
- echo "Compiling..."
test_job:
stage: test
script:
- echo "Executing tests..."
rules:
- when: manual # Requires human click to start
package_job:
stage: package
script:
- echo "Packaging..."
Pipeline Execution Behavior (when: manual)
When the pipeline reaches a manual job, GitLab pauses execution for that job and displays a Play Button in the UI.
Note: If a manual job is placed in a middle stage and is left unclicked, subsequent stages dependent on it will wait or be blocked depending on whether they have downstream dependencies.
5. Fault-Tolerant Execution: The when: always Keyword
The when: always keyword forces a job to execute regardless of the success or failure of any previous jobs in earlier stages.
Practical Use Case
Running notification scripts, releasing temporary infrastructure, or logging diagnostic data even when the primary test suite fails.
YAML Configuration Example
stages:
- compile
- test
- package
compile_job:
stage: compile
script:
- echo "Compiling source code..."
- exit 1 # Deliberately failing the compile stage
test_job:
stage: test
script:
- echo "This test runs regardless of compile status!"
when: always # Ignores upstream failures
Execution Breakdown (when: always)
Even if compile_job fails with an explicit exit 1, test_job bypasses the standard failure-skip mechanism and runs successfully.
6. Timed Execution: The when: delayed Keyword
Sometimes you need to introduce a time lag before a job starts executing—for example, waiting for services to stabilize or scheduling a delayed integration test.
YAML Configuration Example
stages:
- compile
- test
compile_job:
stage: compile
script:
- echo "Compiling..."
delayed_test_job:
stage: test
script:
- echo "Running delayed integration tests..."
when: delayed
start_in: "5 minutes" # Delays execution by 5 minutes after upstream stage completes
Pipeline Flow with when: delayed
compile_jobcompletes successfully.delayed_test_jobenters adelayedstate (displayed as a countdown timer in the GitLab UI).- Once the specified duration (
5 minutes) elapses, GitLab automatically triggers and executes the job.
7. Error Recovery: The when: on_failure Keyword
By default, if a job in an early stage fails, all subsequent stages are skipped. However, you can configure a job to run specifically when an upstream failure occurs using when: on_failure.
Practical Use Case
Triggering rollback scripts, alert dispatchers, or diagnostic data collectors when a build or test stage breaks.
YAML Configuration Example
stages:
- compile
- test
compile_job:
stage: compile
script:
- echo "Building application..."
- exit 1 # Forcing a failure
rollback_job:
stage: test
script:
- echo "Build failed! Executing rollback procedure..."
when: on_failure # Executes ONLY if compile_job fails
8. Comprehensive Synthesis of when Options
The following table summarizes the execution criteria, default states, and primary use cases for each parameter available under the when keyword in GitLab CI/CD:
9. Summary and Key Takeaways
- Default CI/CD Flow: GitLab executes pipelines stage-by-stage sequentially, halting and skipping subsequent jobs if any upstream job fails (
when: on_success). - Human Gating (
when: manual): Pauses pipeline execution at specific jobs, requiring an authorized user to manually click the "Play" button in the GitLab UI to proceed. - Bypassing Failures (
when: always): Forces a job to execute regardless of upstream success or failure status, ensuring critical secondary tasks always run. - Time-Based Control (
when: delayed): Combines with thestart_inparameter (e.g.,start_in: "5 minutes") to schedule job execution after a designated time window. - Error Handling (
when: on_failure): Enables dedicated recovery, notification, or rollback jobs to trigger exclusively when preceding pipeline stages fail. - The
neverKeyword Limitation: Direct usage ofwhen: neveris not supported in basic job blocks; it requires advanced rule definitions (rules:if) which are evaluated in workflow and pipeline routing contexts.