Safe Settings uses a three-level configuration hierarchy that allows you to set organization-wide defaults while giving teams flexibility to customize settings for their specific needs.
Configuration Hierarchy
Precedence Order : Repository > Sub-Organization > Organization
Settings at lower levels override settings at higher levels, allowing centralized defaults with local customization.
Basic Multi-Level Example
.github/settings.yml
.github/suborgs/backend-services.yml
.github/repos/api-gateway.yml
# Organization-level defaults
repository :
private : true
has_issues : true
has_wiki : false
allow_merge_commit : false
allow_squash_merge : true
delete_branch_on_merge : true
labels :
include :
- name : bug
color : "d73a4a"
- name : enhancement
color : "a2eeef"
teams :
- name : all-engineers
permission : pull
branches :
- name : default
protection :
required_pull_request_reviews :
required_approving_review_count : 1
dismiss_stale_reviews : true
required_status_checks :
strict : true
contexts : []
enforce_admins : false
restrictions :
apps : []
users : []
teams : []
Result for api-gateway repository :
Private repository (from org)
Has issues, no wiki (from org)
Squash merge only (from org)
Backend + performance + database labels (from suborg)
Backend team has admin access (from suborg)
3 approvals required (from repo)
4 CI checks required (2 from suborg + 2 from repo)
Only platform leads can push (from repo)
Enforced for admins (from repo)
Real-World Scenarios
Scenario 1: Tiered Security by Service Criticality
# Baseline security for all repos
repository :
private : true
security :
enableVulnerabilityAlerts : true
enableAutomatedSecurityFixes : true
branches :
- name : default
protection :
required_pull_request_reviews :
required_approving_review_count : 1
dismiss_stale_reviews : true
required_status_checks :
strict : true
contexts :
- "ci/tests"
enforce_admins : false
restrictions :
apps : []
users : []
teams : []
.github/suborgs/production-critical.yml
# Higher security for production services
suborgproperties :
- criticality : high
branches :
- name : default
protection :
required_pull_request_reviews :
required_approving_review_count : 2
dismiss_stale_reviews : true
require_code_owner_reviews : true
required_status_checks :
strict : true
contexts :
- "ci/tests"
- "ci/security-scan"
- "ci/compliance"
enforce_admins : true
restrictions :
apps : []
users : []
teams : []
.github/repos/payment-processor.yml
# Maximum security for payment processing
repository :
topics :
- payments
- pci-compliant
- critical
branches :
- name : default
protection :
required_pull_request_reviews :
required_approving_review_count : 3
dismiss_stale_reviews : true
require_code_owner_reviews : true
require_last_push_approval : true
dismissal_restrictions :
teams :
- security-team
- payment-leads
required_status_checks :
strict : true
contexts :
- "ci/tests"
- "ci/security-scan"
- "ci/compliance"
- "ci/pci-validation"
- "ci/fraud-check"
enforce_admins : true
restrictions :
teams :
- payment-leads
Scenario 2: Team-Based Configuration
.github/settings.yml
.github/suborgs/frontend-team.yml
.github/suborgs/backend-team.yml
# Minimal org defaults
repository :
private : true
has_issues : true
default_branch : main
teams :
- name : everyone
permission : pull
labels :
include :
- name : bug
color : "d73a4a"
- name : enhancement
color : "a2eeef"
Result : Each team automatically gets their configuration applied to any repository they’re added to, with team-specific labels, access, and protection rules.
Scenario 3: Environment-Based Configuration
.github/settings.yml
.github/suborgs/staging-envs.yml
.github/suborgs/production-envs.yml
# Base settings
repository :
private : true
has_issues : true
allow_squash_merge : true
delete_branch_on_merge : true
teams :
- name : engineering
permission : push
Result : Repositories are automatically configured based on their environment (staging vs production) through naming conventions.
Understanding Setting Merges
How Arrays Merge
Labels
Teams
Branch Protection
Labels are merged - items from all levels are combined:# Org: 2 labels
labels :
include :
- name : bug
- name : enhancement
# Suborg: adds 2 more
labels :
include :
- name : bug # same as org
- name : enhancement # same as org
- name : frontend # new
- name : ui # new
# Result: 4 labels (bug, enhancement, frontend, ui)
Teams are replaced - lower level completely overrides:# Org: 1 team
teams :
- name : everyone
permission : pull
# Suborg: replaces completely
teams :
- name : backend-team
permission : admin
# Result: Only backend-team
# (everyone is NOT included unless explicitly added)
To keep org teams, re-specify them: teams :
- name : everyone # Re-specify from org
permission : pull
- name : backend-team # Add new team
permission : admin
Branch protection is replaced - lower level overrides:# Org: 1 approval
branches :
- name : default
protection :
required_pull_request_reviews :
required_approving_review_count : 1
# Suborg: changes to 2 approvals
branches :
- name : default
protection :
required_pull_request_reviews :
required_approving_review_count : 2
# Result: 2 approvals (suborg value)
Complete Override vs. Partial Override
Complete Override - Repository Settings
# Org level
repository :
private : true
has_issues : true
has_wiki : false
# Repo level - partial override
repository :
has_wiki : true # Only override this field
# Result:
# private: true (from org)
# has_issues: true (from org)
# has_wiki: true (from repo)
Repository settings merge field-by-field.
Complete Override - Branch Protection
# Org level
branches :
- name : default
protection :
required_pull_request_reviews :
required_approving_review_count : 1
required_status_checks :
strict : true
contexts : []
# Repo level - must specify all fields
branches :
- name : default
protection :
required_pull_request_reviews :
required_approving_review_count : 2
required_status_checks :
strict : true
contexts :
- "ci/tests"
enforce_admins : true
restrictions :
apps : []
users : []
teams : []
# Result: Exactly what's in repo level
Branch protection replaces entirely - must specify all required fields.
Advanced Patterns
Pattern 1: Shared Configuration Modules
Create suborg configs for common patterns:
# .github/suborgs/public-repos.yml
# Use for all public/open-source repos
suborgrepos :
- "docs-*"
- "blog-*"
- "examples-*"
repository :
private : false
visibility : public
has_issues : true
has_wiki : true
license_template : mit
labels :
include :
- name : good first issue
- name : help wanted
Pattern 2: Property-Based Suborgs
# .github/suborgs/high-security.yml
# Apply to repos with custom property
suborgproperties :
- security-level : high
branches :
- name : default
protection :
required_pull_request_reviews :
required_approving_review_count : 3
enforce_admins : true
Then set custom properties on repositories:
# .github/repos/auth-service.yml
custom_properties :
- name : security-level
value : high
Pattern 3: Milestone and Autolink Inheritance
# Org level - shared milestones
milestones :
- title : Q1 2024
description : First quarter deliverables
state : open
- title : Q2 2024
description : Second quarter deliverables
state : open
autolinks :
- key_prefix : "JIRA-"
url_template : "https://jira.company.com/browse/JIRA-<num>"
# Suborg level - additional milestones
milestones :
- title : Q1 2024
description : First quarter deliverables
state : open
- title : Q2 2024
description : Second quarter deliverables
state : open
- title : Backend Migration
description : Database migration project
state : open
autolinks :
- key_prefix : "JIRA-"
url_template : "https://jira.company.com/browse/JIRA-<num>"
- key_prefix : "DB-"
url_template : "https://dbissues.company.com/ticket/<num>"
Best Practices
Start Simple Begin with org-level settings only. Add suborg/repo configs when you have a clear need.
Document Overrides Use comments to explain why settings are overridden: # Override: Payment service requires PCI compliance
branches :
- name : default
protection :
required_approving_review_count : 3
Use CODEOWNERS Let different teams manage their suborg configs: .github/suborgs/frontend-*.yml @frontend-team
.github/suborgs/backend-*.yml @backend-team
Validate in PRs Always test multi-level config changes in a PR first. Safe Settings will show you the merged result.
Common Pitfalls
Teams Don’t Merge : If you specify teams at the suborg level, you must re-include org-level teams if you want to keep them.
Branch Protection Requires All Fields : When overriding branch protection, you must specify all required fields (required_pull_request_reviews, required_status_checks, enforce_admins, restrictions).
Include/Exclude Patterns : Be careful with overlapping patterns. Test your glob patterns to ensure repos match the intended suborg.
Troubleshooting
Settings not applying as expected
Debug steps :
Check which suborg config applies (review suborgrepos, suborgteams, suborgproperties)
Check precedence: repo > suborg > org
Review Safe Settings check run for what was actually applied
Look for validation errors in PR comments
Repo matches multiple suborg configs
Solution : Only one suborg config applies per repository. If a repo matches multiple patterns:
First match wins (alphabetical order of filename)
Avoid overlapping suborg patterns
Use repo-level config for specific overrides
Changes not visible on some repos
Check :
Repository is not in restrictedRepos
Safe Settings has been triggered (push to default branch or webhook event)
No validation errors in check runs
Next Steps
Custom Validation Add validators to prevent invalid overrides
Basic Setup Review the basic setup guide