GitLab 21 🦊 Managing Project Permissions, Roles, and Access Control
The previous chapters covered the CI/CD pipeline and its security scanners. This chapter covers the permission model that governs who can do what within a project. GitLab’s access control is built on roles: Guest, Reporter, Developer, Maintainer, and Owner. Each role is a bundle of permissions. The exam tests the distinction between the roles, the visibility levels (Private, Internal, Public), and the mechanisms that override the role-based model: protected branches and deploy tokens. The critical trap is that a role is not a single permission; it is a hierarchy. A Maintainer can do everything a Developer can do, plus more. A Developer can do everything a Reporter can do, plus more. The role assignment is the primary access control, and the protected branch is the secondary control that restricts even the role’s default permissions.
Key point: The five project roles are Guest, Reporter, Developer, Maintainer, and Owner. The role determines the baseline permissions. The protected branch settings override the role for specific branches: a Developer cannot push to main if the protected branch allows only Maintainers. The visibility level (Private, Internal, Public) determines who can see the project at all. The deploy token is a credential for automated access that does not require a user account.
Why project permissions require a specific mental model
The role hierarchy problem. The roles are cumulative. A Maintainer has all the permissions of a Developer, plus the ability to manage project settings, protected branches, and project members. A Developer has all the permissions of a Reporter, plus the ability to push code and create branches. A Reporter has all the permissions of a Guest, plus the ability to view the repository and clone it. The hierarchy is the foundation of the model, and the exam tests the specific permissions that distinguish one role from the next .
The protected branch problem. A Developer can push to the repository by default. But the main branch is protected by default on new projects. The protected branch setting restricts the push permission to a specific role or a specific group. A Developer cannot push to main if the protected branch allows only Maintainers. The role is the baseline; the protected branch is the override .
The visibility problem. The visibility level determines who can see the project. A Private project is visible only to its members. An Internal project is visible to any authenticated user. A Public project is visible to anyone, including unauthenticated users. The visibility does not change the role-based permissions; it changes the audience. A Guest in a Private project has the same permissions as a Guest in a Public project, but the Public project has more people who can see it .
The token problem. A deploy token is a credential that allows automated systems to clone the repository or pull the container image without a user account. The token has a scope: read_repository or read_registry. It does not have a role. It does not have the ability to push to a protected branch unless the branch settings explicitly allow it. The deploy token is the mechanism for CI/CD systems that need to pull code but should not have a user’s full permissions .
The trade-off. The role-based model is simple to understand and manage. The protected branch is the override that adds precision. The deploy token is the mechanism for automation. The exam tests the role permissions and the protected branch override. The most common mistake is assuming that a Developer can push to main because a Developer can push to a feature branch. The protected branch is the difference.
a. The five project roles and their permissions
The five roles form a hierarchy. Each role inherits the permissions of the role below it.
| Role | Key Permissions |
|---|---|
| Guest | View issues, comment on issues, view wiki |
| Reporter | View repository, clone, view merge requests, view CI/CD |
| Developer | Push to feature branches, create merge requests, run pipelines |
| Maintainer | Manage project settings, protected branches, project members |
| Owner | Delete project, transfer project, manage all settings |
The Guest role is the most restricted. A Guest can view issues and leave comments, but cannot view the repository code. The Reporter role adds the ability to view the repository, clone it, and view merge requests. The Developer role adds the ability to push code, create branches, and create merge requests. The Maintainer role adds the ability to manage the project settings, including the protected branches and the project members. The Owner role is the highest; it can delete or transfer the project .
The role assignment is per project, not per group. A user can be a Maintainer on one project and a Guest on another. The group membership grants the role on all projects in the group, but the project-level assignment can override the group-level role. The highest role wins.
b. Visibility levels and access control
The visibility level determines who can see the project. The three levels are Private, Internal, and Public.
| Level | Visible To |
|---|---|
| Private | Only project members |
| Internal | Any authenticated user |
| Public | Anyone, including unauthenticated users |
A Private project is the default for new projects. The project members can see the project, and no one else. An Internal project is visible to any user who has an account on the GitLab instance. A Public project is visible to the internet .
The visibility is not the same as the permission. A Public project is visible to everyone, but the role still determines what each person can do. A non-member of a Public project can view the code and clone it, but cannot push. A Guest of a Private project can view the issues, but cannot view the code .
The visibility of a project must be at least as restrictive as its parent group. A Private group cannot contain a Public project. The constraint is enforced by the system.
c. Protected branches and deploy tokens
The protected branch is the mechanism that restricts the push and merge permissions for specific branches. The main branch is protected by default on new projects. The settings allow you to specify the roles or users who can push and merge.
# Protected branch settings (conceptual)
Branch: main
Allowed to merge: Maintainers
Allowed to push: Maintainers
Allowed to force push: Disabled
Require code owner approval: Disabled
The Allowed to merge setting determines who can merge a merge request into the protected branch. The Allowed to push setting determines who can push directly to the branch. The Allowed to force push setting determines whether force pushes are permitted. The Require code owner approval setting requires an approval from a code owner before the merge .
The role selection is cumulative. Selecting “Maintainers” grants access to Maintainers and Owners. Selecting “Developers + Maintainers” grants access to Developers, Maintainers, and Owners. The higher roles always have the permissions of the lower roles .
The deploy token is the credential for automated access. It is created in the project settings under Settings → Repository → Deploy tokens. The token has a name, a username, and a scope: read_repository or read_registry. The token can clone the repository or pull the image, but cannot push unless the protected branch settings explicitly allow the deploy key .
Complete Example Session
# ============================================
# PART 1: VIEW PROJECT MEMBERS
# ============================================
# Settings → Members
# Shows the list of members and their roles.
# ============================================
# PART 2: ADD A MEMBER
# ============================================
# Settings → Members → Invite members
# Username: alice
# Role: Developer
# Invite
# ============================================
# PART 3: CHANGE A ROLE
# ============================================
# Settings → Members → alice → Edit
# Role: Maintainer
# Save
# ============================================
# PART 4: CHANGE VISIBILITY
# ============================================
# Settings → General → Visibility
# Project visibility: Private
# Save
# ============================================
# PART 5: CONFIGURE PROTECTED BRANCH
# ============================================
# Settings → Repository → Protected branches
# Branch: main
# Allowed to merge: Maintainers
# Allowed to push: Maintainers
# Save
# ============================================
# PART 6: VERIFY THE PUSH RESTRICTION
# ============================================
# As a Developer, attempt to push to main:
git checkout main
git commit --allow-empty -m "Test"
git push origin main
# Error: You are not allowed to push code to protected branches.
# ============================================
# PART 7: CREATE A DEPLOY TOKEN
# ============================================
# Settings → Repository → Deploy tokens → Add token
# Name: ci-read
# Scopes: read_repository
# Create deploy token
# Copy the username and token.
# ============================================
# PART 8: USE THE DEPLOY TOKEN
# ============================================
git clone https://<deploy-user>:<token>@gitlab.com/group/project.git
# The clone succeeds.
# ============================================
# PART 9: VERIFY THE DEPLOY TOKEN CANNOT PUSH
# ============================================
cd project
git commit --allow-empty -m "Test"
git push origin main
# Error: The deploy token does not have write access.
The nine parts trace the permission model: view members, add member, change role, change visibility, configure protected branch, verify push restriction, create deploy token, use deploy token, and verify token restriction.
Quick Reference
Project Roles
| Role | Key Permissions |
|---|---|
| Guest | View issues, comment, view wiki |
| Reporter | View code, clone, view MRs |
| Developer | Push, create branches, create MRs |
| Maintainer | Manage settings, protected branches, members |
| Owner | Delete project, transfer project |
Visibility Levels
| Level | Visible To |
|---|---|
| Private | Members only |
| Internal | Authenticated users |
| Public | Anyone |
Protected Branch Settings
| Setting | Options |
|---|---|
| Allowed to merge | No one, Maintainers, Developers |
| Allowed to push | No one, Maintainers, Developers |
| Allowed to force push | Enabled/Disabled |
| Require code owner approval | Enabled/Disabled |
Token Types
| Token | Scope | Use Case |
|---|---|---|
| Deploy token | read_repository, read_registry | CI/CD clone, pull |
| Project access token | api, read_repository, write_repository | Automation with API |
| Personal access token | User-level scopes | User scripts |
Best Practices
✅ Do This:
# Use the lowest role that works
<!-- Principle of least privilege --> # ✅
# Protect main with Maintainers only
<!-- Developers use merge requests --> # ✅
# Use deploy tokens for CI/CD
<!-- Avoid sharing user credentials --> # ✅
❌ Don’t Do This:
# Grant Owner to everyone
<!-- Destructive actions become possible --> # ❌
# Assume a Developer can push to main
<!-- Protected branch overrides the role --> # ❌
# Use a personal access token for CI/CD
<!-- Deploy tokens are scoped and revocable --> # ❌
Common Pitfalls
| Pitfall | Why It Happens | Fix |
|---|---|---|
| Developer cannot push to main | Protected branch restricts push | Use merge request |
| Cannot see project | Visibility is Private | Request access or change visibility |
| Deploy token cannot push | No write_repository scope | Use project access token |
| Cannot delete project | Role is Maintainer, not Owner | Transfer ownership |
| Group member has no access | Project-level role overrides group | Check project members |
Real-World Examples
1. Add Member
Settings → Members → Invite members → Role: Developer
2. Change Role
Settings → Members → Edit → Role: Maintainer
3. Change Visibility
Settings → General → Visibility → Private
4. Protect Branch
Settings → Repository → Protected branches → main
5. Restrict Push
Allowed to push: Maintainers
6. Create Deploy Token
Settings → Repository → Deploy tokens → Add token
7. Clone with Deploy Token
git clone https://<user>:<token>@gitlab.com/group/project.git
8. Create Project Access Token
Settings → Access tokens → Add token
9. Verify Role
Settings → Members → View role
10. Remove Member
Settings → Members → Remove
Visual
Role Hierarchy
┌──────────────────────────────────────────────┐
│ Owner │
│ ├── Delete project │
│ ├── Transfer project │
│ └── All Maintainer permissions │
└──────────────────┬───────────────────────────┘
│
▼
┌──────────────────────────────────────────────┐
│ Maintainer │
│ ├── Manage settings │
│ ├── Manage protected branches │
│ └── All Developer permissions │
└──────────────────┬───────────────────────────┘
│
▼
┌──────────────────────────────────────────────┐
│ Developer │
│ ├── Push to branches │
│ ├── Create merge requests │
│ └── All Reporter permissions │
└──────────────────┬───────────────────────────┘
│
▼
┌──────────────────────────────────────────────┐
│ Reporter │
│ ├── View code │
│ ├── Clone repository │
│ └── All Guest permissions │
└──────────────────┬───────────────────────────┘
│
▼
┌──────────────────────────────────────────────┐
│ Guest │
│ ├── View issues │
│ └── Comment │
└──────────────────────────────────────────────┘
Protected Branch Override
┌──────────────────────────────────────────────┐
│ Developer role │
│ Default: can push to branches │
└──────────────────┬───────────────────────────┘
│
▼
┌──────────────────────────────────────────────┐
│ Protected branch: main │
│ Allowed to push: Maintainers │
└──────────────────┬───────────────────────────┘
│
▼
┌──────────────────────────────────────────────┐
│ Developer cannot push to main │
│ Must use merge request │
└──────────────────────────────────────────────┘
Summary
| Item | Value |
|---|---|
| Project roles | Guest, Reporter, Developer, Maintainer, Owner |
| Role hierarchy | Cumulative |
| Visibility levels | Private, Internal, Public |
| Protected branch | Overrides role for specific branches |
| Default protected branch | main |
| Deploy token scope | read_repository, read_registry |
| Project access token scope | api, read_repository, write_repository |
Key takeaways:
- The roles are cumulative. A Maintainer has all the permissions of a Developer, plus the ability to manage settings. The role assignment is the baseline permission .
- The protected branch overrides the role. A Developer cannot push to
mainif the protected branch allows only Maintainers. The protected branch is the secondary control . - The visibility determines the audience. A Private project is visible only to members. A Public project is visible to everyone. The visibility does not change the role permissions .
- The deploy token is for automation. It has a scope, not a role. It can clone or pull, but cannot push unless the branch settings allow it .
- The project access token is for API automation. It has a bot user, scopes, and an expiration date. It is the alternative to a personal access token for CI/CD .
Remember: The role is the baseline. The protected branch is the override. The visibility is the audience. The token is the automation. The exam tests the role hierarchy and the protected branch settings. The most common mistake is assuming that a Developer can push to main because a Developer can push to a feature branch. The protected branch is the difference.
Stop using slow, ad-bloated tool sites! 🤮
🔎 Search “KandZ Tools” on Google to use many professional utilities for free.
KandZ.me is the ultimate minimalist hub for:
✅ Finance (Mortgage, Interest, Inflation)
✅ Tech (Base64, JSON, Dev Suite, IP)
✅ Health (BMI, BMR, TDEE)
✅ Productivity (Timer, Workspace, QR)
⚡️ Fast & Private
🔒 No data leaves your device
💎 100% Free
🔗 Use it now: https://tools.kandz.me
🔖 Bookmark it—you’ll need it later!