Protected branches, tags & environments
Who can change and deploy what.
Protection in GitLab starts with branches. A protected branch — typically main — restricts who can push and merge to it, so code only reaches the default branch through a reviewed merge request. That review gate is the foundation everything else builds on: if anyone can push to main, then anyone can change the pipeline, the deploy config, and what ships. Protect main, require merge requests, and require approvals.
# main:# Allowed to push: No one (force all changes through MRs)# Allowed to merge: Maintainers# Require approval from Code Owners: on# protected tags (e.g. v*): only maintainers can create release tags
Protected variables and tags
GitLab ties secrets to protection. A CI/CD variable can be marked "protected," which means it is only exposed to jobs running on protected branches and tags — so a feature branch or a fork pipeline literally cannot see your production credentials. Protected tags similarly restrict who can create release tags, so a tag that triggers a production deploy cannot be pushed by just anyone. Protection is the mechanism that keeps privileged secrets away from unprivileged pipelines.
Protected environments
An environment in GitLab (staging, production) can be protected so that only specific users or roles can run deployment jobs to it, with optional required approvals before the job proceeds. This gives you change control and an audit trail for every production deploy — who approved it, when — without bolting on a separate ticketing system. Combined with a manual rule, a production deploy becomes a deliberate, reviewed, recorded action.
deploy-prod:stage: deployenvironment:name: production # protected in Settings → CI/CD → Environmentsrules:- if: '$CI_COMMIT_BRANCH == "main"'when: manual # a maintainer clicks deploy; approval recorded