GitLab scan execution and merge request approval policies

Scan execution and merge request approval policies let a security team enforce scanners and approvals across many GitLab projects from one place. This page covers the policy design, targeting, roles and setup choices that decide whether enforcement holds on a self-managed instance.

On this page

What GitLab scan execution and merge request approval policies enforce

Scan execution policies make GitLab run required security scanners in the pipelines of every project in scope, and merge request approval policies require specific reviewers or approvals before a merge request can be merged when its findings or changes meet conditions you define. A security or platform lead uses both to enforce a baseline across many groups and projects without editing each .gitlab-ci.yml file. The central security policy project is the mechanism that makes this work: policies are stored there as YAML, and the project's owners control what every targeted project inherits.

Scan execution policies suit cases where a scanner must run regardless of what a team put in its pipeline, such as secret detection or SAST on every repository in a regulated business unit. Merge request approval policies suit cases where a change must be reviewed by a named group, for example when a vulnerability of a given severity appears or when a change touches protected code. Teams usually need both, because one forces the evidence to exist and the other forces a human decision on it. Before you draft policies, decide which scanners are mandatory, which groups fall under each policy and who must approve exceptions.

Scope is the first design decision. Policies target projects through the policy_scope setting, so confirm how your groups, subgroups and individual projects map to the scopes you need, and test one policy against a sandbox group before you apply it broadly. Edition fit depends on which scanners and approval conditions you require, and the detailed comparison is covered in the GitLab features and the Free vs Ultimate pages. Confirm your tier with GitLab before you design around a specific policy type.

Scan execution policies versus merge request approval policies

A scan execution policy changes what runs in a pipeline. You define which scanner jobs must run, and GitLab injects them into the pipelines of projects that match the policy's scope, so a developer cannot remove them by editing .gitlab-ci.yml. The policy depends on the scanner analyzers being available to your runners and on the scan results they produce. Teams pick this type when they need consistent SAST, secret detection or dependency scanning across many projects that were never set up with the same template.

  • Pipeline injection: The policy adds scanner jobs to matching pipelines at run time, which is the main reason it is used for coverage guarantees.
  • Scope targeting: The policy_scope setting decides which projects, groups or compliance framework labels the policy applies to, so confirm its exact syntax before you write it.
  • Runner and image dependencies: Scanner jobs need runners that can pull the analyzer images, which matters on offline or air-gapped instances.
  • Scanner-level results: The policy produces scan results that other policies can read, so the job definition must match the report types you expect.
  • Scope of control: It enforces that scans run, not that a human reviews the findings.

Before building, confirm the configuration keys, supported fields and scanner options in the current GitLab documentation for your version.

How policy_scope decides which projects are governed

A security policy only governs the projects its policy_scope selects, so the first decision is whether the policy applies to the whole instance, to selected groups, or to projects that match criteria. A whole-instance scope is the simplest to reason about but affects every project, including sandboxes and archived work, so teams usually narrow it to the groups that ship production code. Group-level scope fits organizations where each business unit owns its own pipelines. Criteria-based scope fits cases such as regulated repositories identified by naming convention or by a compliance label. Confirm which scope keys your GitLab version supports, how each key combines with the others, and whether a scope applies to subgroups by default before you write any policy.

The most common failure is an assumption that a project created next month will inherit a policy. It will not, unless the scope you wrote actually matches its group or its path. A new project under a group that is not in scope, or one whose path misses a criteria pattern, runs its pipelines with no required scanner and no approval rule, and nothing on the project page warns the team. Treat scope as a prediction you must test. Take a sample of real project paths from your instance, including recently created projects, forks, nested subgroups and projects that were moved between groups, and check each one against the scope definition before rollout.

Write the sample check down as an acceptance step. For each path, record whether the policy should apply, whether it does, and which policy configuration governs it. Repeat the check after every reorganization, because a group rename or project transfer can silently change coverage. Ask your GitLab administrator or partner to confirm the exact scope keys, the inheritance rules for subgroups, and how scope interacts with the security policy project for your installed version, and keep the reviewed sample alongside the policy as evidence of coverage.

Which policy settings fit which governance goal

This table compares the main configuration choices for GitLab security policies, including where the policy project lives, how policies are linked, how scans are targeted and how approvals are designed. Read each row against the governance goal you need to meet, then confirm the exact options and behavior in the GitLab documentation for your installed version before you commit to a design.

OptionWhat it controlsWhen it fitsWhat to confirm
Dedicated security policy projectHolds the policy definitions in a project separate from application code, so policy changes go through their own review and access modelSeveral groups need the same controls and policy authors should not be the application maintainersWhich roles can edit the policy project, and how its merge requests and approvals are configured in your version
Existing project as policy sourceReuses a project that already exists for another purpose to store policy definitionsSmall instances with few policies, or where a separate project would add administration without benefitWhether application maintainers can modify policies, and how this affects separation of duties
Instance-level linkingApplies a policy to every project on the instance, including projects created laterBaseline controls that must hold everywhere, such as required scanners on all default branchesWhich project and group owners are affected, and whether any projects need documented exceptions
Group-level linkingApplies a policy to projects within a chosen group and its subgroupsDifferent business units or regulated workloads need different scan and approval rulesWhether subgroups inherit the link, and how projects moved between groups are handled
Targeting by branchLimits which branches a scan or approval rule applies to, typically default or protected branchesRelease branches need stricter checks than feature branches, or only merges into production matterThe branch pattern syntax supported in your version and how it matches protected branches
Targeting by pipeline or scan executionDetermines which pipelines run the required scanners and which results the policy evaluatesScanners must run on every merge request, or on scheduled pipelines for scans that are slow or time basedWhether required jobs are injected into pipelines and how jobs defined in the project's own CI file interact with them
Required approvers and approval countSets how many approvals a merge request needs and which roles or named users must approveChanges to security-sensitive code need reviewers outside the author's team, such as a security groupRequired roles for approvers, and whether authors can approve their own changes in your settings
Blocking merges on scan resultsDecides whether findings above a defined severity or state prevent a merge until resolved or approvedRegulated workloads where unresolved high-severity findings must stop a releaseWhich finding severities and states block, and whether a blocked merge can be overridden and by whom
Enforcement modeDescribes whether a policy is enforced strictly or can be bypassed or tested before enforcement, if your version offers thisRolling out a new policy to a large estate without disrupting active teamsWhether your version documents an enforcement or dry-run mode, and how exceptions are recorded

How to set up a security policy project and link it to groups

This sequence moves a set of GitLab security policies from a controlled policy project into a single test group, then across target groups in stages. Each step ends with a check that shows the policy behaves as intended before it reaches production scope.

  1. Create a dedicated policy project with restricted maintainers

    A platform or security engineer creates a separate project in a group that only security owners can administer, and limits its maintainer role to a small named set of people. Keep this project apart from application code so that developers who change pipelines cannot also change the rules that govern them. Confirm the membership list and the protected branch settings match your access model before any policy is written. Done means the project exists, its maintainers are documented, and a non-maintainer developer cannot edit its files.

  2. Define one policy in a test group

    Write a single scan execution policy or merge request approval policy in the policy project, using the YAML fields from the current GitLab documentation rather than from memory or older examples. Set its policy_scope so it targets only a test group that holds sample projects. Test on a non-production instance first, since field names and menu paths change between releases. Done means the policy is committed, validated by GitLab, and visible in the security policies list for the test group.

  3. Confirm scan jobs appear in a sample pipeline

    Trigger a pipeline in one sample project inside the test group and open the pipeline graph. The scan execution policy should inject the configured scanner jobs, and you should see them run with the expected analyzer images and variables. If no job appears, check the policy scope, the branch or pipeline conditions, and whether the project has the required scanner access in your instance. Done means the injected job is visible in the pipeline and its results appear in the security dashboard.

  4. Add merge request approval rules

    Add a merge request approval policy to the same policy project that requires approval from a defined role or group when scan results meet your chosen conditions, such as a finding severity. Keep the rule narrow for the first test so that the approval requirement is easy to trace. Open a merge request in a sample project that triggers the condition. Done means GitLab requests the required approval and the merge request cannot be merged until an authorized reviewer approves it.

  5. Link the policy to target groups

    Extend the policy_scope so that it covers the production groups you have agreed with application owners, either by listing them or by using the scope options your GitLab version supports. Check the current documentation for the exact syntax and for how linking works across groups and subgroups. Before saving, review any other policy already linked to those groups to find conflicting actions or duplicate jobs. Done means the scope is saved, the linked groups appear on the policy, and the review of existing policies is recorded.

  6. Roll out in stages and verify out-of-scope projects

    Enable the policy for one target group at a time and watch the first pipelines and merge requests in each. Also test a project outside the scope to confirm that it runs its normal pipeline without injected jobs or new approval rules. Expand scope only after the results match the test and application teams have been informed. Done means each stage has a recorded check, and an out-of-scope project is confirmed unaffected.

Required roles and conflicting links that break enforcement

Scan execution and approval policies fail quietly more often than they fail loudly. The cards below cover the access, linking and configuration gaps that let a policy look active while a project still merges without the required scan or approval.

Who can create and link policies

Creating, editing and linking a policy depends on the role a person holds in both the policy project and the groups or projects the policy targets. A policy author who can write to the policy project but holds no role on the target projects may see the policy in a list yet cannot confirm it applies. Before rollout, list every person who will maintain policies, confirm their role in each place the policy is linked, and test the link with a non-owner account.

Overlapping rules on one project

Two policies that set different approval or job rules on the same project can produce an outcome that neither policy author intended. Symptoms include approvals that appear stricter or looser than either policy states, or a scanner job that runs in one pipeline and not another. Before rollout, inventory every policy linked to each project and compare their approval and job rules side by side. Ask GitLab documentation or support to confirm how conflicting settings are resolved in your version.

Links to an invisible policy project

A group owner may link a policy project that they cannot see in full, or that sits in a group with stricter visibility than the project it governs. The link can save without visible error, while the policy stops taking effect for users who lack access to the policy project. Symptoms include policies missing from the compliance view for some owners. Check that the policy project is reachable by the people who administer the linked groups, and verify the link from each owner's account.

Job names changed by template updates

Policies that reference a scanner job by name break silently when a template renames or replaces that job. The policy keeps running, but it checks for a job that no longer exists, so the gate passes without the scan that it was meant to require. Watch for approvals that stop triggering after an upgrade or a template change. Before rollout, record the exact job names each policy references, compare them with the current templates in your pinned GitLab version, and rerun a test pipeline after every template update.

Maintainers who can edit pipeline config

A project maintainer who can edit .gitlab-ci.yml can remove or override an injected scanner job in the project's own pipeline configuration. The change may not be obvious in merge request review if no policy checks the configuration itself. Symptoms include scanner reports that stop appearing for one project while others remain normal. Before rollout, decide which roles may change pipeline configuration for governed projects, and test whether a maintainer edit can bypass a required scan in a sandbox project.

Who should own policy changes and how they are reviewed

Treat the security policy project like production configuration, because a change to it can alter what runs in pipelines across the instance. Restrict write access to that project to a small named group, require every change to arrive as a merge request, and require approval from a named security approver before it merges. Keep the project's commit history as the record of who changed what and why, and write down how you would revert a bad policy before you need to. Confirm in your own instance how the security policy project is linked to its target groups and which roles can edit it, since those settings decide who can change enforcement without going through review.

Split ownership along the line between intent and execution. The security team should own what the policies require, such as which scanners must run, which projects a policy targets through policy_scope, and which approvals a merge request needs. The platform team should own the GitLab instance, the GitLab Runner fleet that executes the required jobs, and the pipeline capacity those jobs consume. Agree on this split in writing, including who may approve a change that affects runners or instance settings, so that neither team can silently widen or break the other's responsibilities.

Policy changes do not stay local. One edit can add a required job to pipelines for many teams at once, change approval rules on merge requests in dozens of projects, or fail every pipeline that depends on a template that has moved. Roll changes out in stages: test the new policy on a small set of non-critical projects, watch pipeline duration, runner queue time and failure rates, then widen the scope. Keep the previous policy version ready so the rollback is a single, reviewed change rather than an emergency edit under pressure.

Security and operational risks to plan for

Scan enforcement through policies is only as strong as the project that holds the policies, the runners that execute the injected jobs and the versions that keep them working together. The cards below name the checks a platform team can run on each of these links, and they sit alongside, not in place of, the pipeline controls in the hardening guidance.

Protect the policy project

The security policy project holds the rules that decide which scanners run and which approvals are required, so anyone who can change it can weaken enforcement across every linked group. Restrict who can push to its default branch, require merge requests with approvals for changes, and review who holds maintainer or owner roles on it. Check these settings after every administrator change, because a quiet permission change here affects many projects at once.

Verify injected scanner jobs

Policies add scanner jobs to pipelines that teams do not write, so the runner must pull the expected analyzer images. On offline instances, confirm that the scanner image references resolve to your internal registry and that the vulnerability data came from your mirror, not an external source. Run a test pipeline in a sandbox project and inspect the job logs for the image names and digests that actually executed.

Keep analyzer versions aligned

Scanner analyzers are versioned separately from the GitLab release, and a mismatch can change which findings appear or how a policy evaluates them. After every GitLab upgrade, compare the analyzer image tags your mirror holds with the versions the release expects, and update them together. The GitLab Runner documentation asks that Runner major and minor versions stay aligned with GitLab, so include Runners in the same checklist.

Monitor failed scan jobs

When a required scanner job fails, the merge request can stay blocked, and teams often read that as a platform fault and ask for an exception. Track failed or missing scanner jobs on protected branches as an operational signal, not only as a developer problem. Set up a daily review of pipeline failures for the scanner job names, and agree in advance who may grant a documented, time-limited exception.

Plan for policy changes on upgrade

A GitLab upgrade can change how policy rules are evaluated, which scanners are enabled by default and which job names exist, so a policy that passed last month may behave differently after an upgrade. Before each upgrade, test the policy project and a sample of linked projects on a staging instance that matches production. Use the upgrade stops and the maintenance policy to set the target version, then confirm that required approvals still trigger as expected.

Limits to confirm in your own GitLab environment

Before you commit to a policy design, confirm what your GitLab version and edition actually provide. Policy types, the fields available on each one, and the approval settings they can enforce change between releases, so the field names and behaviors in any design document must be checked against the documentation for the exact version you run, not against examples from memory. Confirm the same for your tier: the pricing page lists application security testing and policy-related features under Ultimate, and the tier that applies to each scanner and policy feature should be verified with the GitLab account team before you assume a feature is available. Ask for the feature matrix in writing so procurement and the security team work from the same facts.

Offline and air-gapped instances need a separate check. A policy that requires a scanner job only works if the scanner images and vulnerability data it depends on are present in your registry and reachable from your runners. Confirm with the offline environments documentation which scanner images, vulnerability databases and package sources your enforced jobs need, and test one enforced pipeline on a mirrored instance before any policy is applied across groups. A policy that passes on a connected test instance can still fail at merge time on a restricted network.

Scope is the third check. Work out how many groups, subgroups and projects the policy must cover, and whether they share a security policy project or need several. Test the policy_scope targeting on a sample of projects that reflect your real structure, including archived projects, forks and projects that use custom CI configuration. Finally, verify how approval rules interact with your existing protected branch rules and CODEOWNERS files. Overlapping approvers, conflicting required roles or a policy that adds approvals a branch rule already requires can block merges in ways that are hard to diagnose. Run those interactions on a test instance and record the results before rollout.

What buyers ask before bringing in a partner for GitLab security policies

How much effort does setting up GitLab security policies take?

The effort depends on how many groups and projects the policies must reach, how many scanners are in scope, which GitLab tier you run, whether the instance is offline, and how much your existing pipelines already look alike. Each group with its own pipeline layout adds mapping and testing work, and each scanner adds its own job template, results to check and exceptions to agree on. Offline instances need the scanner images and vulnerability data mirrored first. The GitLab pricing page lists application security testing under its highest tier, so confirm which policy types your tier includes before you scope the work. Ask any partner to size the effort from a written list of groups, scanners, tier and pipeline patterns.

Can our internal team set up GitLab security policies without a partner?

Yes, a capable internal team can do it if it has people who understand GitLab administration, CI/CD configuration, the security scanners you plan to enforce and your approval model. The work spans instance settings, a dedicated security policy project, policy targeting across groups, role assignments and test pipelines that prove each rule behaves as written. Most teams find the hard part is not the YAML but deciding who approves what and handling conflicts between policies that touch the same projects. If those skills are thin, a partner can supply them for a fixed scope while your staff learn the design and operate it afterward.

How long does a pilot for GitLab security policies usually run?

The length of a pilot is set by the number of test groups, the scanners in the first wave, how quickly approvers can review test merge requests, and how many pipeline variations you must prove. A pilot should run on a small set of representative projects before any group-wide rollout, so it can show how policies behave with real pipeline layouts. Schedule time for change approval inside your organization and for a deliberate failure test, which often takes longer than teams expect. Ask for the pilot's exit criteria in writing, since a defined finish line matters more than a guessed duration.

What does our team need to provide for a policy project?

The work needs decisions and access more than heavy hardware. You will need a list of groups and projects in scope, named approvers for each policy, a decision on which roles may edit the security policy project, and an agreed rule for who can change policy after go-live. The engagement also needs administrative access to the instance or a sandbox copy of it, test projects that mirror production pipelines, and a contact who can answer questions about existing CI/CD templates. If the instance is offline, you must also provide the mirror path for scanner images and vulnerability data. Settle these items before work begins so they do not stall the pilot.

What does a fixed-scope GitLab policy engagement include and how is the quote made?

A fixed-scope engagement covers the agreed groups, scanners, policy types, test cases, documentation and handover items, and each of these is written down before work starts. The quote follows from that written scope, so a clearer list of groups, scanners and environments usually produces a more precise quote. Costs are one-time for design, build and testing, while ongoing policy changes, scanner upgrades or new groups can move to a retainer for support and continuous testing. DeployOpen does not publish a price list. When you request a quote, ask the partner to state the included deliverables, the assumptions behind them, and what triggers a change order.

Will a GitLab merge request policy stop a merge if a scanner fails?

It depends on how the policy and the scanner job are configured, and you should confirm the behavior for your GitLab version before relying on it. A merge request approval policy can require approval when scan results meet defined conditions, and a scan execution policy can make sure a required scanner runs in the pipeline. Whether a failed or missing scan blocks a merge depends on those settings and on how the pipeline reports the job, so an unfinished or failing scan may not always produce the block you expect. Test this deliberately in the pilot by forcing a scanner failure and by removing a scanner from a pipeline, then record the result for auditors and approvers.

How DeployOpen scopes, delivers and supports GitLab security policies

DeployOpen plans, implements, documents and supports self-hosted GitLab inside your own infrastructure, including the security policy project and the approval rules that depend on it. Engagements are fixed-scope, with the policy design, targets and handover agreed in writing before work starts.

How we scope

  • The written scope lists which groups and projects the policies target and which scanners each one must run.
  • We record the policy project location, the policy_scope targets and the roles allowed to edit policies.
  • The scope names every existing approval rule, merge request setting and project template that a new policy could conflict with.

How we build

  • We create the security policy project with restricted maintainer access and version its policy files in Git.
  • We test each policy on a sandbox group first, checking that the expected scanner jobs run and approvals trigger.
  • We confirm scanner templates and analyzer images resolve correctly in your instance, including offline mirrors where your network requires them.

How we hand over

  • You receive policy files, a targeting map and a record of each approval rule and its owner.
  • We document the change process for editing policies, including who reviews a policy change before it merges.
  • A runbook covers conflicting links, how to trace an unexpected approval requirement and how to roll back a policy.

How we support

  • Ongoing support, upgrades and continuous testing are available as a retainer, covering policy updates after GitLab releases.
  • Scanner and policy behavior is rechecked at each upgrade stop, since required stops can change how policies apply.
  • We can review new groups and projects against the policy targets before they start receiving merge request approvals.

Talk to our team.

Tell us what you're working on, whether it's a deployment, an audit, a security test or a cyber range. You'll speak with an engineer who can help you scope it.

  • 30-minute call: free, with no obligation.
  • NDA on request: we can sign before you share details.
  • Clear next steps: a scope and plan after the call.