--- title: ClamAV Fleet Deployment with Ansible domain: selfhosting category: security tags: - clamav - antivirus - security - ansible - fleet - cron status: published created: 2026-04-18 updated: 2026-04-18T11:13 --- # ClamAV Fleet Deployment with Ansible ## Overview ClamAV is the standard open-source antivirus for Linux servers. For internet-facing hosts, a weekly scan with fresh definitions catches known malware, web shells, and suspicious files before they cause damage. The key operational concern is CPU impact — an unthrottled `clamscan` will saturate a core for hours on a busy host. The solution is `nice` and `ionice` wrappers. > This guide covers deployment to internet-facing hosts. Internal-only hosts (storage, inference, gaming) are lower priority and can be skipped. ## What Gets Deployed - `clamav` + `clamav-update` packages (provides `clamscan` + `freshclam`) - `freshclam` service enabled for automatic definition updates - A quarantine directory at `/var/lib/clamav/quarantine/` - A weekly `clamscan` cron job, niced to background priority - SELinux context set on the quarantine directory (Fedora hosts) ## Ansible Playbook ```yaml - name: Deploy ClamAV to internet-facing hosts hosts: internet_facing # dca, majorlinux, teelia, tttpod, majortoot, majormail become: true tasks: - name: Install ClamAV packages ansible.builtin.package: name: - clamav - clamav-update state: present - name: Enable and start freshclam ansible.builtin.service: name: clamav-freshclam enabled: true state: started - name: Create quarantine directory ansible.builtin.file: path: /var/lib/clamav/quarantine state: directory owner: root group: root mode: '0700' - name: Set SELinux context on quarantine dir (Fedora/RHEL) ansible.builtin.command: cmd: chcon -t var_t /var/lib/clamav/quarantine when: ansible_os_family == "RedHat" changed_when: false - name: Deploy weekly clamscan cron job ansible.builtin.cron: name: "Weekly ClamAV scan" user: root weekday: "0" # Sunday hour: "3" minute: "0" job: >- nice -n 19 ionice -c 3 clamscan -r / --exclude-dir=^/proc --exclude-dir=^/sys --exclude-dir=^/dev --exclude-dir=^/run --move=/var/lib/clamav/quarantine --log=/var/log/clamav/scan.log --quiet 2>&1 | logger -t clamscan ``` ## The nice/ionice Flags Without throttling, `clamscan -r /` will peg a CPU core for 30–90 minutes depending on disk size and file count. On production hosts this causes Netdata alerts and visible service degradation. | Flag | Value | Meaning | |------|-------|---------| | `nice -n 19` | Lowest CPU priority | Kernel will preempt this process for anything else | | `ionice -c 3` | Idle I/O class | Disk I/O only runs when no other process needs the disk | With both flags set, `clamscan` becomes essentially invisible under normal load. The scan takes longer (possibly 2–4× on busy disks), but this is acceptable for a weekly background job. > **SELinux on Fedora/Fedora:** `ionice` may trigger AVC denials under SELinux Enforcing. If scans silently fail on Fedora hosts, check `ausearch -m avc -ts recent` for `clamscan` denials. See [selinux-fail2ban-execmem-fix](../../05-troubleshooting/selinux-fail2ban-execmem-fix.md) for the pattern. ## Excluded Paths Always exclude virtual/pseudo filesystems — scanning them wastes time and can trigger false positives or kernel errors: ``` --exclude-dir=^/proc # Process info (not real files) --exclude-dir=^/sys # Kernel interfaces --exclude-dir=^/dev # Device nodes --exclude-dir=^/run # Runtime tmpfs ``` You may also want to exclude large data directories (`/var/lib/docker`, backup volumes, media stores) if scan time is a concern. These are lower-risk targets anyway. ## Quarantine vs Delete `--move=/var/lib/clamav/quarantine` moves detected files rather than deleting them. This is safer than `--remove` — you can inspect and restore false positives. Review the quarantine directory periodically: ```bash ls -la /var/lib/clamav/quarantine/ ``` If a file is a confirmed false positive, restore it and add it to `/etc/clamav/whitelist.ign2`. ## Checking Scan Results ```bash # View last scan log cat /var/log/clamav/scan.log # Summary line from the log grep -E "^Infected|^Scanned" /var/log/clamav/scan.log | tail -5 # Check freshclam is keeping definitions current systemctl status clamav-freshclam freshclam --version ``` ## Verifying Deployment Test that ClamAV can detect malware using the EICAR test file (a harmless string that all AV tools recognize as test malware): ```bash echo 'X5O!P%@AP[4\PZX54(P^)7CC)7}$EICAR-STANDARD-ANTIVIRUS-TEST-FILE!$H+H*' \ > /tmp/eicar-test.txt clamscan /tmp/eicar-test.txt # Expected: /tmp/eicar-test.txt: Eicar-Signature FOUND rm /tmp/eicar-test.txt ``` ## See Also - [clamscan-cpu-spike-nice-ionice](../../05-troubleshooting/security/clamscan-cpu-spike-nice-ionice.md) — troubleshooting CPU spikes from unthrottled scans - [linux-server-hardening-checklist](linux-server-hardening-checklist.md) - [ssh-hardening-ansible-fleet](ssh-hardening-ansible-fleet.md)