The implementation of manual triggers within a GitLab CI/CD pipeline represents a critical pivot point between fully automated continuous integration and controlled, human-governed delivery. While the core philosophy of CI is to automate every stage of the software development lifecycle, the practical reality of enterprise software deployment necessitates "gates." These gates ensure that high-risk actions—such as deploying to a production environment, executing destructive database migrations, or initiating costly integration tests—do not occur without explicit human authorization. By utilizing the when: manual keyword, developers can transform a standard automated job into a manual action, effectively creating a pause in the pipeline that requires a user to intervene via the GitLab user interface or the API.
The utility of manual jobs extends beyond simple deployment gates. They are indispensable for Proof of Concept (PoC) workflows where a developer may need to test a specific build configuration on a separate branch without pushing those changes to the official organization's primary artifacts. By utilizing manual triggers combined with custom parameter values, a developer can ensure that the CI environment can handle the codebase while directing the resulting Docker images to a personal organization or a sandbox registry. This prevents the pollution of the main organizational registry and ensures that the application remains stable while experimental configurations are validated. Furthermore, manual jobs allow for granular control over which specific set of servers receives a push from a specific branch, providing a surgical approach to deployment that fully automated pipelines cannot always replicate.
Fundamental Implementation of Manual Jobs
To designate a job as a manual trigger, the .gitlab-ci.yml configuration file must explicitly include the when: manual directive. This instruction informs the GitLab pipeline coordinator that the job should not execute automatically upon reaching its stage; instead, it must wait for a user to manually trigger the "play" button in the pipeline view.
The behavior of these jobs varies based on where the directive is placed and the accompanying settings:
- Default behavior: By default, manual jobs are displayed as skipped when the pipeline first starts.
- Archival state: Manual jobs that have been archived will not run.
- Manual confirmation: To prevent catastrophic accidents, such as accidental deletions or unauthorized production deployments, the
manual_confirmationattribute can be added alongsidewhen: manual. This forces the user to confirm the action in a secondary prompt before the job actually executes.
Optional versus Blocking Manual Jobs
A critical distinction in GitLab CI/CD is whether a manual job is "optional" or "blocking." This distinction determines whether the overall pipeline status is halted until the job is executed or if the pipeline can proceed to a "success" state regardless of the manual job's status.
| Job Type | allow_failure Setting |
Placement of when: manual |
Pipeline Impact |
|---|---|---|---|
| Optional | true (Default) |
Outside of rules |
Does not contribute to overall pipeline status; pipeline can succeed while job remains unrun. |
| Blocking | false (Default) |
Inside of rules |
The pipeline stops at the stage where the job is defined until the job is triggered. |
The impact of a blocking job is significant for deployment pipelines. If a production deployment job is marked as blocking, the pipeline will remain in a "running" or "pending" state until a designated user triggers the deployment. This ensures that no subsequent stages can occur until the manual gate is cleared. Conversely, optional jobs are ideal for "on-demand" tasks, such as running a heavy suite of legacy integration tests that are not required for every single commit but are useful for occasional verification.
Advanced Triggering via Rules and Workflow
The integration of when: manual within the rules block allows for highly conditional pipeline behavior. GitLab evaluates these rules before a pipeline starts to determine which jobs are eligible for execution.
One common implementation involves combining file-change detection with manual triggers. For example, a job might be configured to run automatically if a specific file (e.g., data/metainfo.xml) is changed, but remain available as a manual trigger if no changes are detected. This is achieved by defining multiple rules:
yaml
metainfo:
stage: test
image: "some-image:latest"
script:
- validate-metainfo data/metainfo.xml
needs: ["build@x86_64"]
rules:
- changes:
- data/metainfo.xml
when: always
- when: manual
allow_failure: true
In the above configuration, the job triggers automatically if the XML file is modified. If the file is not modified, the job falls through to the when: manual rule. This allows developers to trigger the validation manually if they suspect the binary included in the image has changed, even if the source file has not.
However, a known complexity arises with branch pipelines. In certain versions of GitLab (such as Community Edition v16.9.1), jobs defined with these rules may still run automatically upon the creation of a branch, regardless of whether the file changed. This is because the pipeline treats the branch creation event as a trigger that overrides the specific changes logic, potentially leading to the job running every time a branch is created.
Variable Management and Manual Overrides
A powerful feature of manual jobs is the ability to inject or modify CI/CD variables at the moment of execution. This transforms a static pipeline into a dynamic tool capable of handling different environments or parameter sets.
When a user triggers a manual job, they can access a panel to tweak settings and add, modify, or delete variables. This process follows specific logic regarding variable precedence and visibility:
- Overriding: If a variable is added during the manual trigger that already exists in the
.gitlab-ci.ymlfile or the project's CI/CD settings, the new value overrides the existing one. - Masking: Variables overridden during the manual trigger process are expanded and are not masked, which may be a security concern for sensitive data.
- Visibility: The ability to manage these variables depends on the project's visibility and the user's role.
- In public projects: Users with Developer, Maintainer, or Owner roles can perform these actions.
- In private or internal projects: Users with Guest, Planner, Reporter, Developer, Maintainer, or Owner roles have this capability.
To retry a manual job with updated parameters, GitLab provides a specific workflow:
- For identical variables: Use the Retry button from the job details page.
- For modified variables: Select "Retry job with modified values" from the dropdown menu. The previous variables are prefilled in the form for convenience.
- For typed/validated parameters: Job inputs should be utilized instead of simple variables.
Protected Environments and Access Control
For high-stakes manual jobs, such as those deploying to production, GitLab (Premium and Ultimate tiers) provides "Protected Environments." This allows organizations to restrict who can trigger a manual job based on their role or membership in a specific group.
To protect a manual job, the job must be associated with an environment. For example:
yaml
deploy_prod:
stage: deploy
script:
- echo "Deploy to production server"
environment:
name: production
url: https://example.com
when: manual
rules:
- if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH
Once the environment is defined in the YAML, the administrator must go to the protected environments settings and add the authorized users, roles, or groups to the "Allowed to Deploy" list. This creates a robust security layer where the pipeline is blocked until an approved user "approves" the manual job.
Addressing the "Manual Trigger" Gap and API Integration
There is a conceptual gap between GitLab's when: manual and the "manual trigger" behavior found in tools like Jenkins or Bamboo. In GitLab, a manual job is part of a pipeline that has already been created; it is a "confirmation" step. A true "manual trigger" (where the pipeline itself is started by a user with specific parameters) is more akin to the web trigger or the use of workflow:rules.
For those seeking the functionality of a true manual trigger with custom parameter input—something that was historically a pain point across pricing tiers—the recommended expert workaround involves the GitLab API. Because GitLab's native UI for manual variables can be limiting, organizations can build external forms that interact with the GitLab API to trigger pipelines with specific variable payloads.
This eliminates the need to migrate to Jenkins, which often introduces significant overhead. Integrating Jenkins with GitLab requires manual synchronization; for instance, if a project migrates from Java to Golang, the Jenkins build jobs must be manually updated to switch from Maven to Go. By staying within the GitLab ecosystem and using the API for advanced manual triggers, teams avoid the "grumble" associated with maintaining two separate, disconnected CI/CD systems.
Conclusion: Strategic Analysis of Manual Control
The implementation of when: manual is not merely a technical configuration but a strategic decision regarding the balance between velocity and safety. The transition from an optional manual job to a blocking manual job changes the entire pipeline's state machine, moving from a "continuous delivery" model to a "continuous deployment with manual approval" model.
The most effective architectures utilize a tiered approach: using rules to automate the majority of the pipeline, employing allow_failure: true for non-critical manual diagnostics, and utilizing protected environments with manual_confirmation for production releases. The ability to override variables at runtime provides the necessary flexibility for PoCs and targeted server deployments, while the API provides an escape hatch for organizations requiring complex input forms that exceed the native capabilities of the GitLab UI. Ultimately, the ability to precisely control when and how a job runs allows a team to maintain the speed of automation while retaining the oversight necessary for enterprise-grade stability.