Skip to content

Scheduling Scans

Scan schedules in HarborGuard are configured per registry, not globally. Each registry has a scanning policy with a schedule mode, the scanner set to use, and tag include/exclude filters.

Schedule modes

ModeBehavior
manualNo automatic scans. Triggered only by UI button or API.
dailyRe-scan tags whose most recent scan is older than 24 hours.
weeklyRe-scan tags whose most recent scan is older than 7 days.
on_pushScan a tag the first time it is observed in the registry catalog.

A scan job is only enqueued when no PENDING or IN_PROGRESS scan already exists for that tag, so safe to run frequently. Dedup is enforced via a per-tag advisory lock at insertion time.

The schedule field stores one of those preset modes. Free-form cron expressions are not currently accepted - if you need finer granularity (e.g. "every 6 hours", "weekdays at 2am"), the recommended pattern is to drive it from your own scheduler and call POST /api/scans on the cadence you want.

Conceptual cron equivalents

If you are driving scans externally, useful cron expressions:

0 2 * * *       # daily at 02:00
0 2 * * 1       # weekly Monday 02:00
0 */6 * * *     # every 6 hours
0 2 * * 1-5     # weekdays 02:00
0 2 1 * *       # 1st of each month 02:00

Pair each with a curl call to POST /api/scans - see CI/CD.

Tag filters

Each registry policy supports include and exclude glob filters:

FieldExampleMeaning
tagIncludeprod-*,v[0-9]*Comma-separated globs. Defaults to *.
tagExclude*-rc,*-beta,sha-*Comma-separated globs. Empty by default.

A tag must match an include and not match any exclude to be eligible for scheduled scans.

On-push scans and registry webhooks

on_push mode scans a tag the first time HarborGuard sees it. Two ways HarborGuard sees new tags:

  1. Native webhook - the registry calls HarborGuard when a tag is pushed.
  2. Polling - HarborGuard syncs the catalog on a schedule and detects new digests.

Webhook support varies by registry. The honest state of the world:

RegistryNative push webhookHarborGuard support
Docker HubYes (paid plans)Webhook + polling
GitHub Container Registry (GHCR)Via repo package eventsWebhook + polling
AWS ECRYes (EventBridge)Polling (EventBridge bridge is user-configured)
Google Artifact Registry / GCRVia Pub/SubPolling (Pub/Sub bridge is user-configured)
Azure Container RegistryYes (webhook config)Webhook + polling
Quay.ioYesWebhook + polling
HarborYesWebhook + polling
Generic OCINo standardPolling only

For registries without a native webhook integration, polling is what you get. Polling cadence is the registry's catalog sync interval, not the scan schedule itself.

Configuring a schedule

UI: Registries -> [registry] -> Settings -> Scanning policy.

API: PUT /api/registries/{id} with the scanning block:

{
  "scanning": {
    "schedule": "daily",
    "scanners": ["trivy", "grype", "syft"],
    "tagInclude": "prod-*,v[0-9]*",
    "tagExclude": "*-rc,*-beta",
    "method": "cloud"
  }
}

method is "cloud" or "sensor" and selects the scan origin (see Sensor Architecture).

Verifying the schedule fires

The Scans page filter origin: cloud plus status: PENDING shows queued work. After a scheduler tick you should see one new PENDING row per eligible tag, then IN_PROGRESS, then COMPLETED. If the count stays at zero, check:

  • Tags actually exist in the registry catalog (the Images page lists them).
  • Tag filters are not excluding everything.
  • The registry's scanning.method matches an available origin (a sensor must be online and bound for method: "sensor").

On this page