Scan Performance
Fixing slow scans, crashes from high concurrency, and file descriptor limits.
Port Scanning Slows Down or Crashes Target Hosts
Symptoms: Web interfaces on target hosts become unresponsive during scans; network feels slow; IoT devices or services with small listen backlogs (e.g. Pi-hole) stop responding; services require restart after scan.
Cause: The default port scan rate (500 pps) and batch size (200 ports) can overwhelm hosts with small TCP listen queues, especially on low-latency networks (e.g. LXC containers on the same Proxmox host).
Solutions:
Reduce scan_rate_pps and/or port_scan_batch_size in the discovery's Speed tab (Discover > Scan > Scheduled → edit discovery → Speed).
Recommended values:
| Scenario | scan_rate_pps | port_scan_batch_size |
|---|---|---|
| Sensitive network (IoT, Pi-hole, embedded) | 50–100 | 50–100 |
| Mixed IoT and servers | 100–200 | 100–150 |
| Robust servers only | 500 (default) | 200 (default) |
See Daemon Configuration for the full parameter reference.
Discovery Takes Hours
Symptoms: Discovery takes hours to complete.
Cause: Two common reasons:
-
Deep scanning: Deep scans perform a full 65,535-port TCP scan on every host, which takes significantly longer than light scans. By default, every third scan is deep. If your network has many active hosts, deep scans can take hours. See Light vs Deep Scanning for how scan intervals work and how to adjust or disable deep scans.
-
Non-interfaced subnets: Scanning subnets the daemon doesn't have a network interface on is orders of magnitude slower. On interfaced subnets, the daemon uses fast ARP scanning (seconds for a /24). On non-interfaced subnets, it falls back to TCP probing — scanning each IP individually. See Layer 2 vs Layer 3.
Diagnosis: Check the daemon's interfaced subnets on its card in Discover > Daemons, then compare to the subnets in the discovery under Discover > Scan > Scheduled. Any subnet not in the interfaced list is being scanned via slow TCP probing. See Managing Scan Targets for how to view and edit what a daemon scans.
If the slow subnet is not interfaced:
- Deploy a daemon on that segment for fast L2 scanning. See Planning Daemon Deployment.
- Shrink the subnet if it's larger than needed. A /16 is 65,536 IPs — if only a /24 within it has hosts, target that /24 instead.
- Remove it if you don't need visibility on that segment.
- Docker networks (e.g. 172.17.0.0/16) should not be scanned over the network. Docker container discovery queries the Docker API directly and takes seconds.
If the slow subnet is interfaced: The daemon should already be using ARP. If the subnet is reasonably sized, check concurrent_scans — a very low value serializes work across many hosts. See Concurrent Scans. If the subnet is large (/16 or bigger), see Large Interfaced Subnets below.
Large Interfaced Subnets
Symptoms: An interfaced subnet isn't fully scanned, or the daemon logs "ARP target list truncated".
Cause: To prevent excessive memory use and scan times, the daemon caps ARP targets per subnet at arp_scan_cutoff (default: /15, ~131,000 IPs). Subnets larger than this prefix are truncated — only the first ~131K IPs are ARP scanned.
Solution: Lower arp_scan_cutoff in the discovery's Speed tab to match your subnet size. Important: also increase arp_rate_pps to keep scan times reasonable.
Scan time estimates at different rates:
| Cutoff | IPs | At 50 pps (default) | At 500 pps | At 1000 pps |
|---|---|---|---|---|
| /15 (default) | ~131K | ~44 min/round | ~4 min/round | ~2 min/round |
| /14 | ~262K | ~87 min/round | ~9 min/round | ~4 min/round |
| /12 | ~1M | ~5.8 hrs/round | ~35 min/round | ~17 min/round |
| /10 | ~4M | ~23 hrs/round | ~2.3 hrs/round | ~70 min/round |
Each scan performs up to 3 ARP rounds (1 initial + 2 retries by default). Subsequent rounds only target non-responsive hosts, so they're typically much faster than the first.
Before increasing arp_rate_pps, check whether your switch has Dynamic ARP Inspection (DAI) enabled. High ARP rates can trigger rate limiting or port shutdown on managed switches. See Switch Rate Limiting (DAI).
Discovery Fails with "CONCURRENT_SCANS too high"
Symptoms: Daemon crashes or runs out of memory during scans
Solution: Reduce concurrent scans in daemon configuration:
Docker:
environment:
- SCANOPY_CONCURRENT_SCANS=10 # Reduce from defaultBinary:
scanopy-daemon --concurrent-scans 10 ...See Daemon Configuration for recommended values.
"Too Many Open Files" Error
Symptoms: Critical error scanning: Too many open files (os error 24) in daemon logs
Causes: System file descriptor limit is too low for the configured concurrent scans.
Solutions:
-
Reduce concurrent scans (easiest):
environment: - SCANOPY_CONCURRENT_SCANS=10 -
Increase system file descriptor limit:
# Check current limit ulimit -n # Increase temporarily ulimit -n 65535 # Increase permanently (add to /etc/security/limits.conf) * soft nofile 65535 * hard nofile 65535 -
For Docker: Add to your daemon container:
ulimits: nofile: soft: 65535 hard: 65535