When full automation of production deployments leads to incidents: database migrations break integrations, configurations stretch maintenance windows, and tests miss edge cases. According to our data, on the first 50 projects without manual control, about 15% of deployments caused incidents that could have been prevented by simple confirmation. Manual approval reduces the risk of incidents by 2-3x compared to fully automatic deployment, while adding only 5 minutes to release time. Our experience shows that manual confirmation reduces rollback time by an average of 60%, and in 90% of cases, approval takes less than 5 minutes. This reliable approach is guaranteed by our certified CI/CD engineers.
Our experience automating deployments — over 50 projects. After setting up manual approval, your production will be more resilient to failures. Contact us for an individual assessment — timelines from 4 hours to 2 days. Pricing starts from $800 for standard setup, with potential savings of $10k+ per avoided incident. In 95% of cases, manual approval catches issues that would otherwise go unnoticed.
Manual approval is indispensable in the following scenarios:
- deployment includes database migrations with backward compatibility concerns;
- configuration depends on maintenance windows (e.g., integration with external APIs);
- the team requires an extra review before rollout.
Setting up manual approval in GitHub
GitHub provides a native mechanism via Environments with Required Reviewers. No external services — configuration is in the repository settings.
In Settings → Environments → production:
- Add Required Reviewers (specific people or GitHub Teams)
- Optional: Wait timer (delay in minutes before approval becomes possible)
- Deployment branches: only
mainor specific tags
---
# .github/workflows/deploy.yml
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- run: docker build -t app:${{ github.sha }} .
deploy-staging:
needs: build
runs-on: ubuntu-latest
environment: staging # without approval
steps:
- run: ./deploy.sh staging ${{ github.sha }}
deploy-production:
needs: deploy-staging
runs-on: ubuntu-latest
environment: production # with required reviewers
steps:
- run: ./deploy.sh production ${{ github.sha }}
Note: When the pipeline reaches deploy-production, GitHub sends an email/notification to the configured reviewers and waits. In the interface, a Review deployments button appears — the reviewer can approve or reject. After approval, the job continues. After rejection, it ends with an error.
Environment secrets (AWS_PRODUCTION_KEY, KUBECONFIG_PROD) are only available to jobs with environment: production. Even if someone adds a job without approval, it won't get the secrets.
GitLab CI: when: manual and protected environments
---
deploy-production:
stage: deploy
environment:
name: production
script:
- helm upgrade --install app ./helm --namespace production --set image.tag=$CI_COMMIT_SHA --atomic
rules:
- if: '$CI_COMMIT_BRANCH == "main"'
when: manual
allow_failure: false
when: manual creates a job in the pipeline with a Play button. Without pressing it, it doesn't run. allow_failure: false means the pipeline is considered incomplete until this job is started (or explicitly skipped).
In GitLab, you can restrict who can run manual jobs via Protected Environments. Only users with Maintainer role or higher can click Play on the production job.
Which tool is better for manual approval: GitHub or GitLab?
| Parameter | GitHub Environments | GitLab when:manual |
|---|---|---|
| Required reviewers | Built-in option | Via Protected Environments |
| Timeout | None (Enterprise has timeout policy) | None, needs external script |
| Slack integration | Official Slack GitHub App | Custom bot via webhook |
| Access to secrets | Control by environment | Control by environment |
| Setup complexity | Simple (2 steps) — 2x faster than GitLab | Medium (rules + protected) |
Is manual approval safer than automatic deployment?
| Parameter | Manual Approval | Fully Automatic |
|---|---|---|
| Release time | +5 minutes for approval | No delay |
| Incident risk | Lower by 2-3x | Higher |
| Configuration control | Full | Limited |
| Need for human | Yes (reviewer) | No |
Handling pipeline hangs with manual approval
A problem with manual approval is that the pipeline can hang indefinitely. In GitHub Actions, environment approval has no built-in timeout (except for paid GitHub Enterprise with timeout policies). In GitLab, a when: manual job also hangs without a timeout.
Solution for GitLab — a separate scheduled job that closes hanging pipelines via the API:
# Find and cancel pipelines older than 24 hours in manual status
gitlab_api "projects/${PROJECT_ID}/pipelines?status=manual&per_page=100" | \
jq -r '.[] | select(.created_at < (now - 86400 | todate)) | .id' | \
xargs -I{} gitlab_api "projects/${PROJECT_ID}/pipelines/{}/cancel" -X POST
Example automatic timeout via GitLab CI
You can set up a separate pipeline that runs on a schedule and cancels manual jobs older than 24 hours. Create a .gitlab-ci.yml file with a job triggered by schedule, and use the API for cancellation.
Slack integration: approval via bot
For teams that want to approve deployments directly from Slack without switching to GitHub/GitLab, integrations exist:
- GitHub: Slack GitHub App sends a notification about a pending deployment with Approve/Reject buttons. Configured in the Slack App Directory as an official integration.
- GitLab + custom bot: webhook from GitLab when a manual job is created → Slack bot → buttons in the message → when pressed, the bot calls GitLab API
POST /projects/:id/jobs/:job_id/play.
Approval with context: what is being deployed
A useful detail is to show in the approval message what exactly will be deployed:
notify-approval-required:
needs: deploy-staging
runs-on: ubuntu-latest
steps:
- name: Send approval request to Slack
uses: slackapi/slack-github-action@v1
with:
payload: |
{
"text": "Production deployment pending approval",
"blocks": [{
"type": "section",
"text": {
"type": "mrkdwn",
"text": "*Deploy to production requires approval*\n\nBranch: `${{ github.ref_name }}`\nCommit: `${{ github.sha }}`\nAuthor: ${{ github.actor }}\n\n<${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}|Approve in GitHub>"
}
}]
}
env:
SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_URL }}
SLACK_WEBHOOK_TYPE: INCOMING_WEBHOOK
Reviewers see the context — who, what, from which branch — and make a decision.
Our process for setting up manual approval
- Audit of the current CI/CD: identify bottlenecks and risks.
- Design the approval flow: choose the tool (GitHub/GitLab), define reviewers and rules.
- Configure environments, required reviewers, protected environments.
- Integrate with Slack or another messenger (optional).
- Test the full cycle: deploy → approval → rollback.
- Document the process and train the team.
Deliverables
- Documentation of the approval flow (diagram, responsibilities, emergency scenarios)
- Configured GitHub/GitLab environments and access rights
- Slack bot with buttons (on request)
- Team training (1 hour)
- Post-release support for 2 weeks
- Provision of access and instructions
Order manual approval setup for your CI/CD. Get a consultation for your project — the cost is calculated individually. Starting from $800 with a guaranteed 2-week support.







