GitLab CI Only Keyword Migration and Job Control Logic

The architecture of a GitLab CI/CD pipeline relies on the precise orchestration of jobs, which are the fundamental elements that execute specific commands to accomplish tasks such as building, testing, or deploying code. Central to this orchestration is the ability to control exactly when a job enters a pipeline. Historically, this was managed primarily through the only and except keywords. These keywords allow developers to define the conditions under which a job should be added to a pipeline, such as restricting a deployment job to only run on the main branch or preventing a test job from running during a scheduled pipeline. However, as GitLab has evolved, the only keyword has been superseded by the more powerful and flexible rules keyword. Understanding the transition from only to rules is critical for maintaining modern, scalable pipelines and avoiding the use of deprecated syntax that may lead to future failures or lack of support.

The Mechanics of Job Execution and Control

CI/CD jobs are designed to run on a runner, often within a Docker container, and they operate independently from other jobs within the same stage. Every job generates a comprehensive job log that captures the full execution history, which is essential for debugging and auditing. To manage these jobs, GitLab provides a variety of keywords that define the execution environment and the logic governing the job's lifecycle.

The logic that determines whether a job is added to a pipeline is primarily handled by only, except, or rules. If a job configuration does not explicitly include any of these three keywords, GitLab applies a default behavior where the job is treated as if it has only: branches and only: tags. This means the job will trigger for any branch or tag push by default.

Comprehensive Analysis of the Only Keyword

The only keyword is a job-level configuration used to specify the conditions under which a job is added to a pipeline. It can accept a variety of values, ranging from specific branch names to complex regular expressions and specialized trigger keywords.

Supported Values for Only

The only keyword supports an array of values that dictate the pipeline trigger source:

  • branches: This value ensures the job runs when the Git reference for a pipeline is a branch.
  • tags: This value triggers the job when the Git reference for a pipeline is a tag.
  • triggers: This value is used for pipelines created specifically by using a trigger token.
  • web: This value applies to pipelines created manually by selecting New pipeline in the GitLab UI, typically found in the project’s Build > Pipelines section.
  • api: This value is utilized for pipelines triggered via the pipelines API.
  • chat: This value triggers jobs created by using a GitLab ChatOps command.
  • external: This is used when the pipeline is triggered by CI services other than GitLab.
  • externalpullrequests: This value is specific to pipelines created when an external pull request on GitHub is created or updated.
  • merge_requests: This enables jobs to run when a merge request is created or updated, which is foundational for merge request pipelines, merged results pipelines, and merge trains.
  • pipelines: This is used for multi-project pipelines created through the API using CI_JOB_TOKEN or the trigger keyword.
  • pushes: This covers pipelines triggered by a git push event, encompassing both branches and tags.
  • schedules: This is used specifically for scheduled pipelines.

Implementation Examples of Only and Except

The only and except keywords can be used to create highly specific execution filters. For instance, a configuration might look like this:

```yaml
job1:
script: echo
only:
- main
- /^issue-.*$/
- merge_requests

job2:
script: echo
except:
- main
- /^stable-branch.*$/
- schedules
```

In this scenario, job1 will only execute if the pipeline is triggered by the main branch, a branch matching the regular expression /^issue-.*$/, or a merge request. Conversely, job2 will run for everything except the main branch, any branch matching /^stable-branch.*$/, or scheduled pipelines.

The Transition from Only to Rules

The only keyword is becoming obsolete in favor of rules. While only is still functional, it lacks the conditional complexity required for modern DevOps workflows. One of the most critical constraints is that only and rules cannot be used together in the same job. Attempting to do so will result in a GitLab error stating that a key may not be used with rules.

Migrating from Only Triggers to Rules

A common requirement is replacing the only: triggers syntax with the modern rules equivalent. The only: triggers keyword is used for pipelines created by trigger tokens. To achieve the same result using rules, developers must leverage the CI_PIPELINE_SOURCE variable.

The correct migration path involves using an if statement to check if the pipeline source is a trigger. The implementation should look like this:

yaml job_name: script: echo "Triggered pipeline" rules: - if: "$CI_PIPELINE_SOURCE == 'trigger'" when: on_success - when: never

In this configuration, the if statement evaluates whether the CI_PIPELINE_SOURCE is equal to trigger. If this evaluates to true, the when: on_success directive is applied, allowing the job to run. If the condition is false, the pipeline falls through to the next rule, which is - when: never. This ensures that the job is strictly excluded from any pipeline not initiated by a trigger token.

Deprecated Variations of Only

Certain uses of the only keyword are now explicitly deprecated. This includes only:variables and except:variables, which were previously used to control job addition based on the status of CI/CD variables. The recommended replacement for these is the rules:if syntax, which provides a more robust way to evaluate variables.

Pipeline Structure: Stages and Job Statuses

The effectiveness of the only and rules keywords is amplified when integrated into a well-structured pipeline using stages. Stages act as the backbone of the pipeline, defining the sequential order of execution.

Defining and Organizing Stages

Stages are declared at the top of the .gitlab-ci.yml file. They create logical groupings for different phases of the software delivery lifecycle:

  • Build Stage: Often contains jobs like build-app and build-docs.
  • Test Stage: Contains quality assurance jobs such as unit-tests, lint, and security-scan.
  • Deploy Stage: Contains the final deployment jobs.

The sequence is strict: all jobs in the Build stage must complete before any jobs in the Test stage can begin. However, all jobs within a single stage run in parallel by default, which can significantly reduce the total pipeline duration.

Job Statuses and Monitoring

Once a job is triggered by the conditions defined in only or rules, it enters one of several statuses:

Status Description
created The job has been created but not yet processed.
pending The job is in the queue waiting for an available runner.
preparing The runner is preparing the execution environment.
running The job is currently executing on a runner.
success The job completed successfully.
failed The job execution failed.
canceled The job was manually canceled or automatically aborted.
canceling The job is being canceled but the after_script is still running.
manual The job requires a manual action to start.
scheduled The job has been scheduled but execution has not yet started.
skipped The job was skipped due to conditions or dependencies.
waitingforcallback The job is waiting for a callback from an external service.
waitingforresource The job is waiting for resources to become available.

Advanced Configuration and Deprecations

Beyond the basic only keyword, GitLab CI includes specific configurations for Kubernetes and Pages that have also seen deprecations.

Kubernetes Integration

The only:kubernetes keyword allows a job to run only when the Kubernetes service is active in the project. For example:

yaml deploy: only: kubernetes: active

However, certain sub-configurations under the kubernetes section are now deprecated. Specifically, environment:kubernetes:namespace and environment:kubernetes:flux_resource_path are deprecated when used directly under kubernetes. Users should instead utilize the dashboard settings via environment:kubernetes:dashboard:namespace and environment:kubernetes:dashboard:flux_resource_path.

GitLab Pages and Publish Keywords

The job-level publish keyword and the specific pages job name for GitLab Pages deployment jobs are now deprecated. This is part of a broader effort by GitLab to standardize job naming and deployment triggers.

Comparison of Triggering Mechanisms

The following table summarizes the differences between the legacy only approach and the modern rules approach for common scenarios.

Scenario Legacy Syntax (only) Modern Syntax (rules)
Trigger Token only: [triggers] rules: [{ if: "$CI_PIPELINE_SOURCE == 'trigger'", when: 'on_success' }]
Main Branch only: [main] rules: [{ if: '$CI_COMMIT_BRANCH == "main"' }]
Variable Check only: variables (Deprecated) rules: [{ if: '$MY_VAR == "true"' }]
Exclude Schedules except: [schedules] rules: [{ if: '$CI_PIPELINE_SOURCE == "schedule"', when: 'never' }]

Conclusion

The transition from only to rules in GitLab CI represents a shift toward a more programmable and conditional pipeline logic. While only provided a simple way to filter jobs based on branches and tags, it lacked the granularity required for complex CI/CD patterns, such as those involving variable-based triggers or multi-source conditions. The rules keyword allows for a sequential evaluation of conditions, where the first matching rule determines the job's fate, and the when: never directive provides a definitive way to exclude jobs.

For developers maintaining older pipelines, the move toward rules is not merely a matter of syntax but a requirement for future-proofing. The inability to mix only and rules within a single job means that migration must be performed comprehensively. By leveraging the CI_PIPELINE_SOURCE variable, users can replicate every functional aspect of the only keyword—including triggers, web, and api sources—while gaining the ability to use complex logic and variable checks. This evolution ensures that GitLab pipelines remain flexible and capable of handling the demands of modern microservices and high-frequency deployment environments.

Sources

  1. GitLab Forum - Replace old syntax only trigger with rules
  2. GitLab Documentation - CI/CD Jobs
  3. GitLab Documentation - Deprecated Keywords
  4. OneUptime Blog - GitLab CI Stages

Related Posts