Technical whitepaper — PodHeitor MongoDB for Bacula

cdylib + backend architecture, dump mode and oplog incrementals, point-in-time replay, Ed25519-signed manifests, Prometheus exporter, and license isolation for Bacula Community 15.0.3+ and Bacularis.

Companion document to the PodHeitor MongoDB plugin page.

1. The problem: stock Bacula can’t back up MongoDB safely

The Bacula File Daemon, on its own, backs up files. MongoDB in production is not a coherent set of files at any single point in time: copying /var/lib/mongo while mongod is running yields an inconsistent snapshot that fails --repair or — worse — silently opens in a corrupt state. Traditional alternatives each have their own cost:

  • Bacula Enterprise MongoDB plugin — only does full mongodump; no oplog incrementals, no PITR, no signed manifest, no metrics exporter.
  • Veeam / Commvault — per-TB pricing scales poorly across large MongoDB clusters; PITR still depends on hand-rolled oplog handling.
  • Home-grown mongodump scripts — ignore read preference, hammer the PRIMARY, fail to detect oplog rotation gaps, and the operator only finds out at restore time.

PodHeitor MongoDB closes those gaps with cross-DB and cross-collection parallelism, persistent oplog tip, PIT replay by timestamp or incremental counter, and RPM/DEB packaging with hardened systemd unit.

2. Architectural model

The plugin follows the PodHeitor cdylib + standalone backend pattern with PTCOMM (length-tagged framing over stdin/stdout):

                      bacula-fd (root, daemon)
                            │
                            │ dlopen
                            ▼
     /opt/bacula/plugins/podheitor-mongodb-fd.so   (Rust cdylib)
                            │
                            │ fork+exec, PTCOMM stdin/stdout
                            ▼
 /opt/bacula/bin/podheitor-mongodb-backend       (per-job subprocess)
     ├── mongodump / mongorestore
     ├── stats sidecar  →  /opt/bacula/working/podheitor-mongodb-<r>-stats.json
     ├── oplog tip file  →  ...-tip.json
     └── manifest + topology + RestoreObject  →  Bacula catalog (FT_RESTORE_FIRST=25)
                                                       │
                                                       ▼
                          /opt/bacula/bin/podheitor-mongodb-exporter
                          (HTTP :9105/metrics, bacula:bacula, hardened systemd)

The split exists for the same reasons as HPC: crash isolation (a panic in MongoDB-touching code can’t wedge bacula-fd), parallelism freedom (the backend hosts tokio + cross-DB threadpools without violating Bacula’s “one thread per bpContext” contract), and license firewall (the subprocess never touches the Bacula ABI; only the cdylib does, exclusively via the in-house bacula-fd-abi crate with independent extern "C").

3. Supported backup modes (v1.0)

Mode Mechanism v1.0 status
dump Logical mongodump streaming, BSON over PTCOMM GA — JobId 7984/7985 validated
dump multi-DB pipelined N DBs in parallel, configurable cap (parallel_dbs=4) GA — JobId 7906
dump per-collection chunking Splits big collections by _id via $bucketAuto GA — JobId 7925/7927
Oplog incremental (level=I) applyOps over the wire — real incrementals, not diff-of-dump GA — JobId 8037-8041
PIT replay second / RFC 3339 / :inc / @inc GA — JobId 8065-8099
snapshot (LVM/ZFS) Code stub Phase 3 — pending LVM/ZFS-backed lab
hot_backup (Percona createBackup) Code stub Phase 4 — pending Percona Server lab
Sharded cluster orchestration Code stub Phase 6 — pending
replicate (DR clone) Code stub Phase 7 — pending

4. SECONDARY-aware source picker

A backup that hammers PRIMARY is a backup that costs SLA. The plugin parses the replicaSet connection string, runs rs.status(), and picks a healthy SECONDARY (readPreference=secondaryPreferred honoured). If no SECONDARY is reachable, it falls back to PRIMARY with an explicit warning in the job log — documented behaviour, never silent.

Field-validated: JobId 7728/7996.

5. Oplog incremental — the piece Bacula Enterprise lacks

Real MongoDB incrementals require the oplog. The plugin maintains a persistent oplog tip at /opt/bacula/working/podheitor-mongodb-<resource>-tip.json: each level=I job reads the previous job’s tip, opens a tailable cursor on local.oplog.rs from that timestamp, and streams every applyOps entry up to the current tip.

Rotation-gap detection is mandatory: if the oplog wrapped between two jobs, the old tip is gone. The plugin detects this by comparing the first entry’s ts against the expected tip and aborts with a clear message — the operator must run a fresh Full. Validated in JobId 8047-8050.

6. Point-in-time replay

Granular restore to an arbitrary instant without restoring the entire chain at the destination:

# By absolute timestamp (RFC 3339)
pluginoptions "podheitor-mongodb: mode=restore pit=2026-04-15T14:30:00Z dry_run=true"

# By incremental counter within the day
pluginoptions "podheitor-mongodb: mode=restore pit=:inc=12"

# By Unix second
pluginoptions "podheitor-mongodb: mode=restore pit=1713187800"

dry_run=true makes the plugin compute the chain (Full + N incrementals) and emit a preview to the job log without applying — useful for the operator to confirm before a destructive restore. Validated in JobId 8097/8099.

The per-restore sidecar JSON removes the need to restart the FD to change restore parameters — the sidecar is read each job and discarded afterward (Phase 5ε, JobId 8090/8092).

7. Signed manifest and BLAKE3 verification

Every backup emits a JSON manifest cataloging what was saved: list of DBs, collections, document counts, oplog tip, replica set topology at backup time. The manifest is BLAKE3-hashed and Ed25519-signed with a key configured via env var (PODHEITOR_MONGODB_SIGNING_KEY).

At restore the plugin verifies the signature before applying — protects against post-backup tampering (ransomware that tries to rewrite backups before detonating). The key lives outside the FD; artefacts can be audited independently via verify-artifacts.sh.

8. Packaging and Prometheus exporter

v1.0 ships RPM (EL9) + DEB (Debian 12 / Ubuntu 22.04), signed (RPM in-archive + DEB detached GPG), validated by a 5-distro smoke matrix on podman (5/5 PASS). The Prometheus exporter exposes at :9105/metrics:

  • podheitor_mongodb_exporter_scrapes_total
  • podheitor_mongodb_backup_duration_seconds{resource}
  • podheitor_mongodb_oplog_lag_seconds{resource}
  • podheitor_mongodb_last_success_timestamp{resource}

The exporter runs as bacula:bacula under a hardened systemd unit (ProtectSystem=strict, NoNewPrivileges=yes, PrivateTmp=yes), reading only the stats sidecars in the working dir.

9. Sample FileSet (replica set)

FileSet {
  Name = "MongoDB-RS-Dump"
  Include {
    Options { Signature = SHA256 }
    Plugin = "podheitor-mongodb: mode=dump uri=mongodb://backup_user@rs0-1,rs0-2,rs0-3/?replicaSet=rs0&readPreference=secondaryPreferred&authSource=admin database=* parallel_dbs=4 verify=true"
  }
}

Job {
  Name     = "MongoDB-Backup"
  Type     = Backup
  Client   = mongo-fd
  FileSet  = "MongoDB-RS-Dump"
  Storage  = File1
  Pool     = Default
  Schedule = "WeeklyCycle"
}

10. Supported versions (v1.0)

  • MongoDB Community 4.4, 5.0, 6.0, 7.0, 8.0
  • Percona Server for MongoDB 6.0, 7.0, 8.0 (hot_backup mode in Phase 4 — code-stub)
  • MongoDB Enterprise 7.0+ ($backupCursor) — Phase 9 / v1.5
  • Bacula Community 15.0.3+ and Bacularis

11. License posture

The plugin ships under PROPRIETARY single-license. The backend never touches the Bacula ABI.

Ready to evaluate?

30-day free trial for qualified MongoDB clusters (Community, Percona, Enterprise). We guarantee at least 50% off vs Bacula Enterprise, Veeam, or Commvault, with more features included — including PIT replay and signed manifests that no competitor delivers out-of-the-box.

Heitor Faria — Founder, PodHeitor International
[email protected]
☎ +1 (789) 726-1749 · +55 (61) 98268-4220 (WhatsApp)
🔗 PodHeitor MongoDB plugin page

Disponível em: pt-brPortuguês (Portuguese (Brazil))enEnglishesEspañol (Spanish)

Leave a Reply