Scale scan workers on queue age and backlog per priority lane, because CPU use alone lags and misleads. Add a minimum worker count, a maximum tied to your source host and registry limits, and cooldowns so the fleet does not thrash. Scale back down once queues drain.
Why CPU is the wrong lead signal
A scan worker spends time waiting: fetching code, pulling layers, calling a database mirror. CPU may be low while the queue grows. By the time CPU rises, developers are already waiting. Scale on the thing users feel, which is how long jobs wait.
Signals to use
| Signal | Use | Caveat |
|---|---|---|
| Oldest job age in the interactive lane | Primary trigger to add workers | Needs per-lane metrics |
| Backlog depth divided by throughput | Estimates time to drain | Throughput varies by scan type |
| Worker utilisation | Secondary check, to avoid idle spend | Not a trigger on its own |
| Failure rate from resource limits | Signals that worker class is too small | Scaling count will not fix it |
| Downstream limit headroom | Cap on scaling out | Adding workers can trigger rate limits |
Steps
- Emit queue depth and oldest job age per lane and scan type.
- Set a target for each lane, for example interactive jobs start within a few minutes.
- Scale out when oldest job age exceeds the target for a sustained short window.
- Scale in slowly, after queues have been empty for a longer window, and drain workers gracefully so they finish their current job.
- Keep a minimum number of warm workers, since cold starts include image pulls and database cache loading.
- Set a maximum based on how many parallel clients your source host, registry and database mirror can support, so autoscaling does not turn a backlog into an outage elsewhere.
- Use separate pools for heavy scan types so a few large jobs cannot hold every worker.
Mind the cost and the cache
Workers with warm caches are faster. Scaling to zero between bursts saves money but means each new worker re-downloads the database and layers. A shared cache volume or mirror reduces that penalty, which is another reason to put the vulnerability database behind an internal mirror.
Spot or preemptible capacity suits the background lane, because jobs are idempotent and can restart. Keep interactive workers on stable capacity.
Predictable peaks
Scan load is not random. Monday mornings, release days, and the hours after a big advisory all bring surges. Schedule extra capacity ahead of known peaks and use the reactive signals for the surprises.
Test it
Run a load test that enqueues a large number of jobs at once, and measure how quickly the fleet reaches steady state and how queue age behaves. Do this before a real advisory event does it for you.
Try it yourself
Click through it at your own pace, or open it full screen. A new disclosure is the sort of event that creates a burst of reactive scan work.
Safeguard is hosted, so capacity planning for scanning sits with us. You can try the free scan at https://gold.safeguard.sh/scan.
Quick check
Can I just scale on CPU?
It is a weak signal for jobs that wait on the network. Queue age is closer to what users experience.
Why cap the maximum workers?
More workers mean more calls to source hosts and registries, which have their own limits.
Last reviewed: 19 October 2026.