Skip to content

Monitor a GitLab CI scheduled pipeline

Scheduled pipelines fail in ways nobody sees. A job starts failing on a branch nobody watches, the schedule is deactivated when its owner leaves the project, or a runner goes offline and the pipeline sits pending. A heartbeat check catches all of these: the pipeline reports to Pingstack after it succeeds, and Pingstack alerts you when that report doesn’t arrive on time.

1. Create a heartbeat check that matches the schedule

Section titled “1. Create a heartbeat check that matches the schedule”

Open the project’s Build › Pipeline schedules and find the schedule’s interval pattern and timezone. GitLab lets you choose the timezone per schedule.

Create a heartbeat check and set its schedule to Cron with the same pattern and timezone. See schedules and timezones.

Set a generous grace period. Scheduled pipelines can start a few minutes late, and the pipeline itself takes time. Allow for both, plus time waiting for a runner. Attach a notification group.

Copy the location’s ping URL from the check in Pingstack. In the project, open Settings › CI/CD › Variables and add PINGSTACK_PING_URL with Mask variable turned on, so it’s hidden in job logs.

If you also mark it Protected, it’s only available to pipelines on protected branches. Make sure the schedule’s target branch is protected, or the report job won’t see the variable.

Add a stage that runs after everything else, only for scheduled pipelines:

stages:
- export
- report
nightly-export:
stage: export
script:
- ./scripts/export.sh
rules:
- if: $CI_PIPELINE_SOURCE == "schedule"
report-to-pingstack:
stage: report
image:
name: curlimages/curl:8.10.1
entrypoint: [""]
script:
- curl --fail --silent --show-error --max-time 10 --retry 3 "$PINGSTACK_PING_URL" > /dev/null
rules:
- if: $CI_PIPELINE_SOURCE == "schedule"
when: on_success

The empty entrypoint lets GitLab run the script in the curl image, whose default entrypoint is curl itself. Jobs in a later stage run only when every job in the earlier stages succeeded, so a failed run never reaches report-to-pingstack and Pingstack alerts you when the deadline passes.

If one project has several schedules, give each its own check and variable, and tell the jobs apart with a variable set on the schedule (for example SCHEDULE_NAME) in their rules.

In Build › Pipeline schedules, choose Run (the play button) next to the schedule. When the pipeline finishes, open the check in Pingstack and confirm the location received a heartbeat and shows a next deadline.

Pass a count from an earlier job to the report job as a dotenv artifact, then send it as a numeric value:

nightly-export:
stage: export
script:
- echo "ROWS=$(./scripts/export.sh --count)" >> export.env
artifacts:
reports:
dotenv: export.env
rules:
- if: $CI_PIPELINE_SOURCE == "schedule"
report-to-pingstack:
stage: report
image:
name: curlimages/curl:8.10.1
entrypoint: [""]
script:
- curl --fail --silent --show-error --max-time 10 --retry 3 --get --data-urlencode "value=$ROWS" "$PINGSTACK_PING_URL" > /dev/null
rules:
- if: $CI_PIPELINE_SOURCE == "schedule"

Set warning and critical thresholds on the check, so a run that succeeds but exports nothing still raises an alert.

  • Deactivated schedule. GitLab deactivates a schedule when its owner can no longer run it, for example after leaving the project. Take ownership of the schedule and reactivate it.
  • No runner. A pipeline waiting for an available runner starts late, or never. Check that a runner with the right tags is online.
  • Protected variable. The report job can’t see a protected variable on an unprotected branch, so curl gets an empty URL and fails.
  • Timezone mismatch. The check and the schedule’s timezone must agree.

If alerts still don’t match what you expect, see troubleshooting monitoring.