Safeguard
Developer Tools•

How do you set up a nightly vulnerability scan in GitHub Actions?

A step by step nightly scan workflow: cron off the top of the hour, a manual trigger, a concurrency guard and a rule for what fails the job.

Safeguard Security Team
3 min read

Set up a nightly scan with a workflow that has a schedule trigger, a manual trigger, a concurrency group and one scan step whose exit code decides the result. Use an off-peak minute, remember the job runs on the default branch in UTC, and send failures somewhere a team will see them.

The workflow

name: nightly-scan
on:
  schedule:
    - cron: "23 3 * * *"
  workflow_dispatch:

concurrency:
  group: nightly-scan-${{ github.workflow }}
  cancel-in-progress: false

jobs:
  scan:
    runs-on: ubuntu-latest
    timeout-minutes: 30
    steps:
      - uses: actions/checkout@v4
      - name: Run dependency scan
        run: ./scripts/scan.sh

The scan script is yours: it calls whichever scanner you use and exits non zero when findings cross your threshold.

Why each part is there

LineReason
cron: "23 3 * * *"Runs at 03:23 UTC. GitHub says the start of each hour is high load and scheduled runs can be delayed, so avoid minute zero.
workflow_dispatchLets you test the job without waiting for night.
concurrencyA group allows one running job at a time, so a slow run cannot overlap the next.
cancel-in-progress: falseLets the running scan finish. A newer waiting run replaces any older pending one.
timeout-minutesStops a hung scan from burning hours.

These behaviours are described in GitHub's documentation on schedule events and concurrency.

Steps

  1. Put the file in the default branch. Scheduled workflows only run there, using the latest commit.
  2. Pick a minute that is not 0, 15, 30 or 45.
  3. Decide the time zone. UTC is the default, and GitHub allows an optional IANA time zone setting.
  4. Run it once by hand with the manual trigger and read the output.
  5. Decide what fails the job. Start with critical findings that have a fix available, then tighten later.
  6. Route failures to a channel, and add a check that the job actually ran each night. See how to detect a silently failing scheduled scan.

What nightly does not cover

A nightly job on the default branch checks one branch at one time of day. It does not check release branches, forks or code that merged after the run. Add a pull request scan for the first, and extra branch checkouts inside the job for the second. If you maintain old versions, see scanning release branches on a schedule.

Quick check

Why is my workflow not running at local midnight?

The default time zone is UTC. Convert your target time, or set the time zone option.

Can I use daily as a cron shortcut?

No. GitHub documents that the at-sign shortcuts are not supported. Write five field cron syntax.

Sources

Last reviewed: 19 October 2026.

Never miss an update

Weekly insights on software supply chain security, delivered to your inbox.

Self-healing security runs on Safeguard.

Your first fix PR is minutes away.

No sales call required, even your agent can complete the purchase over MCP.