UptimeHunt Docs
Cron Job Monitoring

Monitor GitHub Actions Scheduled Workflows

Ping UptimeHunt from a GitHub Actions workflow to monitor a scheduled run the same way you'd monitor a crontab entry.

Monitor GitHub Actions Scheduled Workflows

GitHub's schedule event runs a workflow on a cron-style cadence. UptimeHunt doesn't need to know anything about GitHub — the workflow just pings a cron monitor as one of its steps, and UptimeHunt's own grace period notices when a scheduled run doesn't happen at all (a disabled workflow, a repository archived, GitHub Actions being down — or GitHub's documented behavior of disabling schedules in repositories with no activity for 60 days).

Create the monitor

Create a cron monitor in Cron Monitoring with a cron expression matching the workflow's schedule. Two GitHub-specific sizing notes:

  • Timezone is UTC. GitHub Actions cron expressions are always evaluated in UTC — keep the monitor's timezone at UTC so the expected times line up.
  • Be generous with grace. GitHub explicitly documents that scheduled workflows can be delayed (and during periods of high load, dropped) — start times routinely drift several minutes past the cron minute. A grace period of 15–30 minutes avoids false "late" states for a workflow that is merely queued.

Copy the monitor's ping URL into an Actions secret so it never appears in the repository or logs:

  1. Settings → Secrets and variables → Actions in the GitHub repository.
  2. Add UPTIMEHUNT_PING_URL with the monitor's ping URL.

Ping from the workflow

Report the outcome with two final steps — if: success() / if: failure() gate them on the job's real result, and if: failure() still runs when an earlier step failed:

.github/workflows/nightly.yml
name: nightly-report
on:
  schedule:
    - cron: "0 3 * * *"

jobs:
  nightly:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - name: Generate the nightly report
        run: ./generate-nightly-report.sh

      - name: Report success to UptimeHunt
        if: success()
        run: curl -fsS -m 10 --retry 5 -o /dev/null "$UPTIMEHUNT_PING_URL"
        env:
          UPTIMEHUNT_PING_URL: ${{ secrets.UPTIMEHUNT_PING_URL }}
      - name: Report failure to UptimeHunt
        if: failure()
        run: curl -fsS -m 10 --retry 5 -o /dev/null "$UPTIMEHUNT_PING_URL/fail"
        env:
          UPTIMEHUNT_PING_URL: ${{ secrets.UPTIMEHUNT_PING_URL }}

If the workflow also runs on other triggers (workflow_dispatch, push), gate the ping steps to the scheduled runs only, so a manual run never touches the monitor:

        if: success() && github.event_name == 'schedule'

Reporting a log excerpt

To attach the task's output as the run's log, capture it to a file and send the tail as the ping body — the same idiom as a plain crontab entry:

      - name: Generate the nightly report
        run: ./generate-nightly-report.sh 2>&1 | tee job.log

      - name: Report result to UptimeHunt
        if: always()
        run: |
          EXIT=0
          [ "${{ job.status }}" = "success" ] || EXIT=1
          curl -fsS -m 10 --retry 5 -o /dev/null \
            --data-raw "$(tail -c 2000 job.log 2>/dev/null)" \
            "$UPTIMEHUNT_PING_URL/$EXIT"
        env:
          UPTIMEHUNT_PING_URL: ${{ secrets.UPTIMEHUNT_PING_URL }}

job.status collapses the job to success/failure/cancelled rather than a real process exit code, so EXIT is a 0-or-1 stand-in — enough to drive the monitor's success/fail state without claiming an exit status the workflow doesn't have.

On this page