GitLab CI/CD Manual Job Control and Pipeline Orchestration

The architecture of modern software delivery relies heavily on the ability to balance automation with human oversight. In the ecosystem of GitLab CI/CD, this balance is achieved through the implementation of manual jobs. A manual job is a specific configuration within a pipeline that prevents a task from executing automatically, requiring a human operator to trigger the action via the GitLab user interface. This mechanism is critical for high-stakes operations, such as deploying code to a production environment or executing destructive database migrations, where an automated "push-to-deploy" model would introduce unacceptable levels of risk.

GitLab CI/CD is designed as a continuous method of software development. Its core philosophy is to continuously build, test, deploy, and monitor iterative code changes. By integrating manual triggers into this flow, organizations can maintain the speed of continuous integration while implementing a "human-in-the-loop" safety gate. This ensures that while the code is continuously integrated and tested, the final transition to a live environment is a conscious, audited decision.

The fundamental configuration of these behaviors resides within the .gitlab-ci.yml file. This YAML file, located at the root of a project, serves as the blueprint for the entire automation process. It defines the stages of the pipeline, the specific jobs to be executed, and the logic—including manual triggers—that governs when those jobs transition from a pending state to an active execution state on a GitLab Runner.

The Structural Hierarchy of GitLab CI/CD Pipelines

To understand how manual jobs function, one must first comprehend the structural hierarchy of a GitLab pipeline. A pipeline is not a single monolithic entity but a collection of stages and jobs that operate under a specific set of rules.

Stages define the sequential order of execution. In a typical pipeline, stages are organized logically to mirror the software development lifecycle. For example, a pipeline might start with a build stage, followed by a test stage, and conclude with a deploy stage. The sequential nature of stages means that the pipeline will not move to the test stage until all jobs in the build stage have successfully completed.

Jobs are the smallest units of execution. Each job specifies a set of tasks, such as compiling code, running a linter, or executing a shell script. Jobs are executed by runners, which can be provided by GitLab.com, hosted on a self-managed instance, or configured as local runners on a private machine. Within a single stage, multiple jobs can run in parallel, allowing for efficient use of computing resources. For instance, a test stage might simultaneously run test1 and test2 to verify different aspects of the application.

The relationship between jobs and stages is critical when implementing manual controls. If a job in an early stage is marked as manual and is not triggered, the pipeline may pause, effectively halting the progression to subsequent stages. This creates a natural checkpoint for quality assurance teams to verify the build before allowing the pipeline to proceed to the deployment phase.

Implementing Manual Job Execution

The primary method for defining a manual job is through the when keyword. When a job is configured with when: manual, the GitLab Runner will not start the job automatically. Instead, the job will appear in the pipeline graph with a "play" icon, indicating that it is waiting for manual intervention.

Configuration via the when Keyword

The when: manual directive instructs the pipeline to pause the specific job until a user manually triggers it. This is commonly used in deployment pipelines to ensure that a release to production only happens after a lead engineer or release manager has reviewed the staged artifacts.

Configuration via Rules

For more complex logic, GitLab provides the rules keyword. Rules allow for conditional execution based on variables, branch names, or pipeline sources. A manual job can be defined within a rule block to ensure it only appears as a manual option under specific circumstances.

For example, a job might be configured to be automatic on a develop branch but manual on the main branch. This allows for a fully automated flow in lower environments while maintaining a manual gate for production.

Comparison of Job Execution Trigger Types

Trigger Type Execution Behavior Typical Use Case
Automatic Executes immediately upon meeting stage dependencies Unit tests, Linting, Compilation
Manual Waits for user interaction via the UI Production Deployment, Manual Smoke Tests
Scheduled Executes based on a defined time interval Nightly builds, Weekly security scans
Event-driven Executes on specific triggers (e.g., Merge Requests) Integration tests for MRs

Advanced Manual Job Protection and Confirmation

For environments where security and stability are paramount, simply marking a job as manual may not be sufficient. GitLab provides additional layers of protection to prevent accidental triggers and restrict who can execute sensitive jobs.

The manual_confirmation Keyword

The manual_confirmation keyword can be used in conjunction with when: manual. When this is enabled, the user is not simply clicking a button to start the job; they are presented with a confirmation dialogue. The user must explicitly confirm the action before the job begins. This prevents "fat-finger" errors where a user might accidentally click a deployment button for a production environment.

Protected Environments

Available in Premium and Ultimate tiers, protected environments provide a robust security framework for manual jobs. Rather than allowing any user with project access to trigger a manual job, protected environments limit this capability to a specific list of authorized users, roles, groups, or those associated with a particular environment.

To implement this, a job must be associated with an environment. For instance:

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 .gitlab-ci.yml file, the administrator navigates to the protected environments settings and specifies the "Allowed to Deploy" list. This ensures that only approved personnel can trigger the deploy_prod job, effectively blocking the pipeline until an authorized user approves the action.

Constraints and Limitations of CI/CD Components

As GitLab evolves, it has introduced CI/CD components to encapsulate recurring jobs, allowing teams to reuse pipeline logic across multiple projects. However, there is a significant limitation regarding the intersection of components and manual rules.

Currently, the use of when: manual is not supported when utilizing the include keyword to bring in CI/CD components. If a user attempts to define a component with a manual rule, such as the following:

yaml include: - component: <repo-path>/<component-name>@1.0 inputs: stage: build rules: - if: $CI_PIPELINE_SOURCE == "merge_request_event" when: manual

The pipeline will return a configuration error: include:rule when unknown value: manual.

The GitLab CI/CD YAML reference specifies that when using include for configuration from other files, only the values never and always are supported within the rules context. This means that the manual state cannot be passed or defined directly through the component's input or rule set in the way standard jobs can.

Workarounds for Component Manual Constraints

Because the when: manual directive is unsupported within the include framework, users have turned to alternative strategies:

  • Variable-based Logic: Using global variables to conditionally toggle job behavior. While this provides some flexibility, it does not allow for the native "manual" button functionality and is often viewed as an incomplete solution.
  • Direct Job Definition: Avoiding components for jobs that strictly require manual triggers, instead defining those jobs directly in the main .gitlab-ci.yml file.

Managing CI/CD Variables and Expressions

The flexibility of manual jobs is often augmented by the use of CI/CD variables and expressions. Variables are key-value pairs used to store configuration settings or sensitive data like API keys and passwords.

Variable Sources

Variables can be introduced into the pipeline through three primary methods:

  • Hard-coded: Defined directly within the .gitlab-ci.yml file.
  • Project Settings: Defined in the GitLab UI under the project's CI/CD settings, allowing for secret masking and protection.
  • Dynamic Generation: Generated during the pipeline's execution.

Expressions and Input Context

GitLab CI/CD expressions allow for the dynamic injection of data. The inputs context is particularly useful when using components, as it allows a parent file to pass specific information into a child configuration. However, as noted in the component limitations, this input context cannot currently be used to override a job to a manual state if that state is forbidden by the include logic.

Pipeline Execution and Runner Interaction

Every job, whether automatic or manual, must be executed by a runner. A runner is a lightweight agent that picks up jobs from the pipeline, executes the defined scripts, and sends the results back to GitLab.

When a job is marked as when: manual, the runner does not pick up the job immediately. The job remains in a "pending" state. Once the user triggers the job, the GitLab coordinator assigns the job to an available runner. The runner then:

  • Pulls the specified Docker image (if applicable).
  • Executes the commands listed in the script section.
  • Generates a job log containing the full execution output.
  • Saves any defined artifacts, which are files created by the job that can be used by subsequent jobs in the pipeline.

Runners can be hosted in various environments:

  • GitLab.com: Shared runners provided by GitLab.
  • Self-Managed: Runners hosted on a company's own infrastructure.
  • Local: Runners installed on a developer's personal machine for testing.

Analysis of Manual Job Impact on Pipeline Flow

The implementation of manual jobs fundamentally alters the state machine of a GitLab pipeline. In a standard automated pipeline, the flow is linear and deterministic. In a pipeline with manual jobs, the flow becomes conditional and human-dependent.

If a job in the test stage is manual and fails to be triggered, the pipeline effectively pauses. Depending on the configuration, the subsequent deploy stage may never be reached. This creates a critical safety mechanism: the "Manual Block." This block ensures that no code reaches production without a human acknowledging that the previous stages—even if they were successful—meet the qualitative standards required for a release.

Furthermore, the use of manual jobs allows for "Optional" tasks. Not every pipeline run requires every job to execute. For example, a "Performance Test" job might be too resource-intensive to run on every commit, so it is marked as manual. This allows developers to trigger the performance suite only when they believe the code is stable enough for such testing, optimizing runner resource consumption.

Summary of GitLab CI/CD Manual Configurations

The following table summarizes the key elements used to control manual execution within GitLab CI/CD.

Feature YAML Keyword Tier Effect
Manual Trigger when: manual All Stops job from starting until user clicks play
Confirmation manual_confirmation All Adds a confirmation prompt before job starts
Access Control environment Premium/Ultimate Limits manual trigger to authorized users
Conditionals rules All Defines if a manual job should appear based on context
Components include All (Limited) Cannot use when: manual within include rules

Conclusion

The capacity to implement manual controls within GitLab CI/CD is a cornerstone of professional DevOps practices. By utilizing the when: manual directive, organizations can transition from simple continuous integration to a sophisticated continuous delivery model where human judgment acts as the final arbiter of production readiness.

The integration of manual_confirmation and protected environments further elevates this process from a simple button-click to a governed release process, ensuring that only authorized personnel can trigger high-risk actions. While the current limitations surrounding CI/CD components and the include keyword present a challenge for those seeking absolute modularity, the use of direct job definitions and carefully managed variables provides a viable path forward.

Ultimately, the manual job is not a hindrance to automation but a strategic tool for risk management. It allows for the creation of a "fail-safe" architecture where the speed of the pipeline is balanced by the necessity of oversight, ensuring that the deployment of software is both rapid and reliable.

Sources

  1. Get started with GitLab CI/CD
  2. Using CI/CD components with “manual” rule
  3. CI/CD pipelines
  4. CI/CD Jobs
  5. Job Control

Related Posts