Safeguard
Product•

Scheduled scans with the Safeguard CLI: three ways that work today

The Safeguard CLI has no built-in scheduler. Use platform schedules, a signed-in runner, or cron and CI. Real commands, exit codes and the traps to avoid.

Safeguard Security Team
6 min read

The Safeguard CLI does not have a scheduler of its own. Nightly scans work three ways today: the platform schedules jobs for a signed-in local runner, a connected repository is rescanned by the platform, or you run safeguard scan from cron or a CI schedule. Each has different limits on where results end up.

This guide sticks to what the CLI documentation states. If a behavior is not documented, we say so rather than guess.

Which of the three ways fits you?

  • Platform schedule, platform scan. You connect a repository as an integration. The platform scans it and evaluates your policies and gates. Safeguard's docs list scan triggers of on push, scheduled (daily, weekly or custom) and on release. Nothing runs in your pipeline.
  • Platform schedule, local runner. For private code you do not want to give a cloud service access to, the runner built into the CLI picks up scan jobs scheduled from the platform and scans on your machine. Only the SBOM and findings are uploaded, never source.
  • Your own schedule. Cron, a CI schedule or any scheduler calls the CLI directly. Results are files on disk.

Pick the first when OAuth access to the repository is fine. Pick the second when source must stay local. Pick the third when you want results in your own storage or tooling.

How do you run the local runner?

Install the CLI with the official installer, then open the interactive terminal and sign in:

curl -fsSL https://cli.safeguard.sh/install | bash
safeguard

Inside the terminal, type /login. Your browser opens, you approve the session, and the runner starts polling the platform for jobs assigned to that machine. All traffic is outbound HTTPS. For the schedule to run, the machine has to be on and the terminal session has to be running, so use a machine that stays up. The docs say the runner reports its state in the bar above the input, and the slash commands status and jobs show scan status and recent history.

If you would rather read the installer first, the docs suggest downloading it to a file and reading it before running it.

How do you schedule it yourself?

The command line scan writes JSON result files under an output folder. It takes one target:

safeguard scan --dir . --output ./output

On a fresh CI container or server that has never opened the interactive terminal, the scan needs syft and grype available, because the terminal normally downloads them on first run. Put both on the PATH, or point the TOOLS_DIR variable at a folder that holds them. The docs also say to pin the CLI version in CI, using the SAFEGUARD_VERSION variable with the installer.

Here is a small nightly wrapper that handles the documented exit codes. The scan returns 0 when it ran, whatever it found, 1 when the command itself failed, and 2 when results were written but the vulnerability scan did not run:

#!/usr/bin/env bash
cd /srv/checkouts/app || exit 1
OUT="/var/log/safeguard/$(date +%F)"
safeguard update
safeguard scan --dir . --output "$OUT"
code=$?
if [ "$code" -eq 2 ]; then
  echo "results written but the vulnerability scan did not run" >&2
  exit 2
elif [ "$code" -ne 0 ]; then
  echo "safeguard scan failed" >&2
  exit 1
fi

Then add a cron line, here for 02:00 every day:

0 2 * * * /usr/local/bin/nightly-scan.sh

The update command refreshes the vulnerability databases, so run it before the scan. For CI, put the same two commands in a job triggered by your provider's schedule instead of a push.

What do the exit codes not tell you?

Exit 0 does not mean no vulnerabilities. The docs say safeguard scan does not fail on findings and has no severity threshold flag. If you want a nightly job to alert, compare tonight's result files with last night's using your own script. The docs do not give the JSON layout for this guide, so read a sample file from your own run first. For fail-on-policy behavior, the docs point to platform gates and a CI command that waits for the gate result and exits non-zero when it fails.

Where do cron results end up?

On disk, in the output folder. The docs say that to see results in the platform alongside your other projects, you scan from the interactive terminal while signed in, and the runner uploads the SBOM and findings. A bare cron scan does not do that on its own. If you want both, use the runner for the platform record and keep the cron job for local archives.

What are the common traps?

  • A laptop that sleeps will miss its schedule.
  • A token stored on one machine does not authorize another. The docs say the CLI has no API key variable, so each machine signs in once.
  • A missing syft or grype produces exit code 2 or a failure on fresh containers.
  • Old vulnerability data gives false comfort, so run the update first.
  • Output folders that overwrite themselves leave no history. Use a dated folder.

What to do

  1. Decide between platform scans, the local runner and your own schedule.
  2. Install the CLI and pin a version for any unattended job.
  3. Run safeguard doctor once on the machine and read its output.
  4. Sign in on the machine that will run the runner.
  5. For cron or CI, install syft and grype and handle exit codes 1 and 2.
  6. Keep dated result folders and diff them each night.

Quick check

Does the Safeguard CLI have a built-in schedule command?

No. Use platform-scheduled jobs with the local runner, or call the scan from cron or a CI schedule.

What does exit code 2 from safeguard scan mean?

Results were written but the vulnerability scan did not actually run. Treat it as a failure, not a clean pass.

Sources

Last reviewed: 18 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.