Skip to content
fwozenstart free

guide

You cannot schedule GitHub branch protection.

There is no schedule field, on branch protection rules or on the rulesets that replaced them, and four years of requests have not changed that. Here is what GitHub does give you, the four things teams build instead, and the exact commands for the one that is genuinely free.

Every GitHub behaviour below last verified 2026-09-01.

the short answer

Can you schedule a GitHub branch protection rule?

No. A ruleset is active or disabled, plus evaluate where GitHub offers it, and none of those has a time dimension. Nothing inside GitHub moves a ruleset between them at 17:00 on a Friday and back at 09:00 on a Monday. The scheduling has to come from outside: a person, or something calling the REST API on a timer.

This is a model problem rather than a missing checkbox. Rulesets are configuration, and configuration describes a state rather than a schedule. That is also why no amount of looking through the settings page finds the field.

GitHub community discussions #16796, #157044 and #180858 collect four years of requests for a scheduled or time-boxed freeze. Nothing has shipped. Lock branch, which arrived in October 2022, is adjacent and is not the same thing.

the statuses, and what each one does

  • activethe ruleset is enforced from the moment it is created.
  • evaluatenothing is enforced, and Rule Insights records what would have been blocked. The REST API accepts it everywhere; the repository settings page offers it on Enterprise Cloud, and the Free and Pro pages list only the other two. This is how to test a freeze rule before a release week rather than during one.
  • disabledneither enforced nor evaluated.

Where several rulesets target the same branch, GitHub aggregates them and the most restrictive version of a rule applies. There is no priority ordering to reason about.

do it free first

How to flip a ruleset on a timer, for free

Two API calls and a workflow. This is the honest version of the free path, including the credential it needs, which is the part usually left out.

Step 1: create the ruleset, disabled

Require a status check that never reports, and leave the bypass list empty. Create it disabled so that creating it does not freeze anything, then note the id the response carries.

freeze-ruleset.json
{
  "name": "release-freeze",
  "target": "branch",
  "enforcement": "disabled",
  "conditions": {
    "ref_name": { "include": ["refs/heads/main"], "exclude": [] }
  },
  "bypass_actors": [],
  "rules": [
    {
      "type": "required_status_checks",
      "parameters": {
        "strict_required_status_checks_policy": false,
        "required_status_checks": [{ "context": "freeze" }]
      }
    }
  ]
}
create it once, keep the id
gh api -X POST repos/acme/storefront/rulesets \
  --input freeze-ruleset.json --jq '.id'

# freeze, later
gh api -X PUT repos/acme/storefront/rulesets/12345 \
  -f enforcement=active

# thaw
gh api -X PUT repos/acme/storefront/rulesets/12345 \
  -f enforcement=disabled

Every field on the update call is optional, so sending enforcement alone changes the status and leaves the rules where they are.

Step 2: put it on a schedule

One workflow, two crons, and a manual trigger so you can thaw without pushing YAML into a frozen repository. Times are UTC, which is its own trap in a country that changes its clocks.

.github/workflows/freeze.yml
name: freeze
on:
  schedule:
    - cron: '0 22 * * 3'   # freeze, Wed 22:00 UTC
    - cron: '0 14 * * 2'   # thaw, Tue 14:00 UTC
  workflow_dispatch:
    inputs:
      enforcement:
        type: choice
        options: [active, disabled]

jobs:
  set:
    runs-on: ubuntu-latest
    steps:
      - env:
          # NOT the built-in GITHUB_TOKEN: it cannot be
          # granted repository administration.
          GH_TOKEN: ${{ secrets.RULESET_ADMIN_TOKEN }}
          WANT: ${{ inputs.enforcement ||
            (github.event.schedule == '0 22 * * 3'
              && 'active' || 'disabled') }}
        run: |
          gh api -X PUT \
            repos/${{ github.repository }}/rulesets/12345 \
            -f enforcement="$WANT"
verify, always, from the API
gh api \
  repos/acme/storefront/rules/branches/main \
  --jq '.[] | {type, source: .ruleset_source}'

The settings page shows what you configured. This endpoint shows what applies to the branch, inherited organisation rulesets included. If you take one command from this page, take that one.

the alternatives

The four things teams build instead

Ranked with where each one is right. Three of the four are code you then maintain, and you maintain it during an incident, which is when maintaining things is hardest.

option 1

A calendar reminder and a human with admin rights

worksFree, no code, nothing to maintain. For one repository and one reliable admin this is the correct answer and you should stop reading.

costs youSomebody has to be awake, available, and an admin at 17:00 on a Friday. It stops scaling somewhere around ten repositories. The organisation audit log records that the ruleset changed and who changed it; the reason, the intended end date, and any exception you granted live in a chat thread.

option 2

A scheduled workflow that calls the REST API

worksFree, versioned, reviewable, and it puts the window in the repository where people can read it. With one repository and a strong CI culture this is reasonable.

costs youThe schedule trigger runs no more often than every five minutes, is delayed under high load, and GitHub says some queued jobs may be dropped, so the exact moment a freeze starts is not yours to pick. The built-in GITHUB_TOKEN cannot change a ruleset, so you own a personal access token or a GitHub App credential and its rotation. In a public repository the workflow is disabled after 60 days with no activity. And turning the freeze off in an emergency means pushing YAML into a repository you have just frozen.

option 3

A required check that only passes when a file says so

worksNo admin token, no API calls, and the freeze state is a file in the repository that anybody can read.

costs youThe check re-evaluates on pull request events, so a pull request that went green before the freeze is still green afterwards unless you fan the result out to every open pull request yourself. Merge queues and forks each need their own handling. And a permanently pending check trains a whole team to ignore pending checks, which is a cost you pay for the rest of the year.

option 4

A dedicated app, this one

worksThe window is a schedule with a timezone attached, the result is written to every open pull request and merge-group head, exceptions carry a reason and an expiry, and the log has an actor on every row. Scheduled and recurring freezes are in the free tier.

costs youIt is another app with access to your organisation, and it costs money from repository six. That is why every permission and every API call it makes is published line by line.

questions

Questions this page exists to answer.

Can you schedule a GitHub branch protection rule?

No. Neither branch protection rules nor the rulesets that replaced them have a time dimension. A ruleset is active or disabled, plus evaluate where GitHub offers it, and nothing in GitHub moves it between those states at a time you pick. The scheduling has to come from outside: a person with a calendar reminder, or a scheduled workflow that flips the ruleset through the REST API. The second one is a script you then own.

What enforcement statuses can a GitHub ruleset have?

Active, which enforces the ruleset on creation, and disabled, which does not. A third, evaluate, enforces nothing and records what would have been blocked on the Rule Insights page; the REST API accepts it everywhere, and the repository settings page offers it on Enterprise Cloud rather than on Free and Pro. Where you have it, evaluate is the safe way to find out what a freeze rule would have caught before a release week rather than during one.

Can a GitHub Actions workflow turn branch protection on and off?

It can, but not with the token it is given. The permissions: block in workflow syntax has no administration key, so GITHUB_TOKEN cannot update a ruleset. You need a personal access token or a GitHub App installation token with repository administration write, stored as a secret, owned by somebody, and rotated. That credential is the part of the free option people forget to price.

How precise is a cron-scheduled freeze?

Not very. The schedule trigger runs at most once every five minutes, is delayed when GitHub Actions is under load, and the documentation says some queued jobs may be dropped when the load is high enough. The top of every hour is named as a busy time. If your freeze has to start at exactly 17:00, cron is the wrong mechanism and you should start it early.

Does locking a branch work as a scheduled freeze?

Lock branch makes the branch read-only and stops it being deleted, which does block merges. It is still a switch a person flips, so it is a workaround for the enforcement half and not for the scheduling half. It is also all or nothing: there is no per-pull-request exception, so the hotfix on the Friday night means unlocking the branch for everybody.

Planning a specific window? The Black Friday checklist and the 2026 calendar have the dates.