In GitLab, how do you require human approval before a pipeline job deploys to production, and what happens to that job while it waits?
answer
- not a YAML keyword
- project settings, outside the pipeline file
- allowed-to-deploy and approvals are separate
- the deployment blocks, not the runner
- protection matches the environment name
basics
~20 sYou protect the environment in the project's CI/CD settings, not in .gitlab-ci.yml: a protected environment lists who is allowed to deploy and how many approvals a deployment needs. A job targeting it is created and then blocked, waiting for approval before the runner picks it up.
solid answer
~50 sThe `.gitlab-ci.yml` side is just `environment: name: production` — the control lives in the project's **protected environments** settings, which is a Premium/Ultimate feature. There you pick the environment by name and set two separate things: **allowed to deploy**, the roles, users or groups whose jobs may run against it at all, and **approval rules**, which require a number of approvals from named approvers before a deployment proceeds. The job behaviour is the important half: the pipeline creates the deploy job as normal, and when it reaches it the deployment enters a blocked state waiting for approval, visible on the environment and on the job. Approvers approve the *deployment* — not the merge request — and only then does a runner pick the job up. A self-approval restriction and multiple independent rules (for example one release manager plus one security approver) are the usual escalations. Because it is keyed on the environment name, a badly named or renamed environment silently escapes the protection.
go deeper
Know that the pipeline file only names the environment, and that requiring sign-off before production is configured in the project's protected-environment settings instead.
Distinguish the two controls — allowed-to-deploy access versus approval rules — and describe the job being created and then blocked pending approval rather than dispatched to a runner.
Argue why the gate belongs outside the pipeline file that developers can edit, and name the sharp edge: protection keys on the environment name, so renames and new environments fail open.
Own the policy end: how many independent approvers, whether self-approval is barred, how the gate interacts with environment-scoped credentials, and how you keep it applied as environments proliferate.
## Where the control actually lives A candidate who reaches for a YAML keyword here has the wrong model. In GitLab the pipeline file *names* the environment and nothing more: ```yaml deploy_prod: stage: deploy script: ./deploy.sh production environment: name: production url: https://app.example.com ``` Authority over that environment is configured in the project's **Settings → CI/CD → Protected environments**, out of reach of anyone who can only edit `.gitlab-ci.yml`. That separation is the design: if the gate lived in the pipeline file, whoever can open a merge request against the pipeline file could remove it. Protected environments and deployment approvals are paid-tier features (Premium and above). ## The two controls **Allowed to deploy** is an access list: roles (maintainers, developers), specific users, or groups whose jobs may run against the environment. A deployment job triggered by anyone outside it fails rather than waiting — this is authorisation, not queueing. **Approval rules** are separate. They require a number of approvals before the deployment proceeds, drawn from named approvers or groups. Multiple independent rules can be configured — a release manager *and* a security approver, each with their own required count — so no single person can satisfy the whole gate. A restriction preventing the person who triggered the deployment from also approving it is the usual complement; without it the control degrades into a formality. Note the granularity: approval is on the **deployment**, not on the merge request. The same commit deploying to staging and to production produces two deployments, and only the production one is gated. This is what makes protected environments the right place for the gate rather than merge request approval rules, which are about code review. ## What the job does while waiting The pipeline runs normally until it reaches the deploy job. The job is created, and the deployment enters a blocked state pending approval rather than being dispatched to a runner. Nothing is executing and no runner slot is held. The environment page and the job page show the pending deployment and who may act on it, and approvers approve or reject there. On approval the job proceeds to a runner and runs; on rejection it does not run. The operational consequences follow from that: the pipeline is not finished while a deployment waits, an unattended approval keeps a release open indefinitely, and any downstream job that depends on the deploy is waiting too. Teams that gate production usually keep the deploy job in a late, narrow stage so a pending approval blocks as little as possible. ## Interaction with the rest of the environment machinery Protection is matched on the environment **name**, and that is where the sharp edge is. Rename `production` to `prod` in the pipeline file and the job targets an unprotected environment — the gate does not fail closed, it simply does not apply, and the deploy runs. The same applies to a *new* environment nobody protected, which is why wildcard-style protection of production-shaped names and periodic review of the environment list matter. Environment-scoped CI/CD variables key on the same names, so production credentials that resolve only for the protected environment reinforce the gate: a job deploying to an unprotected environment does not get the values it would need to touch production even if it tried. ## Choosing this over the alternatives GitLab offers a second, cruder gate: a manual job, which anyone permitted to run jobs can start. A manual job is a *pause*, not an approval — it records who clicked, allows self-service, and is trivially removed in a merge request that edits the pipeline file. Use it for convenience ("deploy when you are ready"), and use protected environments when the requirement is that a specific set of people, distinct from the author, authorised this specific deployment, with an audit trail attached to the deployment record. ## Version note This area has moved: deployment approvals gained multiple approval rules and self-approval restrictions over several GitLab 14.x–15.x releases, and older documentation describes a single required-approval count. Say which behaviour you are describing, and confirm against the version the team runs. ## What to say in an interview Lead with the split — pipeline names the environment, project settings own who may deploy and who must approve — then describe the blocked deployment state, then name the failure mode: protection keys on the name, so a renamed or newly created environment is unprotected by default.
- Why is a manual job a weaker gate than a protected environment in GitLab?A manual job only pauses the pipeline; anyone able to run jobs on that branch can start it, and it can be removed in the same merge request that changes the code. Protected-environment rules live in project settings, name specific approvers, can require several independent approvals, and attach the record to the deployment.
- A team renamed their production environment in `.gitlab-ci.yml` and approvals stopped applying. Why?Protected-environment rules are matched on the environment name. The renamed environment is a different, unprotected environment, so the gate does not apply — and it fails open rather than closed. Renames and newly introduced environments both need the protection re-applied deliberately.
- Why does GitLab put the approval on the deployment rather than on the merge request?Because the question being answered is "may this commit go live *here*", not "is this code good". The same commit may deploy to staging freely and to production only with sign-off, and a redeploy or rollback months later needs its own authorisation even though the merge request was approved long ago.
saying these in an interview costs you the question
- Looks for an approval keyword in .gitlab-ci.yml
- Treats a manual job as equivalent to an approval
- Thinks merge request approval gates the deployment
- Assumes a renamed environment keeps its protection
- Lets the person who triggered the deploy approve it