Skip to content

Prebuilt database

Loading raw ClamAV signatures means parsing millions of them and building the signature index, which takes time and transient RAM. --build-db does that work once and writes a self-contained .exavdb; pointing -d at that file reads the index back instead of rebuilding it.

Measured with the 0.0.2 engine on a small arm64 VM, as the peak RSS of the process. Figures depend on hardware and on the engine version.

Signatures Parse and build (--build-db, or -d on the raw files) Load the .exavdb .exavdb size
daily.cvd alone ~14 s, 1.3 GB ~0.7 s, 0.55 GB 140 MB
main + daily + bytecode ~23 s, 1.6 GB ~1.3 s, 0.92 GB 352 MB

A raw load pays the build column on every start and every reload. The build is where exav spends effort so that loading and scanning don’t have to: whatever can be computed once from the signatures is computed there and stored, even when that makes the file larger. See Design principles.

  1. Build the database on a host with enough RAM (the full set wants about 2 GB, see What it costs):

    Terminal window
    exav -d ~/.cvdupdate/database --build-db exav.exavdb

    PUA detection is decided here: add --detect pua to the build if any scanner should detect PUA. --detect pua on a scan that loads a .exavdb changes nothing.

  2. Distribute exav.exavdb, then load it everywhere:

    Terminal window
    exav -d exav.exavdb /data # fast cold start
    exav --listen 0.0.0.0:3310 -d exav.exavdb # or serve it

Build the database daily on a capable host or in CI, distribute the file, and have every CLI and daemon instance load it. Constrained hosts never pay the build peak.

A .exavdb is tied to the engine that wrote it: it carries a format version, and a database with another version is rejected rather than loaded half-understood:

exav: loading database old.exavdb: unsupported database version 3 (this build expects 4)

Rebuild after an upgrade whose changelog mentions a new format version. Nothing is lost: the sources are the .cvd/.cld files freshclam or cvdupdate already fetched. If you ship a prebuilt database to other hosts, upgrade the builder and the scanners together: a download is checked for integrity, not for its format version, so a scanner offered a newer format keeps the database it has on a reload, and fails to start from scratch.

Point the daemon at the database file itself with -d. A .exavdb inside a directory given to --sig-dir is not loaded.

Terminal window
exav --listen /run/exav/exav.sock -d /srv/exav/live.exavdb

The daemon polls that file (like clamd’s SelfCheck), in every listener mode, so an update is a rebuild and an atomic swap:

Terminal window
# Once: tell cvdupdate where to keep the signatures.
cvd config set --dbs-directory /srv/exav/staging
# 1. Fetch fresh signatures, then build to a temporary path (the daemon keeps
# serving the old database).
cvd update
exav -d /srv/exav/staging --build-db /srv/exav/live.exavdb.tmp
# 2. Swap it in with a rename on the same filesystem. The next poll (~10 s)
# reloads the pool with the new signatures.
mv /srv/exav/live.exavdb.tmp /srv/exav/live.exavdb

The rename means a poll only ever sees a complete file, and the integrity frame lets the daemon reject a bad one and keep serving the current database. To reload at once, send RELOAD or SIGUSR2 (see Hot-reloading signatures). A reload holds the new database next to the old one until the swap, so leave room for both (see Sizing a server).

When the .exavdb is built on one server and the scanners run elsewhere, the daemon can pull it over HTTP instead of sharing a volume: give it --db-url and --auto-update. This needs an http-update build, such as the Docker image; the release binaries lack it (see Installation).

Terminal window
exav --listen /run/exav/exav.sock --auto-update --workers 4 \
--db-url https://build.internal/exav/live.exavdb \
-d /var/lib/exav/live.exavdb

-d names where the pulled database is written and loaded from; without it the destination is <--sig-dir>/remote.exavdb.

--update-interval-secs sets how often it re-checks, and defaults to 300 seconds here rather than a day, because the check is a conditional HEAD comparing the ETag / Last-Modified and transfers nothing when the file has not changed. Only a changed validator triggers a download, which is installed only after its Content-Length and trailing CRC-32 verify; a corrupt or interrupted download is rejected and the current database kept. The download is held in memory until it verifies, so budget its size on top of the loaded database. Basic-auth credentials in the URL (percent-encoded as URLs require) are sent as a header and redacted from logs. See the Docker guide for the container equivalent.