Put the deploy job in a named environment, store deployment secrets as environment secrets, and require a reviewer. GitHub says a job cannot access environment secrets until a reviewer approves. That one change turns a deploy template from automatic on every push into approved by a person each time.
What the public templates assume
GitHub's collection of starter workflows includes a deployment category next to continuous integration and others. Those files are a starting point for getting code to a platform. They cannot know your approval process, which accounts are production, or who may release.
So the first edit is not the cloud settings. It is the control around the deploy job.
Step 1: create an environment
In the repository settings, create environments such as staging and production. GitHub's documentation lets you attach protection rules, including required reviewers.
Step 2: move secrets into it
Repository secrets are readable by any workflow in the repository. GitHub's guidance suggests protecting environment secrets with required reviewers, so a job cannot read them until approval is granted. Move deploy credentials out of the repository level and into the matching environment.
Step 3: name the environment in the job
jobs:
deploy:
runs-on: ubuntu-latest
environment: production
permissions:
contents: read
steps:
- run: echo "deploy step goes here"
The job waits for approval, then receives the environment's secrets.
Step 4: prefer short lived credentials
If your cloud provider supports it, replace stored keys with OpenID Connect. GitHub describes it as a way to authenticate without storing long lived cloud credentials as secrets. The deploy job can then request a short lived token for the run. See GitHub Actions OIDC for cloud access.
Step 5: separate build from deploy
Run build and tests in one job with minimal permissions and no deploy secrets. Run deploy in a second job that depends on the first and sits behind the environment. If a test step is compromised, it never sees the deployment credentials.
What reviewers should check
| At approval time | Question |
|---|---|
| What is being deployed | Is it the commit I expect? |
| Who triggered it | Is this a normal release or something unusual? |
| Where it goes | Right environment, right account? |
| What changed in the workflow | Did the YAML change since the last approved run? |
The last question matters. Code owners on the workflows directory mean the deploy logic itself had a review, not just the app code.
A caveat on reusable workflows
GitHub's reuse documentation says that to use an environment secret in a reusable workflow, you set the environment on the job inside the reusable workflow, and the caller must still pass the secret. It also warns that if the caller does not pass an environment secret, it resolves to an empty string with no error, unless you mark the secret required. Test the path end to end.
Quick check
Does a required reviewer stop all bad deploys?
No. It adds a person at a key moment. Reviewers still need to look at what is being released, and workflow files need their own review.
Should staging also need approval?
Not necessarily. Many teams let staging deploy automatically and require approval for production.
Sources
- Secure use reference, GitHub Docs
- Reuse workflows, GitHub Docs
- OpenID Connect in GitHub Actions, GitHub Docs
- actions/starter-workflows README
Last reviewed: 19 October 2026.