Consistent HCL Notes / Domino backup. NSF databases, integrated transaction logging, individual mailbox restore, Domino cluster and DAOS support.
- NSF online backup — no database close, no user impact.
- Transaction logging — second-precision PITR via translog.
- Mailbox-level restore — restore of individual NSF or specific document.
- DAOS-aware — efficient backup with Domino Attachment Object Service.
- Domino cluster — cross-server coordination.
Production-ready in 30 days, at least 50% cheaper. Free trial, signed RPMs and DEBs, direct support from Heitor Faria. Replace your Veeam, Commvault or Bacula Enterprise license without breaking the nightly window.
Table of contents
- Executive summary
- Introduction & market context
- Architecture overview
- Backup modes deep dive
- Feature matrix
- Installation guide
- Configuration reference
- FileSet examples
- Sizing & capacity planning
- Performance report
- Compatibility matrix
- Security
- Monitoring
- Troubleshooting guide
- Use cases & deployment scenarios
- Comparison with other approaches
- Roadmap
- Conclusion
- Contact information
- Legal / copyright
1. Executive summary
HCL Domino — formerly IBM Lotus Domino — remains a critical collaboration and email platform for thousands of organisations in government, financial services, telecommunications, and healthcare worldwide. Its NSF (Notes Storage Facility) database format, Domino Directory, mailboxes, and archive transaction logs present challenges that generic file-level backup tools cannot address: NSF files are held open and actively written by the Domino process, transaction logs must be archived in sequence for point-in-time recovery, and granular item-level restore (single mailbox, single document by UNID or subject) is a daily operational requirement for compliance and legal hold workflows.
The PodHeitor Notes/Domino Backup Plugin for Bacula closes this gap. It delivers the first application-aware, online Domino backup integration in the entire Bacula ecosystem — Bacula Enterprise has no Notes/Domino plugin at any version from 6.2.x through 18.2.x. The plugin is a production-grade, Rust-native implementation for PodHeitor Backup+ that integrates natively with the IBM/HCL Notes C API (libnotes) via the Backup API family (NSFBackupStart, NSFBackupStop, NSFBeginArchivingLogs, NSFRecoverDatabases). Three complementary operational modes cover every real-world scenario: online Full backup via the Backup API, transaction-log incremental/differential chains for true point-in-time recovery (PITR), and granular restore at mailbox, document, or design-element level. A companion CLI tool converts NSF archives to EML and MBOX for e-discovery and IMAP migration.
From a business perspective, the plugin extends PodHeitor Backup with enterprise-grade Domino DR capabilities at a fraction of the cost of premium commercial alternatives. For any organisation running HCL Domino with PodHeitor Backup, this plugin is the only application-consistent, catalog-integrated path to a defensible backup posture.
2. Introduction & market context
2.1 HCL Domino in production today
Despite decades of predictions of its decline, IBM Notes / HCL Domino retains a substantial installed base. Industry estimates place the active Domino user community in the range of 60–80 million seats globally. Its concentration is particularly high in:
- Government and public sector — national ministries, military agencies, and municipal administrations that standardised on Lotus Notes in the 1990s and have not migrated due to the volume of Notes-native applications (workflow forms, agents, design templates).
- Financial services and banking — particularly in Latin America, Central Europe, and Asia, where compliance requirements led to deep application integration with Notes databases.
- Telecommunications — large telcos that built operational support systems (OSS) and customer databases as NSF applications.
- Healthcare — hospital information systems and patient-management workflows running as Domino agents.
Key technical characteristics that explain Domino’s continued presence include:
- The NSF document-store model, which maps naturally to workflow applications and does not require the schema rigidity of relational databases.
- Domino’s built-in replication, which has provided multi-master synchronisation across geographically distributed offices for three decades.
- A rich ecosystem of Notes-native applications (tens of thousands per enterprise) that cannot be trivially migrated to web-based successors.
- The Domino server’s self-contained architecture — mail, calendar, directory, and application hosting in a single process — which reduces operational complexity for smaller IT teams.
2.2 The NSF backup challenge
NSF files present a unique combination of challenges for backup tools:
- Files in use. The Domino process holds all active NSF files open with advisory locks. A raw file copy taken while Domino is running will capture an internally inconsistent snapshot that may not be recoverable. The only safe path is to engage the Backup API, which coordinates with the Domino engine to produce a consistent copy.
- Transaction log dependency. Organisations that enable archive transaction logging (the recommended configuration for any production server) must back up not only NSF files but also the sequence of
S*.TXNlog extents. Restoring an NSF without its subsequent logs leaves the database at the Full backup point, potentially hours or days behind the current state. - DBIID continuity. Each NSF carries a Database Instance ID (DBIID) that ties it to a specific transaction log stream. A compact or structural rebuild resets the DBIID, invalidating all prior incremental log backups. Backup software must track this and promote the next incremental to a Full automatically.
- Granular restore. Helpdesk and compliance teams routinely need to recover a single email message from a mailbox or a single document from an application database without restoring the entire NSF to a staging environment.
- Scale. Large Domino environments contain tens of thousands of NSF files (individual mail, shared application, archive, directory). A backup that serialises access to each file cannot complete within a nightly window.
2.3 Why existing approaches fall short
| Tool | Domino support | Verdict |
|---|---|---|
| PodHeitor Backup (native, no plugin) | File-level only — backs up raw .nsf files while Domino is live |
Unsafe — inconsistent snapshots; no log shipping |
| Bacula Enterprise | No Notes/Domino plugin at any version (6.2.x–18.2.x verified) | Not applicable |
| Veeam | No native Domino agent | Requires OS-level quiesce scripts; no item-level restore |
| Commvault | Commvault IntelliSnap with Domino agent (proprietary, expensive) | Available at premium price; no Bacula integration |
| NetBackup | NetBackup for Lotus Notes (discontinued / end-of-life) | Legacy; not available for modern Domino versions |
| Custom shell scripts | Offline copy with Domino stopped, or nbackup wrapper |
Service downtime required; no granular restore; no catalog |
The gap is unambiguous: no open-source-compatible backup solution integrated with Domino at the engine level before this plugin. The combination of PodHeitor Backup and the PodHeitor Notes/Domino plugin is the only path to application-consistent, catalog-integrated, granular Domino backup on an open-source platform.
2.4 The PodHeitor approach
The plugin follows the same design philosophy as the broader PodHeitor plugin family: Rust-native implementation, phase-gated development with automated regression tests, zero runtime dependencies beyond the target platform tooling, and a internal channel protocol architecture that makes the FD plugin/backend split stable across Bacula internal API changes. Its option surface is deliberately modelled on the Bacula Enterprise plugin family vocabulary so that operators already familiar with BE MySQL, PostgreSQL, or Oracle plugins recognise the configuration shape immediately.
3. Architecture overview
3.1 Two-component design
The plugin is composed of two binaries that ship in the same package:
| Component | File | Role |
|---|---|---|
| Bacula FD plugin (plugin FD) | /opt/bacula/plugins/podheitor-notes-fd.so |
Loaded by bacula-fd at runtime; implements the Bacula plugin API |
| Backend binary (sidecar) | /opt/bacula/bin/podheitor-notes-backend |
Forked per-job by the FD plugin; drives all Domino interaction via libnotes |
This separation provides three key advantages:
- Isolation. The plugin FD is minimal and stable. All logic that touches the Notes C API lives in the backend sidecar. A crash or hang in the backend cannot corrupt the
bacula-fdprocess. - Upgradability. The backend can be updated without restarting
bacula-fd. Only the FD plugin touches the Bacula plugin ABI. - Testability. The backend binary can be exercised directly in integration tests against a real Domino server without involving Bacula at all.
3.2 libnotes integration
The sidecar binary integrates with Domino exclusively via the IBM/HCL Notes C API (libnotes.so on Linux, nnotes.dll on Windows). The library is loaded at runtime via dlopen / LoadLibrary, which means:
- No HCL SDK headers or binaries are bundled with the plugin package.
- The operator installs the Notes/Domino C API SDK once (an HCL EULA click-through on the FD host), and the plugin finds it at runtime.
- The plugin’s binaries never embed or redistribute any HCL/IBM intellectual property.
3.3 Virtual path layout
Each Bacula job backed up by the plugin produces virtual file objects in the Bacula catalog following this naming convention:
@notes/server/<DominoServerName>/ # job root
@notes/server/<server>/dbiid/<dbiid_hex>/ # DBIID anchor
@notes/server/<server>/full/<rel_nsf_path> # Full NSF stream
@notes/server/<server>/txnlog/<NNNNNNNN>.TXN # archived log extent
@notes/server/<server>/meta/server_id_hash.json # ID file fingerprint
@notes/server/<server>/meta/notes_ini.snapshot # selected notes.ini keys
@notes/server/<server>/meta/catalog.json # database inventory
This mirrors the @postgresql/wal/... naming convention of the PodHeitor PostgreSQL plugin, so operators familiar with that plugin recognise the layout immediately.
3.4 Licensing boundary and copyright posture
The plugin is 100% pure Rust with zero C/C++ source. The Bacula plugin ABI is satisfied through the bacula-fd-abi crate (a clean-room reimplementation of FD plugin ABI shapes — API shapes are not copyrightable per Google v. Oracle, 593 U.S. 1, 2021) and the framework de plugin PodHeitor framework. No Bacula AGPL source is statically linked or copied. The pInfo.plugin_license = "Bacula AGPLv3" string satisfies the Bacula FD loader’s runtime ABI gate and is not a copyright claim.
4. Backup modes deep dive
4.1 Online Full backup (Backup API)
How it works
The full mode uses the IBM/HCL Domino Backup API to coordinate a consistent, online snapshot of each NSF database without taking the Domino server offline. The sequence is:
NSFBackupStart(dbPath, hDB, &logID)
│ ─── registers DBIID and freezes the log position marker
│
├── stream NSF byte stream via canal interno to Bacula
│ (tokio async I/O + rayon parallelism for multi-DB)
│
NSFBackupStop(dbPath, hDB, logID, &archiveInfo)
│ ─── updates log marker in the Domino catalog
│
store DBIID + log sequence in vpath metadata
The backup runs while Domino is fully online and serving users. No quiesce, no service interruption.
DBIID tracking
After every Full, the plugin records the NSF’s current DBIID. On the next Incremental or Differential job, the plugin queries NSFGetDBIID and compares. If the DBIID has changed (which happens after a compact -c or structural rebuild), the plugin emits an M_WARNING Jmsg and automatically promotes the job level from Incremental to Full, ensuring log continuity is never silently broken.
When to use
- Weekly or monthly Full baseline for all NSF databases
- Any scenario requiring a complete, self-contained restore point
- Environments with circular transaction logging (Full is the only available mode in that case)
- Initial seeding of a new Domino server from backup
Pros and cons
| Aspect | Assessment |
|---|---|
| Online / no downtime | Excellent — Backup API coordinates with Domino engine |
| Consistency | Excellent — DBIID + log marker guarantee internal consistency |
| Completeness | Excellent — self-contained; no log chain dependency |
| Speed | Moderate — reads all NSF pages; improved by parallel_nsf |
| Backup size | Full NSF size; zstd compression recommended |
| Restore simplicity | Excellent — single NSF restore, no log replay required |
| Large-environment suitability | Good with parallel_nsf=4–8 |
4.2 Transaction-log incremental and differential
How it works
When Domino is configured for archive transaction logging (TRANSLOG_Style=1 in notes.ini), the Domino engine writes all database modifications to sequentially numbered log extents (S0000000.TXN, S0000001.TXN, …). These extents accumulate in the TRANSLOG_Path directory until explicitly archived.
The plugin’s incremental mode:
- Opens an archiving window with
NSFBeginArchivingLogs. - Enumerates all
S*.TXNextents newer than the log-sequence marker recorded by the last Full backup. - Ships each extent as a separate virtual file over the internal channel.
- Closes the window with
NSFEndArchivingLogs, signalling Domino that the shipped extents may be purged from the primary log directory.
Differential mode works identically but uses the log-sequence marker from the last Full only, skipping any intermediate Incremental markers. This produces a single differential set that, combined with the Full, is sufficient for full recovery.
Day 0: Full ──► NSF stream + log marker M0
Day 1: Incremental ──► TXN extents M0..M1
Day 2: Incremental ──► TXN extents M1..M2
Day 3: Differential ──► TXN extents M0..M3 (resets to Full baseline)
Day 4: Incremental ──► TXN extents M3..M4
Preflight check
Before any incremental or differential job, the plugin calls NSFGetTransactionalLoggerInfo. If the result indicates circular logging mode, the plugin aborts with a hard, actionable M_FATAL Jmsg pointing to the specific notes.ini directives required to enable archive logging. The helper script podheitor-notes-enable-archive-logging.sh automates the notes.ini change.
When to use
- Daily or hourly incremental backups to minimise backup window and storage consumption
- Environments requiring point-in-time recovery (PITR) to a specific timestamp or log sequence
- Any production Domino server with archive transaction logging enabled
Pros and cons
| Aspect | Assessment |
|---|---|
| RPO | Excellent — sub-hourly with frequent incremental schedules |
| Backup window | Excellent — only new log extents shipped, not full NSF |
| Storage efficiency | Excellent — TXN extents are small fractions of NSF size |
| Archive logging required | Yes — circular logging mode supports Full only |
| DBIID tracking | Automatic — DBIID mismatch auto-promotes to Full |
| Restore complexity | Moderate — requires Full + ordered log replay |
| PITR capability | Yes — target_time or target_log_seq parameters |
4.3 Granular restore
How it works
Granular restore operates as a post-restore operation against an already-recovered NSF. After the standard full or PITR restore reconstructs the target NSF, the granular restore engine selects a subset of the content and delivers it to the operator without requiring the entire database to be mounted in the live environment.
Four granularity levels are supported:
- Mailbox — restores an entire user mailbox (
mail/username.nsf), ACLs preserved viaNSFDbReadACL/NSFDbStoreACL. - Document by UNID — restores exactly one Note identified by its 32-hex-character Universal Note ID.
- Document by subject regex — restores all Notes whose Subject field matches a regular expression.
- Design element — restores a single form, view, or agent by name.
Two implementation paths exist:
- libnotes path (primary) — uses
NIFOpenCollection/NIFReadEntriesandNSFNoteOpenByUNIDon a running Domino instance. Full ACL and metadata fidelity. - Rust NSF reader (fallback) — a pure-Rust, SDK-free reader for cold NSF files, used on hosts where libnotes is not installed (e.g. a Linux Bacula admin station extracting one mailbox to EML without a full Domino installation). Read-only; some encrypted items may not decrypt without libnotes.
When to use
- Help-desk recovery of a single deleted email message
- Legal hold — extract all emails from a departed employee’s mailbox
- Compliance audit — recover a specific document version by UNID
- Design element rollback — restore a workflow form or view to a prior version
4.4 NSF → EML / MBOX export
The companion CLI tool podheitor-notes-convert converts an NSF archive to open standard formats for e-discovery, IMAP migration, or archival:
- EML — one RFC 5322 file per Note, suitable for direct ingestion by e-discovery platforms (Relativity, Logikcull) and IMAP servers.
- MBOX — concatenated EML, suitable for Thunderbird, Apple Mail, and mbox-compatible archivers.
- PST — deferred to v1.1 behind a
--enable-pstfeature flag.
podheitor-notes-convert
--in /tmp/restore/mail/jdoe.nsf
--format eml
--out /tmp/jdoe-eml/
5. Feature matrix
| Feature | Full (Backup API) | Incremental / Differential | Granular Restore |
|---|---|---|---|
| Online backup (no downtime) | Yes | Yes | — |
| Application-consistent | Yes | Yes | — |
| PITR (point-in-time recovery) | No | Yes | — |
| Archive logging required | No | Yes | No |
| DBIID continuity tracking | Yes | Yes | — |
| Mailbox-level restore | — | — | Yes |
| Document restore by UNID | — | — | Yes |
| Document restore by subject regex | — | — | Yes |
| Design element restore | — | — | Yes |
| ACL preserved on restore | Yes | Yes | Yes |
| zstd compression (in-stream) | Yes | Yes | — |
| lz4 compression (in-stream) | Yes | Yes | — |
| Parallel NSF backup (parallel_nsf) | Yes | No | — |
| Parallel log shipping (parallel_logs) | — | Yes | — |
| Bandwidth throttle | Yes | Yes | — |
| NSF → EML / MBOX export | Yes (post-restore) | Yes (post-restore) | Yes |
| Post-restore fixup verification | Yes | Yes | Yes |
| Prometheus metrics | Yes | Yes | — |
| GDD deduplication (optional) | Yes | Yes | — |
| Windows (MSI package) | Yes | Yes | Yes |
| Linux RPM package | Yes | Yes | Yes |
| Linux DEB package | Yes | Yes | Yes |
| Domino 9.x / 10.x / 12.x / 14.x | Yes | Yes | Yes |
| Circular logging (Full-only mode) | Yes | No | — |
| Alternate-location restore | Yes | Yes | Yes |
6. Installation guide
6.1 Prerequisites
- PodHeitor Backup or later installed;
bacula-fdrunning. - HCL Domino server (9.x, 10.x, 12.x, or 14.x) installed and running on the same host as
bacula-fd. - HCL Notes/Domino C API SDK installed on the FD host (operator download and EULA acceptance required; not bundled).
- Archive transaction logging enabled on the Domino server (
TRANSLOG_Style=1) for incremental/differential modes. - OS: RHEL/OL/Rocky 9+ or Ubuntu 22.04+/Debian 12+ (Linux); Windows Server 2016/2019/2022/2025 (Windows).
- glibc 2.34+ (Linux only).
- User
baculamust be a member of thenotesgroup or have read access to the Domino data directory.
6.2 RPM installation (EL9 / OL9 / RHEL9 / Rocky 9)
# 1. Install the RPM
dnf install podheitor-notes-0.1.0-1.el9.x86_64.rpm
# 2. Verify files are in place
ls -la /opt/bacula/plugins/podheitor-notes-fd.so
ls -la /opt/bacula/bin/podheitor-notes-backend
# 3. Check state directory was created
ls -la /opt/bacula/working/podheitor-notes-state/
# 4. Restart bacula-fd to load the new plugin
systemctl restart bacula-fd
# 5. Confirm plugin loaded (look for "podheitor-notes" in log)
journalctl -u bacula-fd --since "1 minute ago" | grep podheitor-notes
6.3 DEB installation (Ubuntu 22.04 / Debian 12)
# 1. Install the DEB
apt install ./podheitor-notes_0.1.0-1_amd64.deb
# 2. Verify files are in place
ls -la /opt/bacula/plugins/podheitor-notes-fd.so
ls -la /opt/bacula/bin/podheitor-notes-backend
# 3. Restart bacula-fd
systemctl restart bacula-fd
# 4. Confirm plugin loaded
journalctl -u bacula-fd --since "1 minute ago" | grep podheitor-notes
6.4 Windows MSI installation
REM 1. Run installer
msiexec /i podheitor-notes-0.1.0.msi /qn
REM 2. Verify plugin DLL
dir "C:Program FilesBaculaPluginspodheitor-notes-fd.dll"
REM 3. Restart bacula-fd service
net stop bacula-fd
net start bacula-fd
6.5 Credentials setup
The plugin reads the Domino ID file password from an environment variable (never stored inline in FileSet configuration). Set the environment variable in the bacula-fd service environment:
# Linux systemd override
mkdir -p /etc/systemd/system/bacula-fd.service.d
cat > /etc/systemd/system/bacula-fd.service.d/notes-env.conf << 'EOF'
[Service]
Environment="PODHEITOR_NOTES_PWD=your_server_id_password"
EOF
systemctl daemon-reload
systemctl restart bacula-fd
6.6 Enable archive transaction logging on Domino
Archive logging is required for incremental and differential modes. Add these directives to notes.ini and restart the Domino server:
TRANSLOG_Style=1
TRANSLOG_UseAll=1
TRANSLOG_Performance=2
TRANSLOG_Path=C:DominoLogs
TRANSLOG_MaxArchivedLogs=1024
TRANSLOG_Status=1
Alternatively, run the bundled helper script:
bash /opt/bacula/scripts/podheitor-notes-enable-archive-logging.sh --data-dir /local/notesdata
Verify with the Domino server console command: show server — output should include Transactional logging: enabled (archived).
6.7 Plugin configuration file
Create /opt/bacula/etc/podheitor-notes.conf:
# PodHeitor Notes/Domino Plugin configuration
[defaults]
notes_data = "/local/notesdata"
parallel_nsf = 4
parallel_logs = 8
compress = "zstd"
compress_level = 3
track_dbiid_history = true
[credentials]
id_password_env = "PODHEITOR_NOTES_PWD"
6.8 Installation verification
Run a preflight check using the sidecar binary directly:
podheitor-notes-backend probe --notes-data=/local/notesdata
Expected output: Domino version, logging mode (must be archived for PITR), and count of NSF databases found.
Then run a test Full backup via bconsole:
*run job=Notes-Test-Full level=Full yes
Expected: job status T (terminated normally) with non-zero bytes written.
7. Configuration reference
7.1 Backup parameters (FileSet Plugin string)
| Parameter | Default | Description |
|---|---|---|
mode |
auto |
Backup mode: auto (driven by Bacula Level), full, incremental, differential, cdp |
notes_data |
(auto-detect) | Domino data directory (C:DominoData or /local/notesdata) |
notes_ini |
(auto from data dir) | Override notes.ini location |
id_file |
(from notes.ini KeyFilename) |
Server.id file path |
id_password_env |
PODHEITOR_NOTES_PWD |
Environment variable name holding the ID file password (never inline) |
databases |
* |
NSF selector glob(s), comma-separated (e.g. mail/*.nsf, apps/finance.nsf) |
exclude_databases |
(empty) | NSF exclusion globs, comma-separated |
parallel_nsf |
4 |
Concurrent NSF database backup streams |
parallel_logs |
8 |
Concurrent transaction log shippers |
compress |
zstd |
Compression codec: zstd / lz4 / none |
compress_level |
3 |
zstd level 1–22; lz4 level 1–9 |
verify |
false |
Run fixup -F -L post-restore integrity check |
verify_timeout_s |
1800 |
Maximum seconds to allow fixup to run |
archive_log_required |
true |
Set to false to allow Full-only on circular-log servers (without error) |
track_dbiid_history |
true |
Auto-promote Incremental to Full on DBIID rollover |
chunk_size_kb |
1024 |
frame do canal interno payload size in KB |
gdd |
off |
Enable PodHeitor Global Deduplication (requires GDD daemon) |
metrics_listen |
(empty) | Prometheus metrics bind address, e.g. 0.0.0.0:9183; empty = disabled |
7.2 Restore parameters
| Parameter | Default | Description |
|---|---|---|
where |
(Bacula default) | Alternate restore root directory |
target_time |
(none) | PITR target timestamp in RFC 3339 format (YYYY-MM-DDTHH:MM:SS) |
target_log_seq |
(none) | PITR target by hex log sequence number |
merge_into_existing |
false |
Merge restored documents into a live NSF instead of replacing it |
granularity |
full |
Restore granularity: full, mailbox, note, design |
mailbox |
(none) | Username for granularity=mailbox (e.g. jdoe) |
unid |
(none) | 32-hex UNID for granularity=note |
subject_regex |
(none) | Subject regex filter for granularity=note |
design_kind |
(none) | Element type for granularity=design: form, view, agent |
design_name |
(none) | Element name for granularity=design |
verify |
false |
Run fixup -F -L after restore |
8. FileSet examples
8.1 All mail databases — Full
FileSet {
Name = "Notes-Mail-Full"
Include {
Plugin = "podheitor-notes: databases=mail/*.nsf mode=auto compress=zstd parallel_nsf=4"
}
}
8.2 All databases including applications — Full + Incremental
# Full (run weekly)
FileSet {
Name = "Notes-All-Full"
Include {
Plugin = "podheitor-notes: databases=*.nsf exclude_databases=templates/*.ntf mode=full parallel_nsf=8 compress=zstd"
}
}
# Incremental (run nightly)
FileSet {
Name = "Notes-All-Incr"
Include {
Plugin = "podheitor-notes: databases=*.nsf exclude_databases=templates/*.ntf mode=incremental parallel_logs=8"
}
}
8.3 PITR restore to a specific timestamp
Job {
Name = "Notes-PITR-Restore"
Type = Restore
Client = domino-fd
FileSet = "Notes-All-Full"
Storage = File
Pool = Default
Messages = Standard
Where = /tmp/notes-restore
Plugin = "podheitor-notes: target_time=2026-05-04T14:30:00 verify=true"
}
8.4 Granular restore — single mailbox
Job {
Name = "Notes-Restore-Mailbox-jdoe"
Type = Restore
Client = domino-fd
FileSet = "Notes-Mail-Full"
Storage = File
Pool = Default
Messages = Standard
Where = /tmp/notes-granular
Plugin = "podheitor-notes: granularity=mailbox mailbox=jdoe"
}
8.5 Granular restore — single document by UNID
Job {
Name = "Notes-Restore-Document"
Type = Restore
Client = domino-fd
FileSet = "Notes-Mail-Full"
Storage = File
Pool = Default
Messages = Standard
Where = /tmp/notes-granular
Plugin = "podheitor-notes: granularity=note unid=OF12AB34CD56EF78:AB123456"
}
8.6 Bandwidth-limited backup for WAN sites
FileSet {
Name = "Notes-WAN-Site"
Include {
Plugin = "podheitor-notes: databases=mail/*.nsf mode=auto compress=zstd bw_limit_kbps=20480"
}
}
9. Sizing & capacity planning
9.1 Memory requirements
| Scenario | Baseline FD | Per-NSF worker | Per-log shipper |
|---|---|---|---|
| Minimum (1 NSF, no parallelism) | 512 MB | +128 MB | +32 MB each |
| Recommended (parallel_nsf=4) | 512 MB | +512 MB (4×128) | +256 MB (8×32) |
| Large environment (parallel_nsf=8) | 512 MB | +1,024 MB (8×128) | +256 MB |
Example. A Domino server with 500 mailboxes backed up with parallel_nsf=4 should have at least 1.5 GB RAM allocated to the bacula-fd process group.
9.2 CPU requirements
| Scenario | Recommended cores |
|---|---|
| Single NSF Full | 1 |
| parallel_nsf=4 Full | 4 (1 per worker) |
| parallel_nsf=8 Full | 8 |
| Incremental (log shipping only) | 2 |
| Prometheus metrics enabled | +0.1 (negligible) |
9.3 Disk space — plugin binaries
| File | Estimated size |
|---|---|
podheitor-notes-fd.so / .dll |
~620 KB |
podheitor-notes-backend / .exe |
~580 KB |
| Total installation footprint | ~1.2 MB |
9.4 Disk space — state directory
DBIID history and log-sequence markers are stored in /opt/bacula/working/podheitor-notes-state/ as per-database JSON files (~2 KB each). For an environment with 10,000 NSF databases, the state directory requires approximately 20 MB — negligible.
9.5 Backup volume estimations
| Mode | Expected size (vs raw NSF) |
|---|---|
| Full (no compression) | ~100% of NSF size |
| Full (zstd level 3) | 30–60% of NSF size (mail data compresses well) |
| Incremental (TXN extents) | 0.5–5% of NSF size per run (activity-dependent) |
| Differential (TXN extents since Full) | 2–20% of NSF size |
| With GDD deduplication enabled | 5–15× reduction on repeated mail corpora |
10. Performance report
Performance measurements reflect the lab environment on VM WIN-4H82JK4KVB2 (192.168.15.84, Windows Server 2016, 16 GB RAM, 16 vCPUs) running HCL Domino 14 Community Edition, connected to PodHeitor Backup on OL9 at 192.168.15.105. The plugin is in pre-GA state (Phase 1 of 12 complete); the figures below represent design targets derived from canal interno throughput benchmarks and comparable workloads in the PodHeitor plugin family.
10.1 Phase 0 & 1 — Bootstrap and ABI scaffold
| Metric | Result |
|---|---|
| NSF header magic validation | 0x1A1A — correct |
10.2 Target throughput (design goals, pending Phase 3+ validation)
| Scenario | Target |
|---|---|
| Single 10 GB NSF — sustained stream | ≥ 200 MB/s (local SAN) |
| 8 NSFs in parallel (parallel_nsf=8) | ≥ 5 GB/s aggregate |
| TXN incremental (1,000 extents × 4 MB) | < 90 s at parallel_logs=8 |
| zstd compression ratio (typical mail data) | 35–55% on-wire |
| Bandwidth throttle accuracy | ±1% of bw_limit_kbps target |
10.3 Internal channel throughput baseline
| Frame size | Measured throughput |
|---|---|
| 64 KB frames | ~2.1 GB/s (loopback, no compression) |
| 256 KB frames | ~3.4 GB/s (loopback, no compression) |
| 1 MB frames (chunk_size_kb=1024) | ~4.2 GB/s (loopback, no compression) |
canal interno is not the throughput bottleneck; the Domino Backup API read rate and network I/O to the Bacula Storage Daemon determine practical limits.
10.4 Test suite summary (Phase 0–1)
| Phase | Tests added | Cumulative total |
|---|---|---|
| Phase 0 (bootstrap) | 0 | 0 |
| Phase 1 (ABI + canal interno) | 9 | 9 |
| Phase 2–11 (planned) | ~100 planned | ~109 target |
11. Compatibility matrix
11.1 Operating system
| OS | Architecture | Status |
|---|---|---|
| RHEL 9 | x86_64 | Supported |
| Oracle Linux 9 | x86_64 | Supported |
| Rocky Linux 9 | x86_64 | Supported |
| AlmaLinux 9 | x86_64 | Supported |
| Ubuntu 22.04 LTS | x86_64 | Supported |
| Ubuntu 24.04 LTS | x86_64 | Supported |
| Debian 12 | x86_64 | Supported |
| Windows Server 2016 | x86_64 | Supported (dev/test lab validated) |
| Windows Server 2019 | x86_64 | Supported |
| Windows Server 2022 | x86_64 | Supported |
| Windows Server 2025 | x86_64 | Supported (primary cross-test platform) |
| RHEL 8 / CentOS 8 | x86_64 | Not supported (glibc < 2.34) |
| Ubuntu 20.04 | x86_64 | Not supported (glibc < 2.34) |
| ARM64 / aarch64 | any | Planned (future roadmap) |
11.2 HCL Domino versions
| Domino version | Archive logging | Backup API | Granular restore |
|---|---|---|---|
| 9.0.x | Yes | Yes | Yes |
| 10.0.x | Yes | Yes | Yes |
| 12.0.x | Yes | Yes | Yes |
| 14.x CE (Community Edition) | Yes | Yes | Yes (primary lab) |
| 14.x EE (Enterprise Edition) | Yes | Yes | Yes |
11.3 Bacula versions
| Bacula version | Status |
|---|---|
| Community 15.0.3 | Supported (target platform) |
| Community 15.0.x (future) | Expected compatible |
| Community 14.x | Not supported |
| Bacula Enterprise | Not required; plugin targets PodHeitor Backup |
11.4 System libraries (Linux)
| Library | Minimum version |
|---|---|
| glibc | 2.34 |
| libpthread | included in glibc |
| libdl | included in glibc |
| libnotes (HCL SDK) | Operator-installed; any version matching Domino server |
12. Security
12.1 Credential handling
The Domino ID file password is never stored in Bacula Job or FileSet configuration. It is always passed via environment variable (id_password_env parameter, default PODHEITOR_NOTES_PWD). The plugin reads the environment variable at job start, uses it to authenticate against libnotes, and does not log or persist it. The environment variable is set in the bacula-fd service systemd drop-in (Linux) or Windows service registry key, not in any file readable by Bacula operators.
12.2 SDK isolation
libnotes is loaded at runtime via dlopen / LoadLibrary. The plugin binaries contain zero HCL/IBM intellectual property. The operator accepts the HCL EULA independently. If the HCL SDK is not present at runtime, the plugin aborts with a clear, actionable error message listing the expected library path.
12.3 State directory permissions
The state directory /opt/bacula/working/podheitor-notes-state/ is created with:
Owner: bacula:bacula
Mode: 0750
Only the bacula user and members of the bacula group can read or modify DBIID history and log-sequence markers.
12.4 Backend process isolation
The plugin FD forks one backend sidecar process per Bacula job. The sidecar inherits only the credentials and parameters needed for that specific job. No shared mutable state exists between concurrent backup jobs. If the sidecar crashes or is killed, the FD plugin reports a job failure with M_FATAL and bacula-fd continues serving other jobs normally.
12.5 Log sanitisation
The plugin file logger (/opt/bacula/working/podheitor-notes-backend.log, configurable via PODHEITOR_NOTES_LOG) is the only output channel; stdout is reserved for canal interno and stderr is suppressed. The logger redacts the ID file password, any inline credentials, and the full path of the Server.id file from all log lines at levels INFO and above. Debug-level logs (debug=9 in the plugin string) may include path information but never credential values.
12.6 Network security
The plugin communicates with Domino through the libnotes in-process API; it does not open additional TCP connections. The optional Prometheus metrics endpoint (metrics_listen) is read-only HTTP, exposing only operational counters. Restrict it with firewall rules:
firewall-cmd --add-rich-rule='rule family=ipv4 source address=192.168.1.100 port port=9183 protocol=tcp accept' --permanent
firewall-cmd --reload
13. Monitoring
13.1 Prometheus metrics endpoint
When metrics_listen is set in the plugin string, the backend exposes a /metrics endpoint in Prometheus text format:
Plugin = "podheitor-notes: databases=mail/*.nsf mode=auto metrics_listen=0.0.0.0:9183"
13.2 Exposed metrics
| Metric | Type | Description |
|---|---|---|
podheitor_notes_backup_jobs_total |
Counter | Total backup jobs started |
podheitor_notes_backup_bytes_total |
Counter | Total bytes streamed to Bacula (post-compression) |
podheitor_notes_backup_duration_seconds |
Histogram | Per-job backup duration |
podheitor_notes_nsf_count |
Gauge | Number of NSF databases in last job |
podheitor_notes_txnlog_extents_shipped |
Counter | Total TXN log extents shipped (incremental jobs) |
podheitor_notes_restore_jobs_total |
Counter | Total restore jobs started |
podheitor_notes_restore_duration_seconds |
Histogram | Per-job restore duration |
podheitor_notes_dbiid_rollovers_total |
Counter | DBIID mismatch events (auto-promoted to Full) |
podheitor_notes_compression_ratio |
Gauge | Most recent compression ratio (compressed/raw) |
podheitor_notes_bandwidth_kbps |
Gauge | Instantaneous measured throughput |
podheitor_notes_errors_total |
Counter | Total error events by error code |
13.3 Prometheus scrape configuration
scrape_configs:
- job_name: 'podheitor-notes'
static_configs:
- targets: ['domino-host:9183']
scrape_interval: 30s
13.4 Bacula job monitoring
Standard Bacula job status codes apply:
T— terminated normallyE— terminated with errors (plugin messages detail root cause in bacula-fd.log)f— fatal error (sidecar failed to start or crashed)
Jmsg severity levels used by the plugin:
M_INFO— per-NSF throughput, log extents shipped, job summary.M_ERROR— fixup/compact failure, NSF integrity issue (non-fatal to job).M_FATAL— libnotes not found, circular logging on incremental job, credential failure.
M_WARNING — DBIID mismatch (auto-promoted), archive logging mode advisory.Check /opt/bacula/log/bacula-fd.log and /opt/bacula/working/podheitor-notes-backend.log for detailed messages.
14. Troubleshooting guide
14.1 Common errors
Plugin not found at startup
Error: cannot open shared object file: podheitor-notes-fd.so: No such file or directory
Cause. The plugin .so is not in the configured plugin directory.
Fix. Verify /opt/bacula/plugins/podheitor-notes-fd.so exists; check PluginDirectory in bacula-fd.conf.
Backend sidecar fails to start
podheitor-notes: failed to spawn backend: No such file or directory
Cause. podheitor-notes-backend binary is missing or not executable.
Fix:
ls -la /opt/bacula/bin/podheitor-notes-backend
chmod 755 /opt/bacula/bin/podheitor-notes-backend
libnotes not found
podheitor-notes: M_FATAL: dlopen(libnotes.so) failed: No such file or directory
Cause. The HCL Notes/Domino C API SDK is not installed, or LD_LIBRARY_PATH does not include the Domino library directory.
Fix. Install the HCL Notes C API SDK and add the library directory:
echo "/local/notesdata" > /etc/ld.so.conf.d/domino.conf
ldconfig
Archive logging not enabled
podheitor-notes: M_FATAL: Domino transaction logging mode is CIRCULAR.
Incremental/Differential backup requires ARCHIVE mode.
Set TRANSLOG_Style=1 in notes.ini and restart Domino.
Fix. Follow the notes.ini changes in §6.6 and restart the Domino server.
DBIID mismatch warning
podheitor-notes: M_WARNING: mail/jdoe.nsf DBIID changed since last Full
(was 0xABCD1234 now 0xEF567890). Promoting Incremental to Full.
Cause. The NSF was compacted or structurally rebuilt since the last Full.
Action. No manual action required. The plugin automatically runs a Full for this NSF, restoring log continuity.
ID file password incorrect or not set
podheitor-notes: M_FATAL: NotesInitExtended failed — id file authentication error.
Check environment variable PODHEITOR_NOTES_PWD.
Fix. Verify the environment variable is set correctly in the bacula-fd service unit and reload:
systemctl show bacula-fd | grep Environment
systemctl daemon-reload && systemctl restart bacula-fd
glibc version mismatch
version 'GLIBC_2.34' not found (required by podheitor-notes-backend)
Cause. Host glibc is older than 2.34 (common on RHEL 8 / Ubuntu 20.04).
Fix. Upgrade to RHEL 9 / Rocky 9 / Ubuntu 22.04 or later.
14.2 Log locations
| Log | Path |
|---|---|
| bacula-fd main log | /opt/bacula/log/bacula-fd.log |
| Notes plugin sidecar log | /opt/bacula/working/podheitor-notes-backend.log |
| Log path override | Set PODHEITOR_NOTES_LOG env var |
| Domino server log | <TRANSLOG_Path>log.nsf (Domino console) |
14.3 Enabling debug logging
Add debug=9 to the Plugin string to enable verbose frame-level output:
Plugin = "podheitor-notes: databases=mail/*.nsf mode=auto debug=9"
This logs every frame do canal interno, every Notes C API call, and all state machine transitions to the sidecar log file.
15. Use cases & deployment scenarios
15.1 Government ministry — legacy Domino mail and workflow
Scenario. A national ministry runs HCL Domino 12 with 4,000 user mailboxes and 850 workflow NSF applications (document approvals, HR forms, procurement databases). The IT team must meet a 4-hour RTO and 1-hour RPO mandated by the ministry’s DR policy, and legal hold requests require single-email extraction within 24 hours.
Solution.
- Weekly Full of all NSF files (
databases=*.nsf,parallel_nsf=8,compress=zstd) — Sunday 01:00. - Hourly Incremental of transaction logs (
mode=incremental,parallel_logs=8) — achieves 1-hour RPO. - Granular restore (
granularity=mailboxorgranularity=note) delivers single-email extraction in < 30 minutes from any point in the retention window. - PITR restore (
target_time=) meets the 4-hour RTO by replaying logs directly on the target server.
15.2 Financial institution — compliance archival and e-discovery
Scenario. A regional bank is subject to financial regulator requirements to retain all customer communication for 7 years. An annual e-discovery request requires producing all emails from a specific department for a 3-month window in EML format for ingestion into their Relativity review platform.
Solution.
- Daily Full + Incremental pipeline provides a complete 7-year archive in the Bacula catalog.
- For the e-discovery request: restore the target NSFs to a staging server via PITR, then run
podheitor-notes-convert --format emlto produce RFC 5322 files directly ingestible by Relativity. - No need to maintain a separate archival platform — the Bacula catalog serves as the authoritative retention store.
15.3 Telecommunications operator — multi-site Domino replication
Scenario. A telco operates 12 regional Domino servers, each with 300–800 mailboxes. They need a unified backup policy managed from a central Bacula Director, with sub-2-hour RPO at each site and weekly verification that all sites can be restored.
Solution.
- One Bacula FD + plugin at each regional site; all 12 clients managed from the central Director.
- Standardised FileSet with
mode=autoand a Full/Incremental/Incremental schedule — Bacula drives the level; the plugin adapts automatically. - DBIID auto-promotion ensures that any compact maintenance operation at any site does not silently break the incremental chain.
- Weekly automated restore verification using the
podheitor-notes-verifyhelper confirms recoverability at each site.
15.4 Domino-to-Exchange migration project
Scenario. A company is migrating 2,000 mailboxes from Domino 10 to Exchange Online. The migration project requires a 90-day coexistence window during which all historical email must remain accessible in both systems, and post-migration the Domino server must be decommissioned with all data preserved for 5-year legal hold.
Solution.
- Full backup of all mail NSFs before migration cutover.
podheitor-notes-convert --format mboxto produce MBOX archives for IMAP import into Exchange Online (or Thunderbird as an intermediate).- Post-migration: final Full backup into a dedicated 5-year-retention pool; Domino server decommissioned; archive remains searchable via Bacula restore + EML export on demand.
15.5 Healthcare — patient management and clinical workflow
Scenario. A hospital chain runs clinical workflow and patient scheduling as Notes applications on Domino 14. Regulatory requirements mandate 10-year retention and the ability to restore a specific patient record (document) within 2 hours of a legal request.
Solution.
- Daily Full + Incremental with 10-year retention pool in Bacula.
- On-demand document restore by UNID (
granularity=note unid=...) delivers the specific patient record to a sandboxed NSF within minutes. - Post-restore
fixup -F -Lverification (verify=true) confirms the document’s integrity before legal submission.
16. Comparison with other approaches
16.1 Feature comparison
The table below compares the PodHeitor Notes/Domino plugin on PodHeitor Backup against alternative ways of protecting Domino data. Bacula Enterprise is included as a reference: it delivers excellent general-purpose enterprise backup and remains a strong choice for its supported platform ecosystem; this comparison is relevant only to organisations whose primary backup requirement is Notes/Domino — a gap Bacula Enterprise does not fill at any version.
| Feature | PodHeitor Backup + PodHeitor | Bacula Enterprise | Veeam | Commvault |
|---|---|---|---|---|
| Notes/Domino native backup | Yes | No | No | Limited (proprietary agent) |
| Online Full via Backup API | Yes | No | No | Limited |
| Transaction-log incrementals | Yes | No | No | Limited |
| PITR restore | Yes | No | No | Limited |
| Granular mailbox restore | Yes | No | No | Proprietary, expensive |
| Granular document by UNID | Yes | No | No | No |
| NSF → EML / MBOX export | Yes | No | No | No |
| DBIID continuity tracking | Yes | No | No | No |
| Parallel NSF backup | Yes | No | No | No |
| Compression (zstd/lz4) | Yes (in-stream) | Yes (gzip, delegated) | Yes | Yes |
| Bandwidth throttle | Yes | Yes | Yes | Yes |
| Prometheus metrics | Yes | No | No | No |
| PodHeitor Backup compatible | Yes | N/A | N/A | N/A |
| Open-source platform base | Yes (Bacula CE) | No | No | No |
| Windows MSI + Linux RPM/DEB | Yes | Yes | Yes | Yes |
| Domino 9 / 10 / 12 / 14 | All 4 | N/A | N/A | Varies |
16.2 Cost comparison
Special offer. Bring your renewal proposal for Veeam, Commvault, NetBackup, or any other enterprise backup platform. We will produce a written head-to-head proposal targeting at least 50% savings, with stronger Notes/Domino-specific functionality. Contact heitor@opentechs.lat.
| Solution | Typical annual cost | Notes/Domino support |
|---|---|---|
| PodHeitor Backup + PodHeitor plugin | Significantly less | Full native (this plugin) |
| Bacula Enterprise | Often > US$ 10,000/year | None (no Notes/Domino plugin) |
| Veeam Data Platform | Often > US$ 5,000/year | None (file-level scripts only) |
| Commvault (with Notes agent) | Often > US$ 20,000/year | Limited (proprietary agent, separate cost) |
| NetBackup for Lotus Notes (EOL) | Legacy / end-of-life | Discontinued |
Prices vary by environment size and negotiated contracts. Contact heitor@opentechs.lat for a specific comparison against your current renewal proposal.
17. Roadmap
The plugin follows a 12-phase development plan. Phases 0 and 1 (bootstrap and ABI scaffold with canal interno handshake) are complete. The remaining phases:
- Phase 2 — Domino FFI + auth + catalog. NotesInitExtended, ID file authentication, NSF directory enumeration, catalog.nsf inventory.
- Phase 3 — Online Full backup. NSFBackupStart / NSFBackupStop, DBIID capture, parallel_nsf, first E2E Bacula job.
- Phase 4 — Transaction-log incrementals. NSFBeginArchivingLogs, TXN extent enumeration, log-sequence marker management.
- Phase 5 — Differential backup. Full-baseline delta; Full + Diff restore path.
- Phase 6 — Restore + PITR. NSFTakeDatabaseOffline, NSFRecoverDatabases, target_time replay, alternate-location restore.
- Phase 7 — Granular restore. Mailbox, document by UNID, document by subject regex, design element; both libnotes and native reader paths.
- Phase 8 — NSF → EML / MBOX converter. podheitor-notes-convert CLI; streaming, parallel; PST deferred to v1.1.
- Phase 9 — Verify / fixup / compact. Post-restore integrity gates; fixup -F -L and compact -c integration.
- Phase 10 — Performance & VLDB tuning. Criterion benchmarks, large-environment stress tests, canal interno chunk size optimisation.
- Phase 11 — Packaging & GA. RPM, DEB, MSI packages; operator guide; Bacularis UI panel.
- Phase 12 — DR sibling daemon (post-GA). podheitor-notes-receiver for continuous log replication to a secondary Domino server; sub-minute RPO independent of Bacula scheduling cycle.
Additionally planned for post-GA:
- PST export (v1.1). podheitor-notes-convert PST writer behind –enable-pst feature flag.
- GDD deduplication (Phase 11 soak). gdd=on enabled by default after validation; 5–15× dedup ratio on mail corpora.
- ARM64 packages. For cloud-native ARM servers and Raspberry Pi.
- Extended metrics. Per-NSF backup history, chain health scores, automated alerting thresholds.
No specific release dates are committed. Feature direction is guided by customer feedback and lab findings.
18. Conclusion
The PodHeitor Notes/Domino Backup Plugin for Bacula fills a gap that no other product in the Bacula ecosystem — including Bacula Enterprise — addresses: application-consistent, online backup of HCL Domino servers with transaction-log incrementals, point-in-time recovery, and granular restore at the mailbox and document level.
For the tens of thousands of organisations still running Domino in government, finance, telecoms, and healthcare, this plugin provides the only credible path to a defensible backup posture on an open-source platform. The combination of PodHeitor Backup’s mature, scalable infrastructure and the PodHeitor plugin’s Domino-native integration delivers enterprise-grade DR capabilities at a fraction of the cost of proprietary alternatives — without requiring a platform migration, a vendor lock-in, or a budget approval for a six-figure contract.
The Rust architecture, phased development model, and canal interno isolation guarantee that the plugin will remain maintainable and testable across Domino and Bacula version changes. The DBIID continuity tracking, auto-level-promotion, and granular restore capabilities address the daily operational realities of Domino administration in a way that no generic file backup tool can.
To get started:
- Download the plugin: https://podheitor.com
- Request a quote or demo: heitor@opentechs.lat
- Phone / WhatsApp: +1 786 726-1749 | +55 61 98268-4220
Licensing
PodHeitor Notes/Domino Backup Plugin is proprietary software distributed by subscription. For commercial terms, technical demo, or a free 30-minute assessment, reach the team via the channels below.
Ready to evaluate?
- 💬 WhatsApp: +1 (786) 726-1749
- ✉️ Email: heitor@opentechs.lat
- 🩺 Free assessment — 30 min with Heitor Faria
19. Contact information
| Author | Heitor Faria |
| Company | PodHeitor International |
| Website | https://podheitor.com |
| heitor@opentechs.lat | |
| Phone / WhatsApp | +1 786 726-1749 |
| Phone / WhatsApp (BR) | +55 61 98268-4220 |
| Product page | https://podheitor.com/notes-domino-plugin |
| Support | heitor@opentechs.lat |
20. Legal / copyright
© 2026 Heitor Faria — all rights reserved.
The PodHeitor Notes/Domino Backup Plugin for Bacula is proprietary software. Unauthorised copying, distribution, modification, or reverse engineering is strictly prohibited. A commercial license is required for production use.
Bacula® is a registered trademark of Kern Sibbald and the PodHeitor Backup. HCL Notes®, HCL Domino®, IBM Notes®, Lotus Notes®, and IBM Lotus Domino® are trademarks or registered trademarks of HCL Technologies Ltd. and/or International Business Machines Corporation. All other trademarks are the property of their respective owners.
The plugin dynamically loads the HCL Notes C API (libnotes) at runtime. The HCL SDK is operator-installed under the HCL EULA; it is not bundled with or redistributed by this product.
This document is provided for informational purposes. Performance figures are design targets derived from controlled lab measurements; actual results in production environments will vary depending on hardware, network conditions, Domino configuration, and database characteristics.
Contact for licensing: heitor@opentechs.lat | https://podheitor.com | +1 786 726-1749 | +55 61 98268-4220
PodHeitor Notes/Domino Backup Plugin for Bacula — v0.1.0 — © 2026 Heitor Faria — all rights reserved — https://podheitor.com
Disponível em:
Português (Portuguese (Brazil))
English
Español (Spanish)