Air-gapped security data updates (ClamAV, Grype, EPSS, CISA KEV)¶
repod needs no internet connection for its core operation — self-hosted, manual upload works fully offline. Internet access is only used by a handful of optional features, the most important of which is keeping the CVE-scanning pipeline current. Four data sources refresh from the internet:
| Source | What it is | Refreshed by | Local path (default) |
|---|---|---|---|
| ClamAV signatures | Antivirus signature database | freshclam |
/var/lib/clamav |
| Grype vulnerability DB | CVE matching database (grype db update, cron security_sync_daily) |
grype db update |
/repos/grype-db |
| EPSS scores | Exploit Prediction Scoring System (FIRST.org) | services/cve_enrichment.py |
/repos/security/epss_cache.json |
| CISA KEV | Known Exploited Vulnerabilities catalog | services/cve_enrichment.py |
/repos/security/kev_cache.json |
If your repod instance has no internet access at all, none of these 4 can self-refresh. This guide covers the supported workflow: a relay host (any machine with internet access) fetches fresh data, you transfer it offline (USB, disk, or whatever your security policy allows), and you import it on the air-gapped instance. Same pattern already used for the upstream cache, applied here to the security pipeline instead of package content.
1. Fetch on the relay host¶
The relay host needs Docker and a checkout of this repository — it does not need to be a full repod install, and it does not need internet access to the air-gapped site, only to the outside world.
git clone <your-repod-repo> && cd repod
bash scripts/security-data-transfer.sh fetch ./security-data-2026-08.tar.gz
This runs freshclam, grype db update, and repod's own
services/cve_enrichment.py (EPSS + KEV refresh) each inside a short-lived
container of the repod backend image itself — not a hand-rolled
reimplementation. This matters: it guarantees the exported files are
byte-for-byte what a normally-connected repod instance would have produced
(same tool versions, same cache file format), so there's no format drift
to worry about on import.
If a backend image has already been built locally (docker compose build
backend), the script reuses it. Otherwise it builds one from
./backend — this can take several minutes (Nuitka compilation is part
of the normal backend build), which is expected the first time.
A partial failure (e.g. Grype's upstream is briefly unreachable) doesn't abort the whole fetch — each of the 3 steps is independent, and the script warns and continues, packaging whatever succeeded. Check the console output before transferring if you need all 4 sources current.
2. Transfer¶
Move security-data-2026-08.tar.gz to the air-gapped site via whatever
offline medium your security policy allows (USB drive, write-once disk,
etc.) — same handoff step as the upstream cache guide.
3. Import on the air-gapped instance¶
From the repod checkout on the air-gapped host:
This extracts the bundle directly into the same host directories
docker-compose.yaml already mounts into the backend container
(./repos/clamav-db, ./repos/grype-db, ./repos/security) — no manual
path juggling needed on a standard deployment. Override
CLAMAV_HOST_DIR/GRYPE_HOST_DIR/SECURITY_HOST_DIR env vars first if
your deployment maps these elsewhere.
ClamAV needs a restart to pick up new signatures — clamd loads them
into memory once at container startup:
Grype and EPSS/KEV are re-read from disk on every scan/request — no restart needed for those.
4. Keeping it current¶
Repeat steps 1–3 periodically (e.g. weekly for ClamAV/Grype, which change
often; EPSS/KEV can go longer between refreshes) on the relay, transfer,
and import. There's no delta/incremental mode — each run re-fetches the
full current state of all 4 sources, which is what freshclam/grype db
update already do incrementally against their own upstream anyway; the
tarball itself is always a full snapshot, not a diff.
What this does not cover¶
This guide is specifically about the 4 CVE-pipeline data sources above. For air-gapping the package content itself (APT/RPM/APK repository mirroring), see the upstream cache guide instead — the two are independent and typically used together on a fully air-gapped deployment.