Setting Up ClamAV Antivirus for Mail
August 28, 2026
ClamAV is an open-source antivirus engine that scans email attachments for malware. On a mail server, it integrates with Rspamd to scan every incoming message before delivery. Malicious attachments are rejected or quarantined before reaching the user's inbox. This post covers installing ClamAV on Gentoo, configuring the daemon and automatic signature updates, and integrating with Rspamd.
Installing ClamAV
On Gentoo
emerge -av app-antivirus/clamav
ClamAV installs two services:
- clamd — the scanning daemon that stays in memory for fast scanning
- freshclam — the signature update daemon that downloads new virus definitions
Configuring ClamAV
clamd Configuration
Edit /etc/clamav/clamd.conf:
# Logging
LogFile /var/log/clamav/clamd.log
LogTime yes
LogVerbose no
LogSyslog no
# Socket
LocalSocket /var/run/clamav/clamd.sock
LocalSocketGroup clamav
LocalSocketMode 666
# Scanning limits
MaxFileSize 25M
MaxScanSize 100M
MaxRecursion 16
MaxFiles 10000
# Performance
MaxThreads 12
ReadTimeout 180
IdleTimeout 30
# Self-check interval (seconds)
SelfCheck 3600
# Database directory
DatabaseDirectory /var/lib/clamav
# Run as clamav user
User clamav
Key Settings
LocalSocket — ClamAV listens on a Unix socket rather than a TCP port. This is faster and more secure since only local processes can connect. Rspamd connects to this socket for scanning.
MaxFileSize 25M — files larger than 25MB are skipped. This prevents ClamAV from consuming excessive memory on large attachments. Adjust based on your message_size_limit in Postfix.
MaxScanSize 100M — the maximum amount of data to scan per message (including all attachments after extraction). This limits memory usage for messages with many attachments.
MaxThreads 12 — limits concurrent scanning threads. Each thread consumes memory for the virus signature database (typically 200-400MB). On a server with 4GB RAM, 12 threads is reasonable.
SelfCheck 3600 — ClamAV checks its own database for corruption every hour. This catches issues with failed signature updates.
freshclam Configuration
Edit /etc/clamav/freshclam.conf:
# Logging
UpdateLogFile /var/log/clamav/freshclam.log
LogTime yes
LogVerbose no
# Database directory
DatabaseDirectory /var/lib/clamav
# Update source
DatabaseMirror database.clamav.net
# Update checks per day
Checks 12
# Run as clamav user
DatabaseOwner clamav
# Notify clamd after update
NotifyClamd /etc/clamav/clamd.conf
Key Settings
Checks 12 — freshclam checks for new virus definitions 12 times per day (every 2 hours). New virus signatures are released multiple times daily, so frequent checks are important.
NotifyClamd — after downloading new signatures, freshclam tells clamd to reload its database. This ensures new virus definitions take effect immediately without restarting the daemon.
DatabaseMirror database.clamav.net — the official ClamAV mirror network. Uses geographic DNS to route to the nearest mirror.
Creating Required Directories
mkdir -p /var/log/clamav
mkdir -p /var/run/clamav
mkdir -p /var/lib/clamav
chown clamav:clamav /var/log/clamav /var/run/clamav /var/lib/clamav
Initial Signature Download
Before starting clamd, download the initial virus signature database:
freshclam
This downloads approximately 300MB of virus definitions. The first download takes several minutes depending on your connection speed.
Verify the databases were downloaded:
ls -lh /var/lib/clamav/
You should see main.cvd, daily.cvd, and bytecode.cvd.
Starting the Services
On Gentoo
rc-update add clamd default
rc-update add freshclam default
rc-service clamd start
rc-service freshclam start
On Debian
systemctl enable clamav-daemon clamav-freshclam
systemctl start clamav-daemon clamav-freshclam
Integrating with Rspamd
Rspamd has a built-in ClamAV integration module. Create /etc/rspamd/local.d/antivirus.conf:
clamav {
action = "reject";
scan_mime_parts = true;
scan_text_mime = false;
scan_image_mime = false;
symbol = "CLAM_VIRUS";
type = "clamav";
log_clean = false;
servers = "/var/run/clamav/clamd.sock";
patterns {
JUST_EICAR = '^Eicar-Test-Signature$';
}
}
Configuration Decisions
action = "reject" — messages containing detected viruses are rejected immediately. The sender receives a bounce indicating the message was rejected for malware. An alternative is "rewrite subject" which marks the subject but still delivers the message — this is less safe but useful during testing.
scan_mime_parts = true — scan individual MIME parts (attachments) separately. This is more thorough than scanning the entire message as a single blob.
scan_text_mime = false and scan_image_mime = false — skip scanning plain text and image attachments. Viruses are rarely embedded in plain text or standard image files, and scanning them adds unnecessary overhead.
log_clean = false — don't log every clean message. With high mail volume, this would generate enormous log files.
Rspamd Score for Viruses
Create /etc/rspamd/local.d/antivirus_group.conf to set the virus detection score:
symbols = {
"CLAM_VIRUS" {
weight = 999.0;
description = "ClamAV virus found";
one_shot = true;
}
}
A weight of 999 ensures the message exceeds the reject threshold regardless of other scores.
Restart Rspamd to load the antivirus module:
rc-service rspamd restart
Testing ClamAV
Test the Daemon
clamdscan /etc/hostname
This should return:
/etc/hostname: OK
Test Virus Detection
Use the EICAR test file — a standardized test string that all antivirus engines detect:
echo 'X5O!P%@AP[4\PZX54(P^)7CC)7}$EICAR-STANDARD-ANTIVIRUS-TEST-FILE!$H+H*' > /tmp/eicar.txt
clamdscan /tmp/eicar.txt
This should return:
/tmp/eicar.txt: Win.Test.EICAR_HDB-1 FOUND
Clean up:
rm /tmp/eicar.txt
Test via Rspamd
Rspamd's ClamAV integration scans MIME attachments, not inline message text. The defaults are scan_mime_parts = true, scan_text_mime = false, and scan_image_mime = false — this means an EICAR string in the message body won't trigger ClamAV. This is intentional: real malware arrives as file attachments, not inline text.
To test properly, send the EICAR string as an attachment:
# Create the EICAR test file
python3 -c "print('X5O!P%@AP[4\\\\PZX54(P^)7CC)7}\$EICAR-STANDARD-ANTIVIRUS-TEST-FILE!\$H+H*', end='')" > /tmp/eicar.txt
# Send as an attachment via mail (requires mailx with attachment support)
echo "Virus attachment test" | mail -s "EICAR test" -a /tmp/eicar.txt user@example.com
# Clean up
rm /tmp/eicar.txt
Check the mail log for rejection:
grep "CLAM_VIRUS" /var/log/rspamd/rspamd.log | tail -5
The message should be rejected with the CLAM_VIRUS symbol. If the EICAR string is only in the message body (not an attachment), ClamAV won't scan it and no CLAM_VIRUS symbol will appear — this is expected behavior.
Monitoring
Check ClamAV Version and Signature Status
clamd --version
Or through the socket:
clamdscan --version
Check Freshclam Log
tail -20 /var/log/clamav/freshclam.log
Look for successful update entries. If you see "mirror is not synchronized" messages, wait and try again — mirrors sometimes lag behind.
Check Memory Usage
ClamAV's signature database consumes significant memory. Check the resident memory:
ps aux | grep clamd
Typical memory usage is 300-600MB depending on the database size. If this is too much for your server, consider reducing MaxThreads or running ClamAV only during low-traffic periods.
Troubleshooting
clamd Fails to Start
If clamd exits immediately after starting, the most common cause is missing virus databases. Run freshclam manually and check for errors:
freshclam --verbose
If you see Can't connect to port 80 of host database.clamav.net, your server may have outbound HTTP restrictions. ClamAV uses HTTP (port 80) for downloads, not HTTPS.
Socket Permission Errors
If Rspamd cannot connect to the ClamAV socket, verify the socket permissions:
ls -la /var/run/clamav/clamd.sock
The LocalSocketMode 666 setting in clamd.conf allows any local user to connect. If you prefer tighter permissions, add the clamav user to the rspamd group (or vice versa) and set LocalSocketMode 660.
High Memory Usage
ClamAV loads the entire virus signature database into memory. If the server is memory-constrained, reduce the thread count:
MaxThreads 2
Fewer threads mean slower scanning during concurrent mail delivery, but memory usage drops significantly. Each thread shares the same loaded database, so the base memory cost (300-400MB) remains constant.
Clamd Stops Responding
If clamd becomes unresponsive, check the log for out-of-memory kills:
dmesg | grep -i "killed process"
tail -50 /var/log/clamav/clamd.log
On memory-constrained servers, the Linux OOM killer may terminate clamd. Set OOMScoreAdjust=-500 in the service configuration to reduce the likelihood of clamd being killed over other processes.
Summary
After completing these steps:
- ClamAV scans every incoming email attachment for malware
- Virus signatures update automatically 12 times per day
- Detected viruses are rejected immediately before delivery
- Rspamd integrates with ClamAV via the Unix socket
- The EICAR test confirms both ClamAV and the Rspamd integration work
The mail server now has comprehensive spam and virus protection. The next step is PostfixAdmin for managing domains, mailboxes, and aliases through a web interface.