ClamAV
ClamAV on a Linux server is not the same proposition as antivirus on a Windows desktop. February is not going to be infected by a Windows ransomware payload. The threat model is different: the concern is that February might receive a malicious file through Nextcloud or the mail server and pass it on to someone running Windows, or that a file uploaded to Nextcloud by a user on a compromised machine gets shared to others before anyone notices.
ClamAV addresses this at the server level, scanning files before they travel further. It is not a silver bullet. Its detection rate for novel malware is lower than commercial solutions and its false positive rate on legitimate files is something to watch. But for a homelab running mail and file sharing, it earns its place.
Components
Three packages install together:
clamavprovides the scanning engine (clamscan) and the database updater (freshclam).clamav-daemonprovidesclamd, the persistent daemon that keeps the signature database loaded in memory. Scanning throughclamdusingclamdscanis significantly faster thanclamscan, which reloads the database on every run.clamav-freshclamis the automatic database update service, checking for new signatures multiple times a day.
Installation
sudo apt install clamav clamav-daemon clamav-freshclam
After installation, the signature database is empty. freshclam needs to run before clamd can start. Stop the freshclam service to run an initial manual update:
sudo systemctl stop clamav-freshclam
sudo freshclam
sudo systemctl enable --now clamav-freshclam
The initial download takes a few minutes: the main database, daily updates, and bytecode signatures together amount to around 300MB. Subsequent updates are incremental.
Confirm the database downloaded:
ls -lh /var/lib/clamav/
You should see main.cvd, daily.cvd, and bytecode.cvd.
Now start the daemon:
sudo systemctl enable --now clamav-daemon
Confirm it started:
sudo systemctl status clamav-daemon
The daemon loads the full signature database into memory on startup. This takes 30-60 seconds and uses around 500MB RAM. On a machine with limited memory, this is a meaningful cost. If February is tight on RAM, consider using scheduled clamscan runs via cron instead of keeping clamd running permanently. The tradeoff is that each scan takes longer.
clamd configuration
The clamd configuration file is /etc/clamav/clamd.conf. The Ubuntu package ships with a working default, but a few settings are worth reviewing.
Check the current configuration:
sudo grep -v '^\s*#\|^\s*$' /etc/clamav/clamd.conf
Key settings and their defaults on Ubuntu 24.04:
LocalSocket /run/clamav/clamd.ctl # Unix socket path
User clamav # Daemon runs as clamav user
LogFile /var/log/clamav/clamav.log # Log location
LogTime yes # Include timestamps in logs
MaxThreads 12 # Parallel scan threads
MaxFileSize 25M # Skip files larger than this
MaxScanSize 100M # Maximum data to scan per file
ScanArchive yes # Scan inside archives
ScanOLE2 yes # Scan Office documents
ScanPDF yes # Scan PDFs
ScanHTML yes # Scan HTML files
The defaults are reasonable for a homelab. One setting worth adjusting: MaxFileSize. The default of 25MB is low for a Nextcloud instance where users may upload large files. For Nextcloud scanning specifically, the file gets scanned at upload time regardless of size, but ClamAV will skip files above this threshold. Increase it if large file uploads need to be scanned:
# In /etc/clamav/clamd.conf
MaxFileSize 250M
MaxScanSize 1000M
After changing clamd.conf, restart the daemon:
sudo systemctl restart clamav-daemon
Quarantine directory
Never configure ClamAV to automatically delete infected files. False positives happen, and deleting a file is irreversible. Use a quarantine directory instead:
sudo mkdir -p /var/clamav/quarantine
sudo chown clamav:clamav /var/clamav/quarantine
sudo chmod 0750 /var/clamav/quarantine
When running scans, move infected files to quarantine rather than deleting them:
sudo clamdscan --infected --move=/var/clamav/quarantine /path/to/scan
Review quarantined files periodically. If a file is a false positive, restore it manually. If it is genuinely infected, delete it once you know what it was and how it got there.
Scheduled scans
ClamAV does not scan proactively by default. Scheduled scans via cron cover the most important directories on February: web roots, Nextcloud data, and mail queues.
Create a daily scan job:
sudo tee /etc/cron.d/clamav-scan << 'EOF'
# ClamAV daily scan
# Runs at 02:30 each night, sends mail to root if infections found
30 2 * * * clamav clamdscan --infected --recursive \
--move=/var/clamav/quarantine \
--log=/var/log/clamav/daily-scan.log \
/var/www \
/home \
/var/lib/nextcloud/data \
2>/dev/null; \
grep -c FOUND /var/log/clamav/daily-scan.log > /dev/null && \
mail -s "ClamAV: infections found on $(hostname)" root \
< /var/log/clamav/daily-scan.log
EOF
This runs as the clamav user, which has permission to use the clamd socket. The mail notification only fires if the word FOUND appears in the log. Adjust the paths to match what is actually on February.
Create the log file with correct permissions:
sudo touch /var/log/clamav/daily-scan.log
sudo chown clamav:clamav /var/log/clamav/daily-scan.log
Testing the setup
The EICAR test file is a standard harmless string that all antivirus software recognises as a test virus. Use it to confirm ClamAV is detecting correctly:
# Write the EICAR test string to a file
echo 'X5O!P%@AP[4\PZX54(P^)7CC)7}$EICAR-STANDARD-ANTIVIRUS-TEST-FILE!$H+H*' \
| sudo tee /tmp/eicar-test.txt
# Scan it with clamdscan
sudo clamdscan /tmp/eicar-test.txt
Expected output:
/tmp/eicar-test.txt: Eicar-Signature FOUND
----------- SCAN SUMMARY -----------
Known viruses: 8700000
Engine version: 1.4.x
Scanned directories: 0
Scanned files: 1
Infected files: 1
Clean up the test file:
sudo rm /tmp/eicar-test.txt
Allowing services to use the clamd socket
Services that need to submit files for scanning (Postfix via clamav-milter, or Nextcloud via an antivirus plugin) communicate with clamd through the Unix socket at /run/clamav/clamd.ctl. The socket is owned by the clamav group.
Add the service user to the clamav group to grant access:
# For www-data (nginx, Nextcloud)
sudo usermod -aG clamav www-data
# For postfix
sudo usermod -aG clamav postfix
Restart the relevant services after adding them to the group. Group changes do not take effect for already-running processes.
Confirm the socket permissions:
ls -la /run/clamav/clamd.ctl
freshclam configuration
freshclam runs as a systemd service and updates the database automatically. The configuration is at /etc/clamav/freshclam.conf. Default settings on Ubuntu 24.04 check for updates 24 times per day.
Adjust the check frequency if 24 times a day seems excessive for a homelab:
# In /etc/clamav/freshclam.conf
Checks 12
12 checks per day (every 2 hours) is the commonly recommended value and avoids being throttled by the ClamAV CDN, which blocks clients that check more than once per hour.
Confirm freshclam is running and the database is current:
sudo systemctl status clamav-freshclam
sudo freshclam --version
The version output shows the current database revision and its age. The daily database should not be more than a few days old.
Integration with Postfix
The mail server articles cover ClamAV integration with Postfix in detail. The integration uses clamav-milter, which connects clamd to Postfix’s milter interface and scans attachments before they are accepted by the mail server.
Install the milter package:
sudo apt install clamav-milter
Configuration is covered in the Postfix article.
Integration with Nextcloud
Nextcloud can scan uploaded files using ClamAV via its Antivirus for Files app. The app submits files to clamd via the Unix socket and rejects uploads that are flagged as infected.
Enable the app in Nextcloud’s app store, then configure it under Settings > Administration > Security to use the socket at /run/clamav/clamd.ctl. The www-data user must be in the clamav group as described above.
Memory usage
clamd keeps the full signature database in memory. On Ubuntu 24.04 with the current signature set, expect around 500MB RAM usage at idle. This is the main practical constraint on a homelab server with limited RAM.
Check current memory usage:
ps aux --sort=-%mem | grep clamd | head -2
If memory is constrained and clamd is causing problems, consider scheduling clamscan runs during low-usage hours instead. clamscan loads the database, scans, and exits, using memory only during the scan window. The trade-off is that Nextcloud and Postfix integration require a running daemon and cannot use clamscan.