LinuxsecurityTested on real hardware

ClamAV Download Scan + Auto-Purge (Docker)

Run ClamAV in a container to scan your download folder, delete executables that masquerade as media, and quarantine infected files on a schedule.

Distrosubuntu, debian
Shellyaml
Updated
Compose file
yaml
# docker-compose.yml — ClamAV daemon scanning the download path
# Save as /opt/clamav/docker-compose.yml, then: docker compose up -d
services:
  clamav:
    image: clamav/clamav:latest
    container_name: clamav
    restart: unless-stopped
    volumes:
      - /srv/downloads:/scandir:ro          # your download folder (read-only view)
      - /opt/clamav/quarantine:/quarantine   # infected files moved here
      - /opt/clamav/db:/var/lib/clamav       # signature DB, persisted
    # clamd + freshclam run inside the image; freshclam auto-updates sigs

What this does

Torrent and Usenet clients don’t scan what they download, and Radarr/Sonarr only import video files (executables are ignored, never run) — but junk still lands in your download folder, and a fake .exe dressed up as a movie is a real thing. This playbook adds active protection on the download path:

  1. Runs ClamAV as a container with an auto-updating signature database.
  2. A cron job deletes executable/script file types from the media download subfolders (they have no business there).
  3. It then scans the remaining files and moves anything infected to a quarantine folder.

Why this approach

The media pipeline (Radarr/Sonarr + qBittorrent) only imports video files — executables, scripts, and other file types are ignored and never run by anything in the stack. So the practical malware risk is low. But “never run” isn’t “harmless”: a folder full of .exe files masquerading as movies accumulates over time and eventually someone might click on one in a file manager, or the files might be synced to a device that does run them. Cleaning the download folder proactively is cheap defense in depth.

The two-layer approach here — purge by file type first, then scan the remainder with ClamAV — is intentional. Purging executables is cheap and catches the obvious case without waiting for a signature match. ClamAV scanning catches more subtle infections in otherwise legitimate files (a video file with a known-malicious payload, for instance). Doing both means you’re not relying entirely on signature coverage, which lags behind new malware, and you’re also not relying on ClamAV’s resource-intensive scanner for things that are obviously wrong.

Running ClamAV as a container with a read-only mount of the scan directory means it has no ability to modify your media files directly — only the purge script running on the host touches files. This limits the blast radius of any bug in the scan logic: ClamAV can detect but not act, and the action (move to quarantine) is handled by a script you can audit and test separately.

What this stack is for
Everything in this guide automates managing and streaming media you have the rights to — your own rips, DRM-free purchases, home video, Linux ISOs, and public-domain or Creative Commons releases. Downloading copyrighted material you haven't paid for is illegal in most places, and it isn't what this guide teaches. What you point these tools at is on you.

Prerequisites

  • Docker + Docker Compose installed (install guide)
  • A few GB of RAM free — clamd holds the signature set in memory (~1.5 GB resident)
  • Paths adjusted: replace /srv/downloads with your actual download folder

Deploy ClamAV

Save the script above as /opt/clamav/docker-compose.yml, then:

bash

sudo mkdir -p /opt/clamav/quarantine /opt/clamav/db
cd /opt/clamav
sudo docker compose up -d

# First run downloads the signature DB (a few minutes). Watch it:
sudo docker logs -f clamav

The scan-and-purge script

Save as /opt/clamav/scan-and-purge.sh:

bash

#!/usr/bin/env bash
# Purge disguised executables, then scan+quarantine infected files.
set -euo pipefail

DL=/srv/downloads          # scope to media subfolders only, not the whole tree
LOG=/opt/clamav/scan.log

# 1. Delete executable / script types from the media download subfolders
find "$DL"/{movies,tv} -type f \
\( -iname '*.exe' -o -iname '*.scr' -o -iname '*.bat' -o -iname '*.cmd' \
 -o -iname '*.com' -o -iname '*.pif' -o -iname '*.msi' -o -iname '*.vbs' \
 -o -iname '*.js'  -o -iname '*.ps1' -o -iname '*.lnk' -o -iname '*.jar' \
 -o -iname '*.hta' -o -iname '*.wsf' -o -iname '*.reg' -o -iname '*.apk' \) \
-print -delete >> "$LOG" 2>&1 || true

# 2. Scan and move infected files to quarantine (paths are INSIDE the container)
docker exec clamav clamdscan --multiscan --fdpass \
--move=/quarantine /scandir >> "$LOG" 2>&1 || true

echo "scan complete: $(date)" >> "$LOG"

Schedule it

Run every 20 minutes from root’s crontab (sudo crontab -e):

bash

*/20 * * * * /opt/clamav/scan-and-purge.sh

Notes

  • Scope narrowly. The purge step targets movies/ and tv/ subfolders, not the whole download root — you don’t want it deleting a legitimate installer you parked elsewhere.
  • Test it. Drop the harmless EICAR test string into a file under the scan path; the next scan should detect Eicar-Test-Signature and move it to quarantine. Also drop a dummy .exe in movies/ and confirm the purge step deletes it.
  • Read-only mount on /scandir means ClamAV can’t alter your originals; only the purge script (running on the host) and the quarantine move touch files.
  • Executables are never run by the media pipeline anyway — this is defense in depth, keeping the download folder clean rather than trusting that nothing ever executes them.