Skip to content
On this page

Examples

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 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; the last two bend it where the API forces their hand, as the Sections table notes:

  • Full-payload entries: a ruleset is applied with a full-payload PUT, so a key the entry omits but the live ruleset holds is drift in check and a refused write in apply; Semantics has the edges.
  • Labels and milestones work the other way: only the fields you declare are sent, so an omitted description or state is left alone.
  • Classic protection: 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).
  • actions: 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. Retention-, cache-, workflow-token-, or access-only declarations leave the base policy alone.

A minimal file

Enough to be useful on day one: a few repository fields and the labels you actually triage with.

yaml
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.

yaml
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.

Classic branch protection

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; check mode reports an omitted-but-live core key as drift before an apply would null it away.

yaml
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: null

Two 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.
  • 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.

yaml
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]

What null means

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.

yaml
pages: null

interaction_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.

yaml
interaction_limits: null

protection: null on a branch declares it unprotected, and apply removes existing classic protection.

yaml
branches:
  - name: legacy
    protection: null

A mode: render fold keeps all three meanings: a higher pages: null or interaction_limits: null is written as the section's value even over a lower layer's declaration, and a higher protection: null is the branch entry's value where the two branches lists union by name. A null never removes a key; to drop a lower layer's keyed entry, write _remove: true on it, and the layering guide has the rules.

A multi-repo defaults-file never merges into a target's file, so a null there keeps the meanings above.

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.

Notes in the file

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.

An underscore key is not an escape hatch: the underscore marks this action's three directives, _layering, _undeclared, and _remove (the layering guide owns them), and any other underscore key at the top level or on a list section's wrapper fails validation the same way. Inside a keyed entry, an underscore key is a field and follows the entry's open or closed shape. A note belongs in a YAML comment:

yaml
# owner: platform-team, see runbook RB-112
labels:
  - name: bug
    color: "d73a4a"

Where to go next

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, layering folds several files into one, and the undeclared policy explains what happens to live resources these files do not declare, and the _undeclared knob that changes it.

Built from main at 1a25adbSource: docs/start/examples.md