What's here:

Configuring a Home File Server on Linux Mint

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