An environment with no rule is just a label. It is the protection rules that turn it into a boundary: requiring a human approval, imposing a delay before execution, restricting the branches allowed to deploy, or delegating the decision to a third-party system. This page details all four, with their stated limits and the exact behaviour of secrets during the wait.
What you will learn
- Require an approval and know who can grant it, and who cannot
- Use the wait timer as a cancellation window, without paying for the minutes
- Restrict the branches and tags allowed to deploy to a target
- Wire in a custom rule driven by an observability or ITSM tool
- Understand why secrets do not leak while an approval is pending
The four available rules
| Rule | What it controls | Limit |
|---|---|---|
| Required reviewers | Who authorises the deployment | Up to 6 people or teams |
| Wait timer | How long to wait before starting | 1 to 43,200 minutes (30 days) |
| Deployment branches and tags | Where the deployment may start from | 3 possible policies |
| Custom deployment protection rules | A decision delegated to a third party | 6 active rules at most |
They stack. A serious production environment typically combines three of them: a human approval, a restriction to protected branches, and sometimes a custom rule checking that no incident is ongoing.
Required approval: the main guardrail
The Required reviewers rule pauses the job and waits for an authorised person to validate it. You designate up to six users or teams, and a single approval is enough to unblock the job. So it is not a quorum: it is a list of authorised people, any one of whom can open the door.
One detail matters a great deal in practice: when self-approval is disabled in the settings, the person who triggered the deployment cannot approve it themselves. That is what separates a real four-eyes control from a tick box.
What happens during the wait
This is the most important point on this page, and the most often ignored:
A job cannot access the environment secrets until one of the required reviewers has approved it.
In other words, the wait is not a cosmetic pause in front of an already armed job. Until the approval is granted, the runner has never seen the production token. A compromised workflow reaching the deployment step would obtain nothing more than an approval request visible to the whole team.
jobs: deploy-production: needs: deploy-staging runs-on: ubuntu-24.04 environment: name: production # an environment with required reviewers url: https://my-app.example.com steps: - name: Deploy to production run: ./deploy.sh env: # This secret is only delivered after the human approval DEPLOY_TOKEN: ${{ secrets.DEPLOY_TOKEN }}Wait timer: the cancellation window
The wait timer delays the start of the job by 1 to 43,200 minutes, that is up to 30 days. Its virtue is not slowing things down but opening a cancellation window: if a problem appears just after the merge, you have time to stop the run before it touches production.
One billing point worth knowing, because it answers the most frequent objection: the waiting time is not billed. You do not pay runner minutes while the timer runs, since the job has not started yet.
In practice, five to fifteen minutes is enough for a useful window. Beyond that, you no longer get a safety measure but a queue, and the team will get used to working around the mechanism.
Allowed branches and tags
The third rule answers a simple question: where are you allowed to deploy to this target from? Three policies are offered:
| Policy | Effect | Typical usage |
|---|---|---|
| No restriction | Any branch or tag can deploy | A disposable test environment |
| Protected branches only | Only branches covered by a protection rule | Simple production |
| Selected branches and tags | Explicit name patterns (main, release/*, v*) | Production with release branches |
Without this rule, any feature branch can, through a manually triggered workflow,
target the production environment and consume its secrets. The restriction
closes that door before execution: the job fails immediately, without ever
obtaining the secrets.
Custom rules: delegating the decision
The fourth rule, custom deployment protection rules, delegates the authorisation to a third-party service through a GitHub App. The deployment stays pending until that service answers. GitHub lists Datadog, Honeycomb and ServiceNow among the available integrations, and an environment accepts six active rules at most.
The point is not to add a click, but to automate a criterion humans evaluate badly or too late:
- observability refuses the deployment when the error rate of the running version exceeds a threshold, or when an error budget is already spent;
- ITSM refuses the deployment outside an approved change window, or while a major incident is open.
That is the maturity level above human approval: the question "are we allowed to deploy right now?" stops depending on one person's alertness on a Friday evening.
A realistic progression
Applying all four rules at once to an existing repository mostly produces workarounds. The order that lasts goes from the least costly to the most demanding:
| Step | Rule applied | What it costs the team |
|---|---|---|
| 1 | Allowed branches on production | Nothing, nobody deployed from elsewhere |
| 2 | Secrets moved to environment level | One migration, once |
| 3 | Required approval on production | One click per production release |
| 4 | A 10-minute wait timer | Ten minutes, recoverable as a cancellation |
| 5 | A custom rule tied to incidents | An integration to maintain |
The first two steps are not negotiable: they are free in friction and close the two most used paths. The following ones are discussed with the team, because they are paid in time.
Key points
- Four rules protect an environment: approval, wait timer, allowed branches and custom rules.
- The approval accepts up to six reviewers or teams, and a single approval unblocks the job; the initiator cannot self-approve when the option is disabled.
- Environment secrets are not delivered before the approval: the wait is a real barrier, not a pause in front of an already armed job.
- The wait timer runs from 1 to 43,200 minutes and is not billed.
- The branch policy stops a feature branch from targeting production, but it does not replace protecting the branch itself.
- Custom rules (six at most) delegate the decision to observability or ITSM, and automate the "can we deploy right now?" question.
Next steps
- Releases and packages: publishing what these rules let through, as a tagged release and a consumable image.
- Rollback and concurrency: going back, the only mechanism left when an approval let a regression through.
- Lab: promoting an approved deployment: putting it into practice on a real repository, from the attested image to the production approval.