GitHub·7 min read·

Events that Trigger Workflows: Push, PR, Schedule, Manual

Master every GitHub Actions trigger — push, pull_request, schedule and workflow_dispatch — and pick the right event, branch and path filters for your pipeline.

NB

Netbay Developer Relations

Netbay Engineering

On this page

Every GitHub Actions workflow starts because something happened. That something is an event, and mastering the event system is the difference between a pipeline that runs precisely when you want and one that fires constantly or never at all. The good news is the trigger model is small: push, pull requests, schedules, manual dispatch, and a handful of platform events. This post breaks down each so you can pick the right one — and combine them safely.

Push: the most common trigger

The on: push trigger fires the workflow whenever code is pushed to the repository. By default it runs on every branch and tag. You almost always want to narrow it with branches or paths so CI does not run on every internal commit. Use the branches filter to limit to protected branches like main, and paths to limit to directories that actually changed.

A common mistake is triggering CI on pushes to every branch and then also running it on every pull request, which doubles work. Decide whether branches get CI or whether all review happens through pull requests, then filter to match.

Pull request events

The pull_request trigger fires on the lifecycle events of a pull request: opened, synchronize, reopened, and the rest. The default (no specific types) handles the common cases. Critical detail: a push trigger runs against the code just pushed, while a pull_request trigger runs against the merge result — the commits from the PR merged into the base branch. That is why you test pull_request for integration checks that need the combined state.

Schedule: cron-based runs

The schedule trigger uses standard cron syntax and is great for nightly dependency updates, cache refresh, or regular smoke tests. It can be finicky: schedules only run on the default branch, and the hour is interpreted in UTC. There is no guarantee of exact timing — GitHub may delay scheduled runs under load — so never design a workflow that must fire at a precise second.

Manual and on-demand events

workflow_dispatch lets a human start the workflow from the Actions tab, optionally passing inputs. It is indispensable for tasks like "re-run prod migration" or "rollback to a tag." You can define inputs on the workflow and they appear as a form in the UI. Combined with the ability to choose a ref before running, it gives you a genuine manual trigger when automation needs a human in the loop.

Combining triggers with branches and paths

Here is a workflow that runs CI on pushes to main and on every pull request, plus a nightly maintenance job, plus a manual deploy:

yaml
name: Pipeline
on:
  push:
    branches: [main]
    paths: ['src/**', '.github/workflows/**']
  pull_request:
  schedule:
    - cron: '0 3 * * *'
  workflow_dispatch:
    inputs:
      environment:
        description: target environment
        required: true
        default: staging

jobs:
  ci:
    runs-on: ubuntu-22.04
    steps:
      - uses: actions/checkout@v4
      - run: echo building
  nightly:
    if: github.event_name == 'schedule'
    runs-on: ubuntu-22.04
    steps:
      - run: echo running nightly maintenance
  deploy:
    if: github.event_name == 'workflow_dispatch'
    runs-on: ubuntu-22.04
    steps:
      - run: echo deploying to ${{ github.event.inputs.environment }}

Each event carries its own context

Every trigger populates a different github context. In a push it is github.event_name == 'push' and github.ref holds the branch. In a pull request you get github.event.pull_request with number, title, and base. In a schedule you get github.event.schedule. Using github.event_name inside if conditions lets one workflow act differently depending on how it was started, as shown by the nightly and deploy guards above.

A workflow can log which event started it, which is handy when the same file fires on several triggers:

yaml
name: Event Aware
on:
  push:
    branches: [main]
  pull_request:
  workflow_dispatch:

jobs:
  info:
    runs-on: ubuntu-22.04
    steps:
      - if: github.event_name == 'push'
        run: echo "started by push to ${{ github.ref }}"
      - if: github.event_name == 'pull_request'
        run: echo "started by PR #${{ github.event.pull_request.number }}"
      - if: github.event_name == 'workflow_dispatch'
        run: echo "started manually by ${{ github.actor }}"

The webhook events beyond the big four

Beyond push, pull_request, schedule and workflow_dispatch, GitHub can trigger on many webhook events: issues, issue_comment, release, watch (stars), workflow_run (chaining), and repository_dispatch. The most useful for CI is workflow_run, which lets you run a follow-up workflow after another workflow completes — including workflows in the same repo. Keep most pipelines to the four core triggers and reserve the event-specific ones for genuinely event-shaped work.

push pull_request schedule dispatch github.event_name selects behavior if: condition guards run job only when matched

Takeaway

Pick the trigger that describes the real event, not the one that is easiest to write. Narrow push and pull_request by branch and path, keep schedules for maintenance, and use workflow_dispatch for anything a human might need to start with inputs. If you are testing event-heavy pipelines, your cloud test bed matters — a fast Netbay VPS makes an excellent target for webhook-driven deploys, so spin one up and point GitHub at it via netbayhosts.in.

Keep reading

Follow along on a real VPS

Deploy Linux in under 60 seconds

These guides are written against Ubuntu, Debian, and RHEL-family images — the same ones on NetBay.

Deploy an instance