GitLab CODEOWNERS explained: syntax, sections and required approvals

By Melih Toksari, developer of MergeBell · Updated · 4 min read

Short answer

Code Owners is a Premium and Ultimate feature. Put the file at ./CODEOWNERS, ./docs/CODEOWNERS or ./.gitlab/CODEOWNERS; GitLab uses the first one it finds, in the target branch. Each line pairs a path pattern with owners, written as @user, @group, @@role or an email address, and within a section the last matching line wins. Sections in [brackets] are enforced separately, ^[Section] makes one optional, [Section][2] requires two approvals, and !path excludes files. Code Owners only become required approvers when the target branch is protected with Require code owner approval turned on.

A CODEOWNERS file tells GitLab who knows each part of your code. GitLab shows those owners on the files they own and, if you set it up, won’t merge a change until they approve it. Here is how the file works in GitLab in 2026, and how to make its approvals required. Code Owners needs GitLab Premium or Ultimate.

Where the file goes

A repository has one CODEOWNERS file. GitLab looks in three places, in this order, and uses the first it finds:

  1. CODEOWNERS in the root directory
  2. docs/CODEOWNERS
  3. .gitlab/CODEOWNERS

For a merge request, GitLab reads the file from the target branch, so a change to CODEOWNERS takes effect once it’s merged there. Files that match no rule need no Code Owner approval, new files included.

The syntax

Each line is a path pattern followed by one or more owners:

# A default owner for every file
* @default-owner

# Several owners for one file
README.md @doc-team @tech-lead

# Every Ruby file
*.rb @ruby-owner

# A directory and everything in it
/docs/ @all-docs

# Markdown files at any depth under /docs
/docs/**/*.md @markdown-docs

# A role: direct project members with the Maintainer role
/config/ @@maintainer

How GitLab reads it:

  • The last match wins. Within a section, only the last line that matches a file counts, so put broad rules like * first and specific ones after.
  • Paths: a leading / anchors to the repository root. Without it, the pattern matches at any depth, so README.md matches a README.md file in any directory. A trailing / means a directory and everything under it, * matches within a name, and ** matches across directories.
  • Spaces: escape them, as in path\ with\ spaces/*.md.
  • Comments: lines starting with # are ignored. Inline comments aren’t supported: everything after the path is read as owners, so an @name in a trailing comment becomes an owner.

Who can be an owner

Write Means Eligible
@username A user Users authorized for the project, such as direct members with the Developer role or higher, and members of the project’s groups
@group or @group/subgroup A group’s direct members The project’s group and its ancestors, and groups invited with the Developer role or higher to the project or (GitLab 18.6 and later) to one of its parent groups
@@developer, @@maintainer, @@owner Everyone with that role Direct project members with exactly that role; @@developer doesn’t include Maintainers. GitLab 17.9 and later
[email protected] A user, by email As for @username, and the address must be a verified email on that user’s account

GitLab ignores owners that are malformed, misspelled or can’t access the project. A rule left with no eligible owner can never be satisfied, so it never blocks a merge request, even when the branch requires Code Owner approval.

Sections

Sections group rules under a name in square brackets, and each one is enforced separately:

[Documentation] @docs-team
docs/
README.md

[Database][2] @database-team
model/db/

^[Go]
*.go @go-team
  • Default owners: owners after the heading, like @docs-team, apply to every path in the section that doesn’t name its own.
  • More approvals: [Database][2] needs two approvals from that section’s owners.
  • Optional: ^[Go] lists owners without requiring their approval in merge requests.
  • Rules before the first heading form an unnamed section of their own.
  • Same name twice: sections with the same name are combined, and the names are case-insensitive.

A file that matches lines in several sections needs approval from each section, counting the last matching line in each. Since GitLab 17.11, if a heading can’t be parsed, GitLab ignores it and adds its rules to the section before it, or to the default section if there isn’t one.

Exclusions

Since GitLab 17.11, a ! exempts paths from approval in that section, which suits files your tools update:

* @default-owner
!package-lock.json
!**/generated/

Exclusions apply in order: once a path is excluded, a later line in the same section can’t bring it back. To give excluded files a different owner, put that rule in another section.

Make the approvals required

On its own, CODEOWNERS only shows owners and suggests approvers. To make their approval required:

  1. Protect the target branch, usually your default branch.
  2. In Settings › Repository › Branch rules, select View details next to it.
  3. Turn on Require code owner approval.

Merge requests into that branch then need an approval from the Code Owners of every changed file, per section. It’s a merge request check, though: people in the branch’s Allowed to push and merge list can still push straight to it and skip the review. To send every change through Code Owners, set Allowed to push and merge to No one.

Check your file

When you view CODEOWNERS in the repository, GitLab (18.2 and later) checks it for syntax errors and for owners who can’t access the project or approve merge requests.

If approvals don’t show on a merge request, check that the file exists in the target branch. GitLab updates Code Owner rules when the merge request is created or edited, when commits are pushed to it, and when a new CODEOWNERS is pushed to the protected target branch.

If the merge request says “1 invalid rule has been approved automatically” and a Code Owner rule shows Auto approved, that section needs more approvals than it has eligible owners, often because an owner isn’t a member of the project. Invite the user or group, or lower the section’s approval count.

Approvals on your phone

When Code Owner approvals are what’s holding a merge request, MergeBell’s merge sheet lists missing approvals with every other blocking reason before you tap Merge, and you can approve from the same screen on the free tier. For how approval rules fit together, see who can approve a GitLab merge request.

Frequently asked questions

Where does the CODEOWNERS file go in GitLab?

In the repository root, in docs/, or in .gitlab/. GitLab checks those three places in that order and uses the first CODEOWNERS file it finds, so a repository has one. It reads the file from the merge request's target branch.

Is CODEOWNERS available on GitLab Free?

No. Code Owners is a Premium and Ultimate feature, both for showing owners on files and for requiring their approval.

Why are Code Owner approvals optional on my merge request?

Usually because the target branch isn't protected with Require code owner approval turned on. A rule is also optional when its section is marked optional with ^, or when its owners aren't eligible, are misspelled, or can't access the project.

How do I require two Code Owner approvals in GitLab?

Add the number after the section name, as in [Documentation][2], and turn on Require code owner approval for the protected target branch in Settings › Repository › Branch rules.

Which CODEOWNERS line wins when several match a file?

Within a section, the last matching line. Each section is enforced separately, so a file can need approval from the last matching owners of every section it appears in.

Sources

Checked against these pages on 25 October 2026. GitLab changes quickly — if something here no longer matches, tell us and we will fix it.

Do this from your phone with MergeBell

Free for the whole review loop: the To-Do inbox, diffs and line comments, approve and merge, pipelines with retry, and push alerts for your To-Dos. Works with gitlab.com and self-managed GitLab 16.0+.

Get MergeBell for AndroidiPhone: email me at launch

On Google Play for Android 12 or later. The iPhone app is coming soon to the App Store. An independent app, not affiliated with GitLab Inc.