Host Scanner
The Scanner is a separate component from the Sensor. The Sensor is the always-on agent that monitors runtime activity. The Scanner runs periodically, reads the host filesystem, and builds the software inventory (SBOM) that vulnerability findings are based on.
How the Scanner Works
- The Scanner runs as
upwind-agent-scanner.service, started byupwind-agent-scanner.timer. - The timer fires every hour. Each time it fires, the Scanner checks the age of the last SBOM and runs a new scan only if it is older than 24 hours.
- A scan starts at the filesystem root (
/) and covers everything that is not excluded. Refer to Scanner Exclusions for what is excluded by default.
Resource Usage
CPU, memory, and disk IO usage during a scan is expected. Depending on the size of the host, the number of files, and the available resources, a scan takes anywhere from a few minutes to several hours. In unusual cases, scans have taken longer than 24 hours.
If a host shows sustained disk IOPS, high iowait, or steady CPU usage, the Scanner is the most likely cause, not the Sensor. To confirm, check whether a scan is running:
systemctl status upwind-agent-scanner.service
To see when recent scans started and finished:
journalctl -u upwind-agent-scanner.service --since "2 days ago"
While a scan runs, the Scanner logs a Scan progress line every minute with the percentage complete, the elapsed time, and an estimate of the time remaining:
journalctl -u upwind-agent-scanner.service | grep "Scan progress" | tail -5
Resource Limits
The Scanner and the Sensor run as separate systemd services with separate resource limits, and each has its own set of environment variables:
| Service | Environment variable prefix |
|---|---|
upwind-agent.service (Sensor) | UPWIND_AGENT_*, for example UPWIND_AGENT_CPUQUOTA, UPWIND_AGENT_MEMORYHIGH, UPWIND_AGENT_MEMORYMAX |
upwind-agent-scanner.service (Scanner) | UPWIND_SCANNER_*, for example UPWIND_SCANNER_CPUQUOTA, UPWIND_SCANNER_IOWEIGHT, UPWIND_SCANNER_MEMORYHIGH, UPWIND_SCANNER_MEMORYMAX |
UPWIND_AGENT_* variables have no effect on the Scanner. To limit the resources the Scanner uses, set the UPWIND_SCANNER_* variables.
The variables map to systemd resource controls:
| Setting | What it does |
|---|---|
| CPUQuota | Hard cap on CPU time. 100% equals one full CPU core, so the default of 50% limits the service to half a core. |
| CPUWeight | Relative share of CPU when the host is under contention. The systemd default is 100, so the Scanner default of 25 gives it lower priority than most services. It does not cap usage on an idle host. |
| IOWeight | Relative share of disk IO when the disk is under contention. Like CPUWeight, it lowers priority but does not cap IOPS or throughput. |
| MemoryHigh | Throttling threshold, not a reservation and not a hard limit. Above this point, the kernel reclaims the service's memory more aggressively, which slows it down. Setting it below the Scanner's real working set causes continuous reclaim, which increases disk IO and makes scans take longer. |
| MemoryMax | Hard limit. If the service goes above it, the kernel kills the process (OOM kill). |
To change the limits, set the variables and re-run the configuration step as described in Editing Configuration After Installation.
Capping Scanner Disk IO
IOWeight only lowers the Scanner's priority, so on an otherwise idle disk the Scanner can still use as many IOPS as it wants. To set a hard cap, add a systemd drop-in for the Scanner service with IOReadIOPSMax or IOReadBandwidthMax. Do not edit the unit file itself, the installer regenerates it on upgrade. Drop-ins are preserved.
sudo mkdir -p /etc/systemd/system/upwind-agent-scanner.service.d
sudo tee /etc/systemd/system/upwind-agent-scanner.service.d/io-limits.conf <<'EOF'
[Service]
IOReadIOPSMax=/ 500
IOReadBandwidthMax=/ 50M
EOF
sudo systemctl daemon-reload
Each setting takes a path and a limit. When the path is not a block device, systemd applies the limit to the device that holds the filesystem the path is on. Add a line for each filesystem the Scanner reads, for example /data if it is a separate volume. Refer to systemd.resource-control for the full syntax.
A cap does not reduce the amount of data a scan reads, so scans take longer. On a large host, a scan that takes longer than 24 hours delays the next one.
The drop-in takes effect from the next scan. It is not removed by sensor.sh uninstall. Delete the directory manually if needed.
Host Scanner 2.0
The Host scanner has been significantly improved in memory efficiency with the Scanner 2.0 framework. This new version bypasses our previous algorithm's default directory and file traversal, implementing a custom process. Consequently, memory usage is considerably reduced and file size limits can be configured to prevent large files from being loaded into memory.
The Scanner skips files larger than max-file-size-to-scan, set in bytes in /etc/upwind/agent.yaml. The default is 1073741824 (1 GiB). For example, to skip files larger than 100 MiB:
max-file-size-to-scan: 104857600
Prerequisite:
- Sensor version >=
0.121.1
Scanner Exclusions
By default, the Scanner skips the following paths and filesystem types. You can add your own exclusions on top of these, as described in Adding Exclusions. To turn off the default path exclusions, set disable-default-excludes: true in /etc/upwind/agent.yaml.
Default Path Exclusions on Linux
System and pseudo-filesystems:
**/boot/***/dev/***/proc/***/run/***/sys/***/lost+found/*
Container runtime paths:
| Path | Used by |
|---|---|
**/var/lib/docker/overlayfs/* | Docker and the containerd snapshotter |
**/var/lib/docker/overlay2/* | Docker |
**/var/lib/containerd/io.containerd.snapshotter** | containerd |
**/var/lib/lxcfs/* | LXC-mounted containers |
**/var/lib/containers/* | CRI-O |
Database data directories:
**/var/lib/mysql/****/var/lib/postgresql/****/var/lib/pgsql/****/var/lib/mongodb/****/var/lib/mongo/****/var/lib/elasticsearch/****/var/lib/cassandra/****/var/lib/clickhouse/****/oradata/****/var/lib/redis/*
Other paths:
**/var/lib/prometheus***/opt/sentinelone/***/tmp/ansible**
Default Filesystem Type Exclusions
The Scanner skips mounts of these filesystem types, which covers network shares and virtual filesystems:
cifs, gpfs, nfs, nfs2, nfs3, nfs4, smb3, tracefs, fuse, fuse.lxcfs
To skip additional filesystem types, set exclude-mount-types in /etc/upwind/agent.yaml to a space-separated list in quotes.
Default Path Exclusions on Windows
**/Windows/Boot/***/Boot/***/pagefile.sys**/hiberfil.sys**/swapfile.sys**/System Volume Information/***/$Recycle.Bin/***/Windows/WinSxS/***/Windows/Installer/***/Windows/SoftwareDistribution/***/Windows/**/Temp/***/Users/*/AppData/Local/Temp/***/ProgramData/docker/windowsfilter/***/ProgramData/docker/containers/***/ProgramData/docker/image/***/ProgramData/containerd/***/Program Files/SentinelOne/***/ProgramData/Prometheus***/Windows/System32/config/***/Windows/System32/LogFiles/***/ProgramData/Packages/**/SystemAppData/***/Users/*/AppData/Local/Packages/**/SystemAppData/***/ProgramData/Microsoft/Windows/LfSvc/***/Users/*/AppData/Local/Comms/UnistoreDB/***/Users/*/AppData/Local/Packages/Microsoft.Windows.StartMenuExperienceHost***/Windows/AppCompat/Programs/***/Windows/ServiceProfiles/LocalService/***/Windows/ServiceProfiles/NetworkService/***/Users/*/AppData/**/WebCache/***/ProgramData/Microsoft/Crypto/***/Windows/System32/catroot2/***/ProgramData/Microsoft/Windows Defender/***/Windows/resources/Themes/*
Adding Exclusions
Add exclusions with the exclusions key in /etc/upwind/agent.yaml:
exclusions: "./data/** ./var/cache/build/** **/node_modules/**"
To apply the same exclusions across a fleet, manage agent.yaml with your configuration management tool, such as Ansible, Puppet, or Chef. The UPWIND_SCANNER_EXCLUSIONS environment variable is deprecated.
Follow these rules when you write exclusions:
- Separate entries with spaces. The value is a single space-separated list, not a YAML list.
- Quote the value. YAML reads a value that starts with
*as an alias, so an unquoted list that starts with a**/pattern fails to parse. - Paths are relative to the scan path. The default scan path is
/, so./data/**excludes/dataand everything under it. The leading./is required -data/**and/data/**do not match. This format comes from the open-source scanning libraries (Syft ) that the Scanner is built on. **/matches at any depth.**/data/**excludes every directory nameddataanywhere on the host, not only/data. Use./when you mean a specific path.
| Pattern | Excludes |
|---|---|
./data/** | /data and everything under it |
./var/lib/jenkins/workspace/** | Only /var/lib/jenkins/workspace and everything under it |
**/data/** | Every directory named data, at any depth - for example /data, /opt/app/data, and /home/user/project/data |
**/node_modules/** | Every node_modules directory on the host |
Prefer ./ paths. A broad **/ pattern such as **/etc/** also excludes directories you probably want scanned, such as application etc directories under /opt, which can hide vulnerable packages.
The Scanner reads agent.yaml each time a scan starts, so the next scan uses the new exclusions. No restart is needed. If your exclusions are overwritten, refer to Disabling Dynamic Values.
Choosing What to Exclude
Exclusions help most on hosts with very large files or very high file counts. Workloads that commonly need additional exclusions:
- Database hosts with data directories outside the default paths, for example on a dedicated mount such as
/dataor/mnt/db. - CI runners and build agents, such as Jenkins workspaces, GitLab Runner builds, and Docker build contexts.
- Artifact and build caches, such as Maven, Gradle, npm, and pip caches, or local artifact repositories.
- Log and data volumes, such as log archives, backups, and large dataset directories.
To find the directories that are likely to slow down a scan, look for the largest directories and the ones with the most files:
sudo du -xh --max-depth=3 / 2>/dev/null | sort -h | tail -20
for d in /*/ /var/lib/*/; do echo "$(sudo find "$d" -xdev 2>/dev/null | wc -l) $d"; done | sort -n | tail -20
Exclude directories that hold data rather than installed software. Do not exclude directories that contain applications or packages, such as /usr, /opt, or application install paths, because vulnerabilities in that software are no longer reported.
Disabling the Scanner
To disable the Scanner, for example to confirm whether it is causing load on a host, set enable-scans to false in /etc/upwind/agent.yaml:
enable-scans: false
The hostconfig service watches agent.yaml and disables the Scanner timer. A scan that is already running is allowed to finish. If enable-scans switches back to true on its own, refer to Disabling Dynamic Values. To stop it right away:
sudo systemctl stop upwind-agent-scanner.service
To disable the Scanner at install time, set UPWIND_SCANNER_ENABLED=false.
Don't disable upwind-agent-scanner.timer with systemctl alone. The hostconfig service manages the timer based on enable-scans, so it can enable the timer again.
The Scanner sends the host's software inventory (SBOM) to Upwind, where it is checked for vulnerabilities. While the Scanner is disabled, the host gets no new vulnerability findings unless Cloud Scanner covers it. The Sensor keeps running, and runtime detection is not affected.
To re-enable the Scanner, set enable-scans back to true. The hostconfig service enables the timer and starts a scan right away.
When to Use Cloud Scanner Instead
Tuning exclusions is workload-specific and ongoing: every new type of workload can need its own set. For hosts that are sensitive to extra load, such as production databases, or for fleets with many different workloads, use the Upwind Cloud Scanner instead of the host Scanner. Cloud Scanner scans snapshots of the host's volumes in your cloud account, so scans add no CPU, memory, or disk IO to the host.
To run the Sensor on a host without the Scanner, disable the Scanner as described in Disabling the Scanner and let Cloud Scanner cover the host. No additional configuration is needed.