The orchestration of continuous integration and continuous delivery pipelines requires a sophisticated balance between total automation and human oversight. In the GitLab CI/CD ecosystem, the when: manual keyword serves as the primary mechanism for introducing human-in-the-loop gates. By default, GitLab pipelines are designed to flow automatically from one stage to the next, executing every job that meets the defined criteria. However, there are critical operational scenarios—such as deploying a build to a production environment, triggering a destructive database migration, or initiating a high-cost performance test—where an automated trigger is either too risky or logically inappropriate. The when: manual directive transforms a standard automated job into a manual action, requiring an explicit trigger from a user via the GitLab user interface before the job is dispatched to a GitLab runner.
This capability is available across all GitLab tiers, including Free, Premium, and Ultimate, and is supported across various offering models such as GitLab.com (SaaS), GitLab Self-Managed, and GitLab Dedicated. The fundamental utility of this directive is to provide a safety buffer. When a job is marked as manual, it is added to the pipeline graph but remains in a skipped state upon the pipeline's inception. It effectively pauses the specific execution path of that job, waiting for a human operator to click the "play" button. This prevents the "catastrophic success" scenario where a pipeline might automatically deploy a flawed build to production simply because the preceding test stage passed.
Mechanics of Manual Job Configuration
To implement a manual trigger, the developer must modify the .gitlab-ci.yml configuration file. The syntax requires the addition of the when: manual key to the specific job definition.
yaml
deploy_production:
stage: deploy
script:
- echo "Deploying to production server"
when: manual
When this configuration is parsed, GitLab recognizes that the job should not be automatically queued. Instead, the job is displayed in the pipeline graph as a manual action. It is important to note that manual jobs that have been archived will not run, ensuring that legacy pipeline configurations do not accidentally trigger outdated deployment scripts.
The behavior of these jobs is further influenced by where the when keyword is placed. If when: manual is defined at the top level of the job, it typically defaults to an optional status. However, if it is placed within a rules block, the interaction with the allow_failure attribute changes, which dictates whether the pipeline continues to subsequent stages or halts until the manual job is executed.
Optional versus Blocking Manual Jobs
A critical distinction in GitLab pipeline architecture is the difference between optional and blocking manual jobs. This distinction determines the overall status of the pipeline and the flow of execution.
Optional Manual Jobs
An optional manual job is one where the pipeline can be marked as "passed" even if the manual job is never triggered. This is the default behavior when when: manual is defined outside of a rules section.
- Technical implementation: In these instances,
allow_failureis set totrueby default. - Pipeline Impact: Because the job is considered optional, its "skipped" status does not negatively impact the overall pipeline health. A pipeline can successfully complete its entire lifecycle while leaving an optional manual job untouched.
- Use Case: This is ideal for non-critical tasks, such as triggering a manual documentation update or running a set of extended integration tests that are not required for a release.
Blocking Manual Jobs
A blocking manual job acts as a hard gate. The pipeline will not progress to subsequent stages until the manual job is either executed and succeeds or is manually skipped.
- Technical implementation: This occurs when
when: manualis defined inside arulesblock, whereallow_failuredefaults tofalse. - Pipeline Impact: The pipeline effectively stops at the stage where the blocking manual job is defined. No jobs in later stages will start until the blocking job is triggered.
- Use Case: This is essential for production deployments where a "Go/No-Go" decision must be made by a release manager before the pipeline can proceed to a final cleanup or notification stage.
Comparative Analysis of Job Execution Policies
The when keyword is part of a broader set of policies that dictate how GitLab runners handle job execution. While manual is used for human intervention, other values manage the logic of success and failure.
| Policy Value | Execution Condition | Typical Use Case |
|---|---|---|
on_success |
Executes only if all jobs in prior stages succeeded. (Default) | Standard build and test sequences. |
on_failure |
Executes only if at least one job in a prior stage failed. | Sending failure notifications or cleaning up resources. |
always |
Executes regardless of the status of prior stages. | Generating artifacts or logs for debugging. |
manual |
Requires a user to manually trigger the job. | Production deployments or sensitive data deletions. |
Historically, there has been a limitation where manual actions were only available if the previous stage succeeded, effectively falling back to on_success. This led to discussions within the GitLab community regarding the need to decouple the manual trigger from the success of previous stages, potentially allowing a manual job to be run even if the pipeline had previously failed.
Advanced Guardrails and Security for Manual Actions
To prevent accidental triggers—which could lead to unplanned downtime or data loss—GitLab provides several layers of confirmation and authorization.
Manual Confirmation
For highly sensitive jobs, the manual_confirmation keyword can be used in conjunction with when: manual. When this is enabled, clicking the "play" button does not immediately start the job. Instead, it prompts the user for a second confirmation. This prevents "misclicks" in the UI from triggering a production deployment.
Protected Environments
Available in the Premium and Ultimate tiers, protected environments allow organizations to restrict who can trigger manual jobs. This transforms a manual job into an "approval" gate.
To implement this, a job must be associated with an environment:
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, administrators can use the "Protected Environments" settings to define an "Allowed to Deploy" list. This list can include specific users, roles, or groups. Only these authorized entities (and GitLab administrators) can execute the manual job. This mechanism effectively blocks a pipeline until an approved user provides the necessary authorization.
Variable Management and Manual Job Retries
Manual jobs are not static; they often require external inputs to change the behavior of the script during execution.
Overriding Variables
Users can add, modify, or delete CI/CD variables through the GitLab UI before running a manual job. If a variable is specified in the UI that already exists in the .gitlab-ci.yml or the project's CI/CD settings, the UI-specified value overrides the existing one. It is important to note that these overridden variables are expanded and not masked, which means they may be visible in the job logs.
Retry Logic and Parameterization
If a manual job fails or needs to be re-run with different parameters, GitLab provides two primary paths:
- Retrying with the same variables: From the job details page, the user can simply select the "Retry" button.
- Retrying with modified values: The user can select "Retry job with modified values" from the dropdown. The variables used in the previous run are prefilled, allowing the user to tweak a specific parameter (e.g., a version number) without re-typing the entire configuration.
For more complex, validated parameters, the use of job inputs is recommended over simple variable overrides.
Temporal Control: The Delayed Job
While when: manual focuses on human triggers, when: delayed focuses on temporal triggers. This is used to execute scripts after a specific waiting period or to prevent jobs from immediately entering a "pending" state.
The delay is configured using the start_in keyword. The value is treated as seconds unless a specific unit is provided. Valid formats include:
'5'(Five seconds; must be in single quotes)30 minutes1 day1 week
The minimum delay is one second, and the maximum is one week. A critical behavioral aspect of delayed jobs is that they block the pipeline's progress; the pipeline will not move to the next stage until the delayed job has finished its execution. The timer starts the moment the previous stage concludes.
Visibility and Access Control
The ability to interact with manual jobs is governed by project visibility and user roles. Because manual jobs can involve sensitive operations, GitLab restricts who can trigger them based on the project's privacy settings.
- Public Projects: Users with the Developer, Maintainer, or Owner roles can trigger manual jobs.
- Private or Internal Projects: Users with the Guest, Planner, Reporter, Developer, Maintainer, or Owner roles have the ability to trigger these actions.
This broad access highlights why the use of Protected Environments (Premium/Ultimate) is critical for production-grade security, as it narrows the "Allowed to Deploy" list to only those with the requisite authority.
Conclusion: The Strategic Role of Manual Intervention
The implementation of when: manual is not merely a technical configuration but a strategic decision in the software delivery lifecycle. By shifting from a fully automated "Continuous Deployment" model to a "Continuous Delivery" model, organizations introduce a necessary layer of human judgment.
The distinction between optional and blocking manual jobs allows for a nuanced pipeline design. Optional jobs provide flexibility for auxiliary tasks, while blocking jobs enforce strict compliance and quality gates. When combined with protected environments and manual confirmation, the when: manual directive transforms the GitLab pipeline from a simple script executor into a sophisticated governance tool. This ensures that the speed of automation is always tempered by the necessity of human oversight, particularly in the final stages of the release process where the cost of failure is highest.