A cookbook of settings.yml files. Every settings example on this page runs through the real document validation in CI, so the shapes stay current. What each section manages, which token permission it needs, and whether its undeclared entries are deleted or kept is specified in the README Sections table; the cross-section rules live under Semantics. This page shows shapes, not behavior.
One rule frames everything below: only declared keys are applied or
compared. A section, or a field inside one, that the file does not mention
is never touched. The rule has edges worth knowing. Some list entries are
one full payload: a declared ruleset is applied with a full-payload PUT,
so a partial ruleset entry silently narrows the live one; declare each
ruleset completely. Labels and milestones work the other way: only the
fields you declare are sent, so an omitted description or state is left
alone. And two sections bend the rule where the API forces their hand, as
the Sections table notes: inside a declared
protection object the classic API requires all four core keys, so apply
fills the ones you omit with null (see
Classic branch protection below), and in the
actions section, declaring any base permissions key (or
selected_actions, which infers allowed_actions: selected) makes the
base PUT carry enabled: true unless the file says otherwise, while
retention-, cache-, workflow-token-, or access-only declarations leave the
base policy alone.
Enough to be useful on day one: a few repository fields and the labels you actually triage with.
repository:
description: Payments service
topics: payments, service
has_wiki: false
delete_branch_on_merge: true
labels:
- name: bug
color: "d73a4a"
description: Something isn't working
- name: needs-triage
color: "ededed"A single-repo file exercising most sections. topics accepts a
comma-separated string or a YAML list, and enable_* keys are feature
toggles the section routes to their own endpoints; everything else under
repository goes to the API verbatim.
repository:
description: Payments service
homepage: https://payments.example.com
topics: payments, service, production
has_wiki: false
has_projects: false
allow_merge_commit: false
allow_squash_merge: true
squash_merge_commit_title: PR_TITLE
delete_branch_on_merge: true
enable_vulnerability_alerts: true
enable_automated_security_fixes: true
labels:
- name: bug
color: "d73a4a"
description: Something isn't working
- name: prio
new_name: priority-high
color: "b60205"
rulesets:
- name: protect main
target: branch
enforcement: active
conditions:
ref_name:
include: ["~DEFAULT_BRANCH"]
exclude: []
rules:
- type: deletion
- type: non_fast_forward
- type: pull_request
parameters:
required_approving_review_count: 1
dismiss_stale_reviews_on_push: true
require_code_owner_review: false
require_last_push_approval: false
required_review_thread_resolution: true
- name: release tags
target: tag
enforcement: active
conditions:
ref_name:
include: ["refs/tags/v*"]
exclude: []
rules:
- type: deletion
- type: update
environments:
- name: production
pinned: true
wait_timer: 30
prevent_self_review: true
reviewers:
- type: Team
id: 4501
deployment_branch_policy:
protected_branches: false
custom_branch_policies: true
deployment_branch_policies:
- name: release/*
- name: v*
type: tag
deployment_protection_rules:
- app: my-gate-app
variables:
- name: DEPLOY_REGION
value: eu-west-1
secrets:
- name: PROD_DEPLOY_KEY
value: $PROD_DEPLOY_KEY
autolinks:
- key_prefix: "TICKET-"
url_template: "https://example.atlassian.net/browse/TICKET-<num>"
actions:
enabled: true
allowed_actions: selected
selected_actions:
github_owned_allowed: true
verified_allowed: false
patterns_allowed:
- Vivswan/*
default_workflow_permissions: read
can_approve_pull_request_reviews: false
artifact_and_log_retention:
days: 30
cache:
max_cache_retention_days: 7
actions_variables:
- name: DEPLOY_REGION
value: us-east-1
actions_secrets:
- name: DEPLOY_TOKEN
value: $DEPLOY_TOKEN
dependabot_secrets:
- name: REGISTRY_TOKEN
value: $REGISTRY_TOKEN
codespaces_secrets:
- name: DEVCONTAINER_PAT
value: $DEVCONTAINER_PAT
agents_secrets:
- name: AGENT_TOKEN
value: $AGENT_TOKEN
agents_variables:
- name: AGENT_MODEL
value: default
webhooks:
- config:
url: https://ci.example.com/hook
content_type: json
secret: $HOOK_SECRET
events: [push, pull_request]
active: true
workflows:
- path: nightly-sync.yml
state: disabled
check_suite_preferences:
auto_trigger_checks:
- app_id: 12345
setting: false
pages:
build_type: workflow
code_scanning_default_setup:
state: configured
query_suite: default
code_quality_setup:
state: configured
languages: [go, python]
collaborators:
- username: octocat
permission: push
deploy_keys:
- title: deploy-bot
key: ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIB5n2eXAMPLEeXAMPLEeXAMPLEeXAMPLEeXAMPLEeXAM deploy@example
read_only: true
teams:
- name: platform
permission: maintain
milestones:
- title: v2.0
description: The big rewrite
state: open
interaction_limits:
limit: collaborators_only
expiry: one_week
pull_request_creation_cap:
enabled: true
max_open_pull_requests: 5
pull_request_creation_bypass: [octocat, hubot]
custom_properties:
- property_name: team
value: payments
- property_name: compliance
value: [soc2, pci]
- property_name: pilot
value: true
secret_scanning_custom_patterns:
- name: Internal API token
pattern: "int_[a-z0-9]{32}"
start_delimiter: '\b'In rulesets, short ref names are auto-prefixed (staging becomes
refs/heads/staging) and ~DEFAULT_BRANCH passes through; rule parameters
go to the API verbatim, so rule types GitHub ships tomorrow work unchanged
(see Forward compatibility).
custom_properties sets values for properties the organization has already
defined (a value: null unsets one), so it applies to org-owned
repositories only - on a personal account the section skips with a note.
branches is the classic per-branch protection API, kept for Probot
compatibility; rulesets are the modern replacement. The declared
protection object is the PUT payload, with one adjustment: the classic
API rejects a payload missing any of its four core keys
(required_status_checks, enforce_admins,
required_pull_request_reviews, restrictions), so apply fills omitted
core keys with null. A null there means "off", so an omitted
enforce_admins is turned off, not left alone; declare every core key you
want to keep, and check mode reports an omitted-but-live core key as drift
before an apply would null it away.
branches:
- name: main
protection:
required_pull_request_reviews:
required_approving_review_count: 1
enforce_admins: true
required_status_checks:
strict: true
contexts: [all-green]
restrictions: nullTwo protection surfaces the REST API cannot express ride GraphQL under the
hood, declared as ordinary keys: force_push_bypassers lists who may force
push (a bare login is a user, org/team-slug a team, app/slug a GitHub
App), and required_deployments requires deployments to the named
environments before merging (null turns the requirement off; declare the
environments in the same file - the environments section applies first).
An entry whose name is a wildcard pattern (release/*) is a classic RULE
rather than a branch: it reconciles entirely through GraphQL and its
protection accepts only the keys with exact GraphQL equivalents, so prefer
rulesets for new pattern-based configuration.
environments:
- name: production
branches:
- name: main
protection:
enforce_admins: true
force_push_bypassers: [octocat, my-org/release-team, app/deploy-bot]
required_deployments:
environments: [production]
- name: release/*
protection:
required_linear_history: true
required_status_checks:
strict: true
contexts: [all-green]For most sections, leaving a key out means "do not touch it". Three
resource-level declarations give an explicit null a meaning of its own.
pages: null declares the Pages site off. Apply deletes an existing site;
an absent pages key leaves the site alone.
pages: nullinteraction_limits: null clears an active repository-level interaction
limit - the base limit only; the pull request creation cap and its bypass
list are separate resources a null never touches. An absent key leaves
whatever limit is live untouched.
interaction_limits: nullprotection: null on a branch declares it unprotected, and apply removes
existing classic protection.
branches:
- name: legacy
protection: nullUnder a multi-repo defaults file, a target's null section can instead mean
"opt out of the defaults for this repository"; the rules for that merge are
in the multi-repo guide. A few individual fields
accept null as a value of their own too, such as pages.cname to remove
a custom domain; the published schema marks
those.
Unknown top-level sections are hard errors, so a typo cannot silently do
nothing (the one exception: under a sections allowlist, unknown keys
outside the allowlist warn instead of failing, which eases version skew;
the troubleshooting guide covers it). Keys starting
with an underscore are the escape hatch: they are ignored, which makes
them usable as private notes.
_owner: platform-team, see runbook RB-112
labels:
- name: bug
color: "d73a4a"Check mode is the safe way to try any of these files
against a real repository before applying. Multi-repo mode
reuses the same documents across a fleet, and
the undeclared policy explains what happens
to live resources these files do not declare, and the undeclared knob
that changes it.