Triggers and schedules in GitHub Actions, with every event in one table
What starts a workflow and how to say when. Triggers, activity types, filters, cron, and manual runs, then one table of every event GitHub fires, what fires by default, and what narrows it.
Nothing runs until something starts it. This page is the on block: the events, how to narrow them, the clock, the button, and a table of every event GitHub has.
⚡ Trigger
The events that start a run. No trigger, no run, ever.
- Reach for it when you want a run at all. Every workflow names at least one event, and most name two or three.
- Lives under
on, at the top of the file: one event, a list of them, or a mapping that narrows each one.
on:
push:
branches: [main]
pull_request:
workflow_dispatch:
Gets you in trouble when
- The file has no
on. GitHub reads it, runs nothing, and only complains once you open that workflow in the Actions tab. - A
pull_requestcomes from a fork. That run gets no secrets, on purpose, so a step that needs one fails there. - You switch to
pull_request_targetto get the secrets back. It runs your base branch's workflow with your token; check out the fork's code in it and the fork now holds your token. - You name the event and stop. Each one fires on its own default activities, rarely all of them; the next term says how to choose.
In the editor it is the When it runs panel: one card per trigger, Add a trigger for the catalogue, Turn off to drop one.
🎯 Activity types
Narrowing an event to the moments you care about: opened, closed, labeled.
- Reach for it when an event fires more often than you want to pay for: an issue workflow that should run on
labeledand nothing else. - Lives under the event as
types, a list. Naming none is not all of them; each event has its own default set, and the table below has every one.
on:
issues:
types: [opened, labeled]
pull_request:
types: [opened, synchronize, reopened, ready_for_review]
Gets you in trouble when
- You write no
typesonpull_requestand expect a review to run it. Its default is opened, synchronize, reopened; the other eighteen wait to be named. - You name a type the event does not have. GitHub refuses the file, and the editor's findings say which one.
- You pick
editedonissues. Every saved change to a title or body fires it, which is more often than it sounds.
In the editor they are the Which activities? chips on a trigger card; a dot marks the ones GitHub picks when you pick none.
🔍 Filters
Narrowing further by branch, tag, or path.
- Reach for it when a push to
docs/should not run the test suite, or onlymainshould deploy. - Lives under
pushasbranches,tags, andpaths, underpull_requestasbranchesandpaths, each with an-ignoretwin.workflow_runtakesbranchesandworkflows.
on:
push:
branches: [main, "release/**"]
paths-ignore: ["**.md", "docs/**"]
Gets you in trouble when
- You write
branchesandbranches-ignoreon the same event. GitHub takes one or the other; put a!pattern in one list instead. tagsis filled in andbranchesis not. Only tag pushes run it then; an empty branch list is not every branch.pathsskips a pull request that touches nothing listed, and a required check waits on it forever. Run something small on those paths instead.- You put a filter on
issues. It takes none; only the events with something in the last column of the table do.
In the editor each is a token field on the trigger card, Branches, Tags, Files changed; Only these or All but these picks which list it writes.
⏰ Cron
A five-field clock for running on a schedule.
- Reach for it when nothing in GitHub marks the moment: a nightly build, a sweep every fifteen minutes.
- Lives under
scheduleas a list ofcronentries: minute, hour, day of month, month, day of week. Cron is UTC unless atimezonebeside it says otherwise; your Tuesday 9am is not GitHub's.
on:
schedule:
- cron: "7-59/15 * * * *"
- cron: "17 3 * * 1-5"
timezone: Europe/London
Gets you in trouble when
- The workflow is on a branch. A scheduled run only ever runs on the default branch, which surprises everyone once.
- The minute is 0. Every cron on GitHub fires on the hour, the queue is longest there, and a run is delayed or dropped; pick an odd minute.
- You reckon in local time. Cron is UTC, and it will not follow daylight saving with you.
- The interval is under five minutes. Five is the shortest GitHub runs.
- Nobody pushes for sixty days. GitHub switches the schedule off on a public repository, and the Actions tab has a button to switch it back on.
In the editor it is the On a timer card: Runs at holds the cron, read back in words beneath it, and every preset sits off the hour.
🖱️ Manual
Starting a run by hand, with inputs you declare.
- Reach for it when a person should choose the moment: a deploy, a one-off sweep, or a workflow you are still writing.
- Lives under
workflow_dispatch, withinputsnaming what the form asks: each astring,boolean,choice,number, orenvironment. A step reads one asinputs.name.
on:
workflow_dispatch:
inputs:
environment:
description: Where to deploy
type: choice
options: [staging, production]
default: staging
dry_run:
description: Print what would change and stop
type: boolean
default: true
Gets you in trouble when
- The Run workflow button is missing. GitHub shows it once the file is on the default branch; the branch picker on it then chooses what runs.
- You start it with
gh workflow runand skip a required input. The call is refused; give it a default, or pass it with-f. - You compare a
booleaninput with the text'true'. It is a real boolean underinputs, soinputs.dry_run == 'true'is false whatever the box said; compare withtrue, or use it bare.
In the editor it is the Run it by hand card and its Inputs rows: name, what it is for, type, default, required. Start a run at the top dispatches the saved file with them.
📋 Event reference
Every event the editor's catalogue offers, in its order: what fires it, the activity types it fires by default and then the rest you can name, and the filters it takes.
The three retired classic project events are left out here, as the catalogue leaves them out.
| Event, and what fires it | Activity types, defaults first | Filters |
|---|---|---|
workflow_dispatch Someone presses Run workflow in the repository's Actions tab. |
none | none |
schedule Runs on a cron schedule, read in UTC. |
none | none |
workflow_call Another workflow calls this one and passes it inputs. |
none | none |
repository_dispatch Something outside GitHub posts an event to start this workflow. |
none | none |
issues An issue is opened, labelled, assigned, closed, or otherwise touched. |
All by default: opened, edited, deleted, transferred, pinned, unpinned, closed, reopened, assigned, unassigned, labeled, unlabeled, locked, unlocked, milestoned, demilestoned, typed, untyped, field_added, field_removed | none |
issue_comment A comment lands on an issue or on a pull request. |
All by default: created, edited, deleted | none |
discussion A discussion is opened, answered, or edited. |
All by default: created, edited, deleted, transferred, pinned, unpinned, labeled, unlabeled, locked, unlocked, category_changed, answered, unanswered | none |
discussion_comment A comment lands on a discussion. |
All by default: created, edited, deleted | none |
label A label is created, edited, or deleted in the repository. |
All by default: created, edited, deleted | none |
milestone A milestone is created, closed, edited, or deleted. |
All by default: created, closed, opened, edited, deleted | none |
pull_request A pull request is opened, gets new commits, or is closed. |
By default opened, synchronize, reopened. Also assigned, unassigned, labeled, unlabeled, edited, closed, converted_to_draft, ready_for_review, locked, unlocked, milestoned, demilestoned, review_requested, review_request_removed, auto_merge_enabled, auto_merge_disabled, enqueued, dequeued | branches, branches-ignore, paths, paths-ignore |
pull_request_review Someone approves, requests changes, or comments on the whole pull request. |
All by default: submitted, edited, dismissed | none |
pull_request_review_comment Someone comments on a line of a pull request's diff. |
All by default: created, edited, deleted | none |
pull_request_target Like a pull request, but running the base branch's code with its secrets. Never check out the fork's code here. |
By default opened, synchronize, reopened. Also assigned, unassigned, labeled, unlabeled, edited, closed, converted_to_draft, ready_for_review, locked, unlocked, review_requested, review_request_removed, auto_merge_enabled, auto_merge_disabled | branches, branches-ignore, paths, paths-ignore |
merge_group A group of pull requests is queued to merge together. |
All by default: checks_requested | none |
push Commits land on a branch or a tag. |
none | branches, branches-ignore, tags, tags-ignore, paths, paths-ignore |
create Someone creates a branch or a tag. |
none | none |
delete Someone deletes a branch or a tag. |
none | none |
gollum Someone creates or updates a page in the repository's wiki. |
none | none |
branch_protection_rule A branch protection rule is created, edited, or deleted. |
All by default: created, edited, deleted | none |
release A release is published, edited, or deleted. |
All by default: published, unpublished, created, edited, deleted, prereleased, released | none |
registry_package A package is published or updated in the registry. |
All by default: published, updated | none |
deployment Something creates a deployment. |
none | none |
deployment_status A deployment posts a new status. |
none | none |
page_build GitHub Pages rebuilds the site. |
none | none |
workflow_run A workflow you name is requested, starts, or completes. |
By default requested, completed. Also in_progress | workflows, branches, branches-ignore |
check_run One check is created, asked for again, or completes. |
All by default: created, rerequested, completed, requested_action | none |
check_suite A suite of checks is requested or completes. |
All by default: completed, requested, rerequested | none |
status A commit gets a new status from an integration. |
none | none |
fork Someone forks the repository. |
none | none |
watch Someone stars the repository. |
none | none |
public A private repository is made public. |
none | none |
In the editor the catalogue behind Add a trigger offers the same list in the same order, a star on the ones most workflows use.
📚 Straight from GitHub
- Events that trigger workflows, the reference the table above follows.
- on, and types for the activities.
- Filters, and the filter pattern cheat sheet.
- schedule, the cron event, and its syntax.
- workflow_dispatch, its inputs, and running a workflow by hand.
- pull_request_target, and why a fork's code never runs under it.