Introduction
A home Linux file server often becomes the quiet workhorse of the household network. It stores documents, photos, installers, backups, archives, media, exported projects, and random files copied from Windows machines. When that same directory is shared to Windows 11 clients through Samba and published over HTTP by nginx, it becomes even more useful. It also becomes a place where malware, unwanted scripts, malicious documents, or infected downloads can sit unnoticed.
That is where ClamAV fits. ClamAV is not a replacement for good backups, careful permissions, patching, endpoint protection on Windows clients, or common sense. However, for a Linux Mint 21.3 home file server sharing /data/ through Samba and nginx, ClamAV provides a practical open-source antivirus layer. The official ClamAV documentation describes it as an open-source toolkit for detecting viruses, Trojans, malware, and other threats, with tools such as freshclam, clamd, clamscan, and clamdscan (Cisco Talos, n.d.-a).
Linux Mint 21.3 uses an Ubuntu Jammy package base, which makes the installation process familiar for Ubuntu-style administration (Linux Mint, n.d.).
The Target Design
The design in this article assumes a simple home server layout:
Windows 11 clients -> Samba share -> /data/
nginx web server -> HTTP access -> /data/
ClamAV daemon -> scheduled malware scans of /data/
freshclam -> automatic signature updates
rsync -> scheduled backup from /data/ to /data-backup/
systemd timers -> automation for scans and backups
The goal is to protect the shared file repository without making the server difficult to maintain. Files can arrive through Samba, be served later by nginx, and be scanned regularly by ClamAV. A separate scheduled rsync job keeps a local backup copy in /data-backup/.
Install ClamAV
Install the ClamAV packages:
sudo apt update
sudo apt install clamav clamav-daemon
The clamav package provides the basic scanner tools. The clamav-daemon package provides clamd, the scanning daemon. ClamAV documentation explains that clamdscan sends scan requests to clamd, while daemon behavior is controlled largely through clamd.conf (Cisco Talos, n.d.-b).
Enable and start the services:
sudo systemctl enable --now clamav-freshclam
sudo systemctl enable --now clamav-daemon
Check status:
systemctl status clamav-freshclam
systemctl status clamav-daemon
The clamav-freshclam service updates virus signature databases. The clamav-daemon service runs the scanner daemon.
Confirm Signature Updates
A healthy freshclam log should show database updates such as:
daily.cvd updated
main.cvd updated
bytecode.cvd updated
Database test passed
Check the updater logs:
sudo journalctl -u clamav-freshclam -n 100
sudo tail -n 100 /var/log/clamav/freshclam.log
A warning such as the following usually means the updater could not notify the scanning daemon:
WARNING: Clamd was NOT notified: Cannot connect to clamd through /var/run/clamav/clamd.ctl
That normally means clamd is not installed, is not running, or its socket was not available when freshclam tried to notify it. Restart both services:
sudo systemctl restart clamav-daemon
sudo systemctl restart clamav-freshclam
Then verify:
systemctl status clamav-daemon
systemctl status clamav-freshclam
Check ClamAV Logging
ClamAV logs are usually found under:
/var/log/clamav/
Useful log commands:
ls -lh /var/log/clamav/
sudo tail -f /var/log/clamav/clamav.log
sudo tail -f /var/log/clamav/freshclam.log
sudo journalctl -u clamav-daemon -f
sudo journalctl -u clamav-freshclam -f
To confirm the configured log file:
grep -E '^(LogFile|LogSyslog|LogFacility|LogFileMaxSize)' /etc/clamav/clamd.conf
The clamd.conf file controls daemon behavior, including logging, socket settings, scan limits, recursion limits, and thread configuration (Canonical, n.d.-a).
Make systemctl Easier to Use
On many systems, systemctl status opens in a pager. To make systemd tools behave as though --no-pager was used, set SYSTEMD_PAGER=cat:
echo 'export SYSTEMD_PAGER=cat' >> ~/.bashrc
source ~/.bashrc
For root:
echo 'export SYSTEMD_PAGER=cat' >> /root/.bashrc
source /root/.bashrc
Test clamdscan
Start small before scanning the full share:
sudo clamdscan -v --fdpass /etc/hosts
Then test the shared directory:
sudo clamdscan -v --fdpass --multiscan /data/
Useful clamdscan options include the following:
|
Option |
Use |
|
-v, --verbose |
Shows more output during a scan. |
|
--fdpass |
Passes open file descriptors to clamd. This is useful when clamd runs as a different user. |
|
--multiscan |
Uses multiple daemon worker threads for directory scans. |
|
--log=FILE |
Writes scan output to a specified log file. |
|
--move=DIRECTORY |
Moves infected files to a quarantine directory. |
|
--copy=DIRECTORY |
Copies infected files to a quarantine directory while leaving the original in place. |
|
--remove |
Deletes infected files. Use cautiously on shared storage. |
|
--reload |
Asks clamd to reload signatures. |
|
--ping |
Checks whether clamd responds. |
The --fdpass option is especially useful on a local Linux server because clamdscan can open files and pass the file descriptors to the daemon. ClamAV documentation notes that --multiscan lets directory scans use multiple daemon worker threads, and when combined with --fdpass, clamdscan walks the directory tree and submits individual file scans to the daemon (Cisco Talos, n.d.-b).
A practical manual scan command is:
sudo clamdscan -v --fdpass --multiscan --log=/var/log/clamav/manual-data-scan.log /data/
Prepare Permissions for Samba, nginx, ClamAV, and Backups
A file server serving the same directory through Samba and nginx has multiple access paths. Each service needs only the access required for its role.
|
Component |
Access requirement |
|
Samba users or groups |
Write and read files through the Windows file share. |
|
nginx worker process |
Read files when serving static content over HTTP. |
|
ClamAV scanner |
Read files during scheduled malware scans. |
|
Backup process |
Read the source directory and write to the backup destination. |
A practical pattern is to use a shared Linux group for write access and ACLs for service read access.
Create a file-sharing group:
sudo groupadd fileshare
Set group ownership:
sudo chgrp -R fileshare /data/
sudo chmod -R 2775 /data/
The 2 in 2775 sets the setgid bit on directories, helping new files inherit the directory group.
Grant nginx read and directory traversal access:
sudo setfacl -R -m u:www-data:rx /data/
sudo setfacl -R -d -m u:www-data:rx /data/
Grant ClamAV read and directory traversal access:
sudo setfacl -R -m u:clamav:rx /data/
sudo setfacl -R -d -m u:clamav:rx /data/
Verify ACLs:
getfacl /data/
When scans are launched with sudo clamdscan --fdpass, the root-launched client can open files and pass them to clamd. Even so, explicit ACLs for the clamav user make future troubleshooting easier.
Example Samba Share
A simple Samba share for /data/ might look like this:
sudo nano /etc/samba/smb.conf
Example share definition:
[data]
path = /data
browseable = yes
read only = no
guest ok = no
valid users = @fileshare
force group = fileshare
create mask = 0664
directory mask = 2775
Restart Samba:
sudo systemctl restart smbd nmbd
Windows 11 clients can connect using:
\\server-name\data
\\server-ip-address\data
Samba also provides a vfs_virusfilter module for on-access antivirus scanning on Samba file services, but scheduled scans are simpler and easier to troubleshoot for a home server. The Samba documentation describes vfs_virusfilter as a stackable VFS module for scanning and filtering virus files on Samba file services (Samba Team, n.d.).
Example nginx Static File Share
nginx can publish /data/ over HTTP as static content. NGINX documentation explains that the root directive defines the filesystem path used to serve requested files (NGINX, n.d.).
Create a site file:
sudo nano /etc/nginx/sites-available/data-share
Example configuration:
server {
listen 80;
server_name server-name;
root /data;
location / {
autoindex on;
try_files $uri $uri/ =404;
}
}
Enable the site:
sudo ln -s /etc/nginx/sites-available/data-share /etc/nginx/sites-enabled/data-share
sudo nginx -t
sudo systemctl reload nginx
For a home LAN-only service, restrict access by subnet:
server {
listen 80;
server_name server-name;
root /data;
location / {
allow 192.168.1.0/24;
deny all;
autoindex on;
try_files $uri $uri/ =404;
}
}
Adjust the subnet to match the local network.
Schedule ClamAV Scans with systemd
A systemd timer is a clean way to schedule scans. Timers are unit files ending in .timer and can activate matching .service units on a calendar schedule. The systemd.timer documentation describes timer units and the use of calendar-based scheduling through timer configuration (freedesktop.org, n.d.).
Create the scan script:
sudo nano /usr/local/sbin/clamav-scan-data.sh
Paste:
#!/bin/bash
LOGFILE="/var/log/clamav/scheduled-data-scan.log"
LOCKFILE="/run/lock/clamav-scan-data.lock"
SCAN_TARGET="/data/"
{
echo "============================================================"
echo "ClamAV scheduled scan started: $(date)"
echo "Target: ${SCAN_TARGET}"
echo "============================================================"
} >> "$LOGFILE"
/usr/bin/flock -n "$LOCKFILE" /usr/bin/nice -n 10 /usr/bin/ionice -c2 -n7 /usr/bin/clamdscan -v --fdpass --multiscan --log="$LOGFILE" "$SCAN_TARGET"
EXIT_CODE=$?
{
echo "============================================================"
echo "ClamAV scheduled scan finished: $(date)"
echo "Exit code: ${EXIT_CODE}"
echo "Exit code meaning: 0=no virus found, 1=virus found, 2=error"
echo "============================================================"
echo
} >> "$LOGFILE"
exit "$EXIT_CODE"
Make it executable:
sudo chmod 750 /usr/local/sbin/clamav-scan-data.sh
Test it:
sudo /usr/local/sbin/clamav-scan-data.sh
sudo tail -n 50 /var/log/clamav/scheduled-data-scan.log
Create the service:
sudo nano /etc/systemd/system/clamav-data-scan.service
Paste:
[Unit]
Description=Scheduled ClamAV scan of /data
Wants=clamav-daemon.service
After=clamav-daemon.service
[Service]
Type=oneshot
ExecStart=/usr/local/sbin/clamav-scan-data.sh
Create the timer:
sudo nano /etc/systemd/system/clamav-data-scan.timer
Paste:
[Unit]
Description=Run scheduled ClamAV scan of /data
[Timer]
OnCalendar=*-*-* 02:00:00
Persistent=true
Unit=clamav-data-scan.service
[Install]
WantedBy=timers.target
Enable the timer:
sudo systemctl daemon-reload
sudo systemctl enable --now clamav-data-scan.timer
Check it:
systemctl list-timers --all | grep clamav
systemctl status clamav-data-scan.timer
systemctl status clamav-data-scan.service
Watch the scan log:
sudo tail -f /var/log/clamav/scheduled-data-scan.log
Persistent=true is useful on a home server because the timer can catch up after a missed run if the machine was powered off during the scheduled time (freedesktop.org, n.d.).
Add a Scheduled rsync Backup from /data/ to /data-backup/
Antivirus is not a backup strategy. A clean ClamAV scan only means malware was not detected by the available signatures and scan engine. A backup helps protect against accidental deletion, hardware failure, user mistakes, ransomware damage, failed upgrades, and administrative errors.
This example uses rsync to mirror the contents of /data/ into /data-backup/.
Create the destination directory:
sudo mkdir -p /data-backup
Run a first manual test:
sudo rsync -aHAX --delete --numeric-ids /data/ /data-backup/
The trailing slash on /data/ matters. It means copy the contents of /data/ into /data-backup/, rather than copying the /data directory itself as a subdirectory. The rsync manual documents archive mode, deletion options, and local-copy behavior (rsync project, n.d.).
The options used here are:
-a Archive mode
-H Preserve hard links
-A Preserve ACLs
-X Preserve extended attributes
--delete Delete destination files that no longer exist in the source
--numeric-ids Preserve numeric user and group IDs
Use --delete carefully. It keeps the destination as a mirror of the source. That is useful for a clean local mirror, but it also means deletions in /data/ will eventually be reflected in /data-backup/.
Create the rsync Backup Script
Create the script:
sudo nano /usr/local/sbin/rsync-backup-data.sh
Paste:
#!/bin/bash
SOURCE="/data/"
DESTINATION="/data-backup/"
LOGFILE="/var/log/rsync-data-backup.log"
LOCKFILE="/run/lock/rsync-data-backup.lock"
{
echo "============================================================"
echo "rsync backup started: $(date)"
echo "Source: ${SOURCE}"
echo "Destination: ${DESTINATION}"
echo "============================================================"
} >> "$LOGFILE"
/usr/bin/flock -n "$LOCKFILE" /usr/bin/nice -n 10 /usr/bin/ionice -c2 -n7 /usr/bin/rsync -aHAX --delete --numeric-ids --info=stats2 "$SOURCE" "$DESTINATION" >> "$LOGFILE" 2>&1
EXIT_CODE=$?
{
echo "============================================================"
echo "rsync backup finished: $(date)"
echo "Exit code: ${EXIT_CODE}"
echo "============================================================"
echo
} >> "$LOGFILE"
exit "$EXIT_CODE"
Make it executable:
sudo chmod 750 /usr/local/sbin/rsync-backup-data.sh
Test it manually:
sudo /usr/local/sbin/rsync-backup-data.sh
sudo tail -n 50 /var/log/rsync-data-backup.log
Optional Safety Check for a Mounted Backup Disk
If /data-backup/ is expected to be a separate mounted filesystem, add a mount check to the script before the rsync command:
if ! /usr/bin/mountpoint -q /data-backup; then
echo "ERROR: /data-backup is not a mount point. Backup aborted: $(date)" >> "$LOGFILE"
exit 2
fi
This prevents accidentally filling the root filesystem if an external or secondary disk is not mounted.
Schedule the rsync Backup with systemd
Create the service:
sudo nano /etc/systemd/system/rsync-data-backup.service
Paste:
[Unit]
Description=Scheduled rsync backup of /data to /data-backup
After=local-fs.target
[Service]
Type=oneshot
ExecStart=/usr/local/sbin/rsync-backup-data.sh
Create the timer:
sudo nano /etc/systemd/system/rsync-data-backup.timer
Paste:
[Unit]
Description=Run scheduled rsync backup of /data to /data-backup
[Timer]
OnCalendar=*-*-* 01:00:00
Persistent=true
Unit=rsync-data-backup.service
[Install]
WantedBy=timers.target
This schedules the backup for 1:00 AM. The ClamAV scan example runs at 2:00 AM, so the backup finishes first and the scan runs afterward.
Enable the timer:
sudo systemctl daemon-reload
sudo systemctl enable --now rsync-data-backup.timer
Check both scheduled jobs:
systemctl list-timers --all | egrep 'rsync|clamav'
Check the backup service:
systemctl status rsync-data-backup.timer
systemctl status rsync-data-backup.service
View backup logs:
sudo tail -f /var/log/rsync-data-backup.log
Backup First or Scan First?
A reasonable nightly schedule looks like this:
1:00 AM rsync /data/ to /data-backup/
2:00 AM ClamAV scan of /data/
This order gives the server a fresh backup before the antivirus scan runs. Another valid approach is to scan first and back up afterward, so detected files are not copied into the backup. There is no single perfect answer. For a home server, the most important point is that both jobs run consistently and produce logs.
A more cautious design is:
1:00 AM ClamAV scan of /data/
2:00 AM rsync /data/ to /data-backup/
That approach reduces the chance of copying suspicious files to the backup. However, if the scan takes a long time, the backup may overlap unless locks and scheduling are planned carefully.
Quarantine Instead of Delete
At first, avoid automatic deletion. A quarantine workflow is safer.
Create a quarantine directory:
sudo mkdir -p /var/quarantine/clamav
sudo chown clamav:clamav /var/quarantine/clamav
sudo chmod 700 /var/quarantine/clamav
To move detected files into quarantine, modify the scan command in the ClamAV script:
/usr/bin/clamdscan -v --fdpass --multiscan --move=/var/quarantine/clamav --log="$LOGFILE" "$SCAN_TARGET"
Avoid --remove until the behavior is well understood. The clamdscan documentation includes removal and quarantine-style options, but automatic deletion on a shared file server can create unnecessary operational risk (Canonical, n.d.-b).
Large Directories Require Patience
A large file share may appear to scan slowly. That is normal. File count matters as much as total size.
Estimate the size:
sudo du -sh /data/
sudo find /data/ -type f | wc -l
Watch ClamAV activity:
ps -eo pid,ppid,user,stat,pcpu,pmem,etime,cmd | egrep 'clamd|clamdscan'
sudo tail -f /var/log/clamav/scheduled-data-scan.log
Watch rsync backup activity:
ps -eo pid,ppid,user,stat,pcpu,pmem,etime,cmd | egrep 'rsync|rsync-backup'
sudo tail -f /var/log/rsync-data-backup.log
For disk I/O:
sudo apt install iotop
sudo iotop -oPa
Optional clamd Tuning
Check key daemon settings:
grep -E '^(MaxThreads|MaxQueue|MaxScanSize|MaxFileSize|MaxRecursion|LogFile|LocalSocket|User)' /etc/clamav/clamd.conf
For a modest home server:
MaxThreads 4
MaxQueue 100
For a larger server with more CPU and RAM:
MaxThreads 8
MaxQueue 200
Restart the daemon after changes:
sudo systemctl restart clamav-daemon
MaxThreads and related scan behavior are part of clamd.conf, which controls daemon-side scanning rather than the clamdscan command alone (Canonical, n.d.-a).
Should On-Access Scanning Be Enabled?
ClamAV supports on-access scanning on Linux through ClamOnAcc. ClamAV documentation describes this as real-time protection that can scan files when they are accessed and can optionally block access while scanning occurs (Cisco Talos, n.d.-c).
For a home file server, scheduled scanning is usually the better first step. It is easier to understand, easier to log, easier to troubleshoot, and less likely to interfere with Samba or nginx performance. On-access scanning can be added later after permissions, scan speed, and logging are understood.
Administration Cheat Sheet
Check ClamAV services:
systemctl status clamav-daemon
systemctl status clamav-freshclam
Update signatures manually:
sudo freshclam
Reload signatures into the daemon:
sudo clamdscan --reload
Run a manual scan:
sudo clamdscan -v --fdpass --multiscan --log=/var/log/clamav/manual-data-scan.log /data/
Check ClamAV timer:
systemctl list-timers --all | grep clamav
systemctl status clamav-data-scan.timer
systemctl status clamav-data-scan.service
Run a manual rsync backup:
sudo /usr/local/sbin/rsync-backup-data.sh
Check rsync timer:
systemctl list-timers --all | grep rsync
systemctl status rsync-data-backup.timer
systemctl status rsync-data-backup.service
View logs:
sudo tail -f /var/log/clamav/scheduled-data-scan.log
sudo tail -f /var/log/rsync-data-backup.log
sudo journalctl -u clamav-daemon -f
Final Recommendation
For a Linux Mint 21.3 home file server that shares /data/ through Samba to Windows 11 clients and through nginx over HTTP, the most maintainable design is:
· Install ClamAV and clamav-daemon.
· Run freshclam automatically.
· Run clamd automatically.
· Use clamdscan with --fdpass and --multiscan.
· Scan /data/ on a systemd timer.
· Back up /data/ to /data-backup/ with rsync on a systemd timer.
· Log both scans and backups.
· Quarantine suspicious files before considering deletion.
· Keep Samba, nginx, ClamAV, and backup permissions explicit.
This gives the server a practical security and recovery baseline. ClamAV helps detect known malware in the shared repository. rsync provides a local backup mirror. Samba and nginx continue serving the same data through their normal paths. The result is a home file server that remains simple, visible, and maintainable.
References
Canonical. (n.d.-a). clamd.conf(5): Configuration file for Clam AntiVirus daemon. Ubuntu Manpages. https://manpages.ubuntu.com/manpages/jammy/man5/clamd.conf.5.html
Canonical. (n.d.-b). clamdscan(1): Scan files and directories for viruses using Clam AntiVirus daemon. Ubuntu Manpages. https://manpages.ubuntu.com/manpages/jammy/man1/clamdscan.1.html
Cisco Talos. (n.d.-a). ClamAV documentation: Introduction. ClamAV Documentation. https://docs.clamav.net/
Cisco Talos. (n.d.-b). ClamAV documentation: Scanning. ClamAV Documentation. https://docs.clamav.net/manual/Usage/Scanning.html
Cisco Talos. (n.d.-c). ClamAV documentation: On-access scanning. ClamAV Documentation. https://docs.clamav.net/manual/OnAccess.html
freedesktop.org. (n.d.). systemd.timer: Timer unit configuration. https://www.freedesktop.org/software/systemd/man/systemd.timer.html
Linux Mint. (n.d.). New features in Linux Mint 21.3 Virginia. https://www.linuxmint.com/rel_virginia_whatsnew.php
NGINX. (n.d.). Serving static content. NGINX Documentation. https://docs.nginx.com/nginx/admin-guide/web-server/serving-static-content/
rsync project. (n.d.). rsync(1): Linux man page. https://download.samba.org/pub/rsync/rsync.1
Samba Team. (n.d.). vfs_virusfilter: On access virus scanner. Samba Documentation. https://www.samba.org/samba/docs/current/man-html/vfs_virusfilter.8.h