Gitlab Merge Request Approvals: Enforce Code Review Before Deployment

You've probably been there. A critical bug ships to production at 3am. The on-call engineer traces it back to a merge request that went straight to main—no review, no second pair of eyes, just one developer hitting "Merge" and hoping for the best.
That's the problem with optional code reviews. They work great until they don't, and by then, your customers are already complaining. Gitlab's merge request approval system turns code review from a suggestion into a requirement, blocking deployment until the right people sign off.
Why Merge Request Approvals Matter
Code review isn't just about catching bugs (though that's huge). It's about knowledge sharing, maintaining standards, and preventing single points of failure. When you enforce approvals, you're forcing your team to actually look at each other's code before it hits production.
What approvals prevent:
- Unauthorized changes to critical files
- Code that doesn't meet team standards
- Knowledge silos where only one person understands key systems
- Deployments that skip security reviews
Infrastructure cost: ~$20-30/month on Elestio (2 CPU / 4GB RAM) - far less than the cost of production incidents from unreviewed code.
Setting Up Required Approvals
Gitlab's approval rules live at the project level. Here's how to enforce them:
Navigate to Settings:
Project → Settings → Merge Requests → Approval Rules
Add a new rule:
Rule name: "Code Review Required"
Approvals required: 2
Eligible approvers: All developers with Maintainer role
Enable critical protections:
- ✅ Prevent approval by author (no self-merging)
- ✅ Prevent approvals by users who add commits (forces fresh review after changes)
- ✅ Require approval from code owners (for specific files/directories)
For production branches specifically:
Protected Branches → main
→ Allowed to merge: No one
→ Code owner approval required: Yes
This setup ensures at least two people review every change, and the author can't approve their own work.
Code Owners for Path-Based Reviews
Here's where it gets powerful. You can require approval from specific people based on which files changed.
Create CODEOWNERS file in repo root:
# Security team must approve auth changes
/src/auth/** @security-team
# Database team owns schema migrations
/db/migrations/** @database-team
# DevOps reviews infrastructure changes
/.gitlab-ci.yml @devops-team
/docker/** @devops-team
# Lead architect approves core changes
/src/core/** @lead-architect
Now when someone modifies /src/auth/login.js, the security team automatically gets notified and their approval becomes required. No more auth changes slipping through without security review.
Practical example:
A developer submits a merge request adding a new API endpoint. The code touches /src/auth/middleware.js, so Gitlab automatically requires approval from @security-team before allowing the merge.
Advanced Approval Rules
You can layer multiple approval requirements for different scenarios:
Security-sensitive rule:
Rule: "Security Review"
Files: **/*.env, **/secrets.*, /src/auth/**
Approvers: @security-team
Required approvals: 1
Production deployment rule:
Rule: "Production Release"
Target branches: main, production
Approvers: @tech-leads
Required approvals: 2
Documentation rule:
Rule: "Documentation Updated"
Files: docs/**, README.md
Approvers: @docs-team
Required approvals: 1
Applies to all MRs: No (only when these files change)
Override approvals for hotfixes:
Rule: "Emergency Hotfix"
Source branches: hotfix/*
Required approvals: 1 (reduced from normal 2)
These rules stack. If a merge request modifies both auth code and production config, it needs approvals from both security AND tech leads.
Approval Workflows in Practice
Let's walk through a real deployment flow with enforced approvals:
Developer creates merge request:
git checkout -b feature/user-export
# ... make changes to /src/api/users.js
git push origin feature/user-export
Gitlab checks approval rules:
- Files changed:
/src/api/users.js,/docs/api.md - Required approvals: 2 general + 1 from
@api-team(code owner) - Status: ❌ Merge blocked until approvals received
First reviewer approves:
- @jane-developer reviews code, requests changes
- Developer fixes issues, pushes new commits
- Gitlab resets approval (because code changed)
- @jane-developer reviews again, approves
- Status: 1/2 general approvals, still needs
@api-team
API team lead reviews:
- @api-lead checks API design, approves
- Status: 2/2 general approvals + code owner approval
- Merge button: ✅ Now enabled
Post-merge:
- CI/CD pipeline runs automatically
- Code ships to production with confidence
Common Pitfalls
Problem: Too many required approvals slow down development
Solution: Use different rules for different branch types. Require 2+ approvals for main, but only 1 for feature branches merging to develop.
Problem: Approvers are bottlenecks (on vacation, too busy)
Solution: Approve by role, not individual. Set eligible approvers as @senior-developers (group) instead of @specific-person.
Problem: Developers gaming the system with trivial approvals
Solution: Enable "Prevent approval by users who add commits" so reviewers can't just push a tiny change and self-approve.
Problem: Urgent hotfixes blocked by approval rules
Solution: Create a separate approval rule for hotfix/* branches with reduced requirements (1 approval instead of 2).
Integrating with CI/CD Pipelines
Approval rules work seamlessly with Gitlab CI/CD. Here's a pipeline that runs additional checks after approval:
# .gitlab-ci.yml
stages:
- test
- security-scan
- deploy
unit-tests:
stage: test
script:
- npm test
security-scan:
stage: security-scan
script:
- npm audit --audit-level=high
only:
- merge_requests
when: on_success
deploy-production:
stage: deploy
script:
- ./deploy.sh production
only:
- main
when: manual # Requires manual trigger after merge
needs:
- job: unit-tests
- job: security-scan
With this setup:
- Developer creates MR → automated tests run
- Reviewers approve → merge allowed
- After merge to main → manual deployment requires additional click
You can even block merging until CI passes:
Settings → Merge Requests → Merge checks
→ Pipelines must succeed: ✅ Enabled
Audit Trail and Compliance
One underrated benefit: approvals create an audit trail. For compliance-heavy industries (finance, healthcare), you can prove who reviewed what and when.
View approval history:
Merge Request → Approvals tab
→ See all approvers, timestamps, and reset events
Export approval data via API:
curl --header "PRIVATE-TOKEN: $TOKEN" \
"https://gitlab.example.com/api/v4/projects/1/merge_requests/5/approvals"
This audit trail is invaluable for security incidents ("Who approved the commit that introduced the vulnerability?") and compliance audits ("Can you prove all production changes were reviewed?").
Deploying Gitlab on Elestio
Ready to enforce code review in your team? Deploy Gitlab on Elestio's managed infrastructure:
Quick setup:
- Select Gitlab from Elestio marketplace
- Choose provider (Hetzner, DigitalOcean, etc.) - 2 CPU / 4GB RAM minimum
- Click "Deploy Service"
Once deployed, configure approval rules immediately:
- Navigate to your project's Settings → Merge Requests
- Add approval rules before inviting your team
- Set protected branches for
mainandproduction
For custom domain setup with automated SSL, follow the official Elestio documentation.
Summary
Gitlab's merge request approvals transform code review from optional to mandatory. By requiring sign-off from the right people before deployment, you catch bugs earlier, share knowledge across the team, and maintain code quality without slowing down too much.
Start simple:
- Require 1-2 approvals for all merge requests
- Add code owners for critical paths (auth, database, infrastructure)
- Prevent self-approval
- Adjust rules based on branch importance
You'll ship fewer bugs, onboard new developers faster, and sleep better knowing production changes have been reviewed by someone other than the person who wrote them.
Ready to deploy Gitlab with enforced code review? Deploy Gitlab on Elestio and start protecting your production deployments today.
Thanks for reading ❤️
Published by Michael Soto, Senior Content Strategist @ Elest.io