GitLab CI’s manual approval step isn’t just a "pause" button; it’s a critical fork in your pipeline’s execution, allowing human judgment to intervene before code hits production.

Let’s see it in action. Imagine a pipeline with a staging deployment that needs a human to give the go-ahead before it proceeds to production.

stages:
  - build
  - test
  - deploy_staging
  - deploy_production

build_app:
  stage: build
  script:
    - echo "Building the application..."
    - echo "BUILD_ID=$(date +%s)" >> build.env

test_app:
  stage: test
  script:
    - echo "Running tests..."
    - echo "TEST_STATUS=PASSED" >> test.env
  needs:
    - build_app

deploy_staging_job:
  stage: deploy_staging
  script:
    - echo "Deploying to staging environment..."
    - echo "STAGING_DEPLOY_SUCCESS=true" >> staging.env
  needs:
    - test_app
  artifacts:
    reports:
      dotenv: staging.env

deploy_production_manual:
  stage: deploy_production
  script:
    - echo "Deploying to production environment..."
    - echo "PRODUCTION_DEPLOY_SUCCESS=true" >> production.env
  needs:
    - deploy_staging_job
  when: manual # This is the key!

When this pipeline runs, after deploy_staging_job completes successfully, the deploy_production_manual job will appear in the pipeline view, but it won’t start automatically. Instead, it will show a play button.

Clicking that play button is the manual approval. The job then proceeds to execute its script.

The core problem this solves is the "fire and forget" nature of automated deployments. While automation is great for speed and consistency, some deployments, especially to critical environments like production, benefit from a human sanity check. This could be verifying test results, ensuring no last-minute code changes were missed, or simply confirming that the timing is right for a release.

Internally, GitLab CI uses a when keyword to control job execution. when: manual specifically tells the CI runner to halt execution at that job and wait for an explicit trigger from a user with sufficient permissions (usually Maintainer or Owner roles for the project). The pipeline graph visually represents this pause, clearly indicating where human intervention is required. You can also set allow_failure: true on preceding jobs, meaning if deploy_staging_job fails, the pipeline won’t necessarily stop, but the manual approval for production will still be gated by the success of its needs.

The magic of when: manual is that it’s not just about stopping; it’s about conditional stopping. You can combine it with other when conditions like on_success, on_failure, or always to create complex approval workflows. For instance, a manual approval might only be required if a previous test job failed, prompting a review of the failure before a rollback or hotfix is deployed.

The most surprising aspect is how granular you can get with manual approvals. It’s not just a project-level gate. You can define manual jobs within specific environments, meaning only deployments to your "production" environment might require a manual click, while "staging" or "testing" environments deploy automatically. This fine-grained control allows you to balance automation with necessary human oversight precisely where it’s most critical.

The next hurdle you’ll likely face is managing who can give that approval, leading you to explore GitLab’s protected environments and approval rules.

Want structured learning?

Take the full Gitlab-ci course →