Technical Whitepaper — Version 2.0.0 — May 2026
Author: Heitor Faria · Website: https://podheitor.com · Email: heitor@opentechs.lat · Phone / WhatsApp: +1 786 726-1749 | +55 61 98268-4220
Special offer. Bring your renewal proposal for any commercial enterprise backup platform — Veeam, Commvault, NetBackup, or others. We will benchmark a head-to-head proposal targeting at least 50% savings with stronger Nutanix AHV-specific functionality. Contact heitor@opentechs.lat for a written quote.
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
Nutanix AHV has emerged as one of the fastest-growing hypervisor platforms in enterprise data centres, with organisations worldwide migrating virtualised workloads onto Nutanix hyperconverged infrastructure. Despite this rapid adoption, the open-source backup ecosystem has lacked a production-grade, native integration capable of leveraging AHV’s built-in Changed Block Tracking (CBT) engine, Prism Central’s API, and iSCSI block-level data paths. Administrators have been forced to accept file-level backups of running VMs, expensive proprietary agent-based solutions, or custom scripted glue around Nutanix CLI utilities — all of which compromise recovery guarantees, operational simplicity, or budget.
The PodHeitor Nutanix AHV Backup Plugin for Bacula closes this gap entirely. Built in pure Rust on both sides of the Bacula metaplugin boundary, it delivers enterprise-class backup, restore, cross-cluster DR replication, and failover orchestration for Nutanix AHV environments, using only Bacula Community Edition 15.0.3+ and a standard Prism RBAC service account. The plugin leverages Nutanix v3 and v4 APIs for snapshot lifecycle management, iSCSI VG attachment for high-throughput block streaming, and a PSK-authenticated TCP transport for continuous DR replication — all driven by standard Bacula Job and FileSet configuration with no external scripts, no cron glue, and no vendor-specific backup agents running inside guest VMs.
From a technical depth perspective, this is the most complete open-source Nutanix AHV backup integration available today. It delivers Full and Incremental backups via Nutanix CBT snapshots, byte-identical same-cluster and alternate-cluster restore, cross-vendor INBOUND restore via qemu-img conversion (enabling Proxmox VE, VMware vSphere, and Hyper-V as restore targets), and a full DR replication stack with seed, bitmap-push delta, bandwidth throttling, mid-push reconnect, and four failover modes. A standalone health-check verb validates Prism reach, RBAC posture, and DSIP routing before the first job runs.
From a commercial perspective, the plugin delivers all these capabilities at a fraction of the cost of enterprise alternatives. Veeam Data Platform for Nutanix starts at approximately US$ 5,000/year for small deployments. Commvault and NetBackup licenses routinely exceed US$ 10,000/year. The PodHeitor plugin offers a comparable — and in some dimensions, superior — feature set for Nutanix AHV environments at 50% or more savings. For any organisation running Nutanix AHV with Bacula Community, this plugin is the most cost-effective path to a defensible, production-grade backup and DR posture.
2. Introduction & market context
2.1 Nutanix AHV in enterprise production today
Nutanix AHV (Acropolis Hypervisor) has grown from a bundled hypervisor in the Nutanix hyperconverged stack into a first-class enterprise virtualisation platform. According to Nutanix’s own market data, AHV now accounts for the majority of new workloads deployed on Nutanix clusters, with organisations across financial services, healthcare, government, education, and manufacturing choosing AHV over VMware vSphere — particularly following Broadcom’s acquisition of VMware and the subsequent licensing changes that sent many customers looking for alternatives.
Key technical characteristics of AHV that make it attractive to enterprise operators include:
- Hyperconverged architecture eliminating dedicated SAN/NAS infrastructure
- Native CBT (Changed Block Tracking) through Prism snapshots for efficient incremental operations
- Prism Central multi-cluster management API (v3 and v4) offering programmatic control over VM lifecycle
- iSCSI Volume Group (VG) block export for out-of-band data access without guest interaction
- AOS (Acropolis Object Store) distributed storage delivering sub-millisecond latency at scale
- Built-in data replication and snapshot features enabling native DR workflows
2.2 Why backing up AHV VMs is challenging
Despite AHV’s rich API surface, protecting AHV workloads with open-source backup tools has remained difficult for several reasons:
- API complexity. Nutanix operates two parallel API generations (v3 and v4), each with different endpoint shapes, authentication models, and pagination patterns. Tools that work against v3 may silently fail against v4 clusters, and vice versa.
- iSCSI attachment lifecycle. Accessing VM disk data at block level requires creating a Volume Group, cloning the snapshot disk into it, attaching via iSCSI to the backup host, streaming the data, and then cleaning up all resources — including on panic or job abort. A single missed cleanup leaves orphaned VGs and snapshots on the cluster.
- CBT state management. Efficient incrementals require tracking per-disk snapshot references across jobs, handling reference invalidation when retention policies delete prior snapshots, and managing disk resizes that invalidate the delta chain.
- Cross-cluster and cross-vendor restore. Alternate-cluster restore requires image upload to a different Nutanix cluster, and cross-vendor restore (to Proxmox VE, VMware vSphere, or Hyper-V) requires qemu-img conversion of raw disk images into the target format.
- DR replication. Continuous DR replication with bandwidth throttling, mid-push reconnect, and failover orchestration is a multi-component system that most open-source tools do not address at all.
2.3 The gap in the open-source ecosystem
| Tool | AHV support | Verdict |
|---|---|---|
| Bacula Community (native, no plugin) | File-level only — backs up raw VM virtual disk files via the host filesystem | Unsafe — no crash consistency, no CBT, no catalog integration |
| Bareos | No native AHV integration | File-level only; same risks as Bacula native |
| Amanda | No native AHV integration | File-level only |
| Nutanix Mine | Native, but requires separate Mine cluster hardware | High additional cost; no Bacula integration |
| Custom shell scripts via Prism API + acli | Partial — no catalog, no retry, no monitoring | Not production-grade |
The gap is structural: no open-source-compatible solution integrated with Nutanix AHV at the API level — with CBT snapshots, iSCSI block streaming, multi-cluster support, and DR replication — until now.
2.4 The PodHeitor approach
The plugin follows the same design philosophy as the broader PodHeitor plugin family: pure Rust on both sides of the metaplugin boundary for memory safety and zero runtime dependencies, phase-gated development with automated regression tests, RAII-based resource cleanup that is panic-safe (no orphaned snapshots or Volume Groups), and a PTCOMM protocol architecture that isolates all Nutanix API interaction in a subprocess so that backend failures cannot corrupt the Bacula FD process.
3. Architecture overview
3.1 Two-component design
The plugin ships two binaries in a single package:
| Component | File | Role |
|---|---|---|
| Bacula FD plugin (cdylib) | /opt/bacula/plugins/podheitor-nutanix-fd.so |
Loaded by bacula-fd at runtime; implements the Bacula plugin API; minimal and stable |
| Backend binary | /opt/bacula/bin/podheitor-nutanix-backend |
Forked per-job by the cdylib; performs all Nutanix API interaction, iSCSI operations, and DR transport |
This split delivers three critical advantages:
- Isolation. The cdylib is a minimal Rust cdylib with no Bacula AGPLv3 source statically linked — copyright-clean. All Prism API calls, iSCSI attach/detach, and DR transport live in the backend. A backend crash or hang cannot corrupt the Bacula FD process.
- Upgradability. The backend binary can be updated without restarting
bacula-fd. Only the cdylib touches the Bacula plugin ABI. - Testability. The backend can be exercised directly (
--standalone health-check) without involving Bacula, and all unit tests run against the backend binary in isolation.
3.2 PTCOMM protocol
The cdylib and backend communicate over the subprocess stdin/stdout channel using PTCOMM (PodHeitor Transport Communications), a length-tagged binary framing protocol shared across the entire PodHeitor plugin family:
┌────────────────────────────────────────────────────────┐
│ PTCOMM Frame (8-byte header + payload) │
│ │
│ Offset Size Field │
│ ────── ──── ───── │
│ 0 4 Magic (0x50544300) │
│ 4 4 Payload length (u32, big-endian) │
│ 8 N Payload (JSON command or binary data) │
└────────────────────────────────────────────────────────┘
Packet types (direction cdylib ↔ backend):
I-packet — Info/log line (backend → cdylib → Bacula job log)
E-packet — Error message (backend → cdylib)
D-packet — Data chunk (bidirectional, for backup/restore streams)
C-packet — Control / command + parameters (cdylib → backend)
OK-ack — Metaplugin acknowledgement (backend → cdylib, after createFile-EOD
and after data-stream-EOD, per Bacula metaplugin protocol)
The phase machine in the backend processes the PTCOMM stream through well-defined states: Handshake → JobInfo → Mode dispatch (Backup / Restore / DR) → Data streaming → Cleanup → Exit. Each state transition is logged at verbose=2.
3.3 Architecture diagram
┌───────────────────────────────────────────────────────────────────┐
│ Bacula File Daemon (bacula-fd) │
│ │
│ ┌───────────────────────────────────────────────────────────┐ │
│ │ podheitor-nutanix-fd.so (cdylib — pure Rust, ~50 LOC) │ │
│ │ │ │
│ │ bEventJobStart ──► fork backend ──► write job params │ │
│ │ bEventBackupCommand ──► send C-packet (mode=backup) │ │
│ │ bEventGetMoreData ◄── receive D-packets (disk stream) │ │
│ │ bEventRestoreCommand ──► send C-packet (mode=restore) │ │
│ │ bEventSetFileAttributes ◄── write reconstructed disk │ │
│ └──────────────────────────┬────────────────────────────────┘ │
│ │ stdin/stdout (PTCOMM frames) │
│ ┌──────────────────────────▼────────────────────────────────┐ │
│ │ podheitor-nutanix-backend (subprocess — Rust binary) │ │
│ │ │ │
│ │ ┌────────────────┐ ┌─────────────────┐ ┌──────────┐ │ │
│ │ │ Backup (F2/F3) │ │ Restore (F4/F5) │ │ DR (F8) │ │ │
│ │ │ Full + CBT Inc │ │ Same / alt-clust│ │ Repl+F/O │ │ │
│ │ └───────┬────────┘ └────────┬────────┘ └────┬─────┘ │ │
│ │ │ │ │ │ │
│ │ ┌───────▼────────────────────▼─────────────────▼──────┐ │ │
│ │ │ Prism Module (v3 / v4 auto-detect) │ │ │
│ │ │ auth.rs · cluster.rs · vms.rs · snapshots.rs │ │ │
│ │ │ volume_groups.rs · crt.rs · rate_limiter.rs │ │ │
│ │ └─────────────────────┬────────────────────────────────┘ │ │
│ │ │ HTTPS (Prism Central 9440) │ │
│ │ ┌─────────────────────▼────────────────────────────────┐ │ │
│ │ │ iSCSI Layer (iscsi.rs + disk_reader.rs) │ │ │
│ │ │ VG attach → O_DIRECT read → PHCBT01 stream │ │ │
│ │ └──────────────────────────────────────────────────────┘ │ │
│ │ │ │
│ │ State: /opt/bacula/working/nutanix-repl/ │ │
│ │ Config: /opt/bacula/etc/nutanix.conf │ │
│ └───────────────────────────────────────────────────────────┘ │
└───────────────────────────────────────────────────────────────────┘
│ │
│ Bacula protocol (TCP) │ PSK-TCP (DR, port 9848)
▼ ▼
┌──────────────────────┐ ┌────────────────────────────┐
│ Bacula Storage │ │ DR Receiver │
│ Daemon + Catalog │ │ podheitor-nutanix-backend │
└──────────────────────┘ │ --standalone receiver │
│ (systemd unit per VM) │
└────────────────────────────┘
3.4 PHCBT01 delta stream format
Incremental backups are streamed in the PHCBT01 format — PodHeitor’s binary delta stream format shared across all VM-style plugins (Proxmox, VMware, Nutanix). PHCBT01 encodes only CHANGED regions from the CBT response, with a ZeroMap sidecar for efficient sparse handling. This format is what enables byte-identical chain reconstruction at restore time without re-reading unchanged blocks from storage.
3.5 State management
CBT reference state is stored per-disk under /opt/bacula/working/nutanix-repl/:
/opt/bacula/working/nutanix-repl/
├── vm-prod-01/
│ ├── state.json # last_snapshot_id per disk, backup level
│ └── seed.bin # DR seed image (if DR configured)
├── vm-prod-02/
│ └── state.json
└── vm-web-01/
└── state.json
State is written atomically (write-rename) so that a crash between snapshot disarm and prior-reference cleanup at most leaves one leaked snapshot, which is operator-recoverable via acli snapshot.list. A stale-reference sweep runs on backend startup (job_cleanup_on_abort=yes) to recover orphaned iSCSI sessions and VGs from any prior abnormal exit.
4. Backup modes deep dive
4.1 Full backup (F2)
How it works
A Full backup creates a Nutanix v3/v4 application-consistent (or crash-consistent, per application_consistent=) snapshot of the target VM(s), then for each virtual disk: creates a Volume Group (VG) on the cluster, clones the snapshot disk into the VG, attaches the VG to the FD host via iSCSI, reads the raw block device using O_DIRECT (1 MiB chunks, 4 KiB-aligned), streams it to Bacula as a virtual file @nutanix/<vm>/disks/disk-<N>.raw, and then cleans up the VG and snapshot in strict reverse order via RAII Drop chains.
Prism Central API
└── vm.snapshot_create (v3/v4)
└── VG.create + VG.clone_disk
└── iSCSI attach → O_DIRECT read
└── PTCOMM D-packets → Bacula Storage
Concurrency
Multiple VMs and multiple disks per VM are processed concurrently. concurrent_vms=2 controls the number of VMs being pre-staged simultaneously; concurrent_disks=4 controls parallel disk reads per VM. The backend uses Rust’s crossbeam-channel for bounded producer/consumer queuing between the iSCSI reader and the PTCOMM emitter.
Pros and cons
| Aspect | Assessment |
|---|---|
| Coverage | Excellent — entire VM disk image captured |
| Consistency | Excellent — Nutanix snapshot is crash-consistent or app-consistent |
| Speed | Good — iSCSI block streaming; O_DIRECT bypasses page cache |
| Restore simplicity | Excellent — single stream, no chain dependencies |
| Bandwidth | High — full disk size; combine with max_bandwidth_mbps if needed |
| Chain dependency | None — self-contained |
4.2 Incremental backup via CBT (F3)
How it works
After a Full backup, subsequent jobs at Level=Incremental use Nutanix’s Changed Regions Tracking (CRT) API to query only the disk regions that changed since the reference snapshot. The backend fetches the changed-regions list, coalesces adjacent CHANGED runs, splits them at chunk boundaries, and emits a PHCBT01-encoded stream containing only the modified data. The prior reference snapshot is preserved until the new snapshot is confirmed, then atomically promoted as the new reference.
Full job (JobID=100):
snapshot_ref_id = snap-abc → stored in state.json
Incremental job (JobID=101):
CRT query: GET /vms/{uuid}/snapshots/{snap-abc}/changed_regions
→ coalesce + split → PHCBT01 stream (only changed blocks)
→ Bacula storage (much smaller than Full)
→ new snapshot promoted as reference, snap-abc deleted
PHCBT01 stream internals
- Header: 20 bytes — magic
PHCBT01, disk size, region count - CHANGED records: 12 bytes each — offset, length, followed by region payload
- ZeroMap sidecar: sparse-map of ZERO regions omitted from the stream (space-efficient)
- Size pre-computation: the encoder pre-sums the declared STAT size so Bacula’s catalog receives an accurate file size before streaming begins
Pros and cons
| Aspect | Assessment |
|---|---|
| Coverage | Excellent — captures all changed blocks since reference snapshot |
| Speed | Excellent — typically 5–10× smaller than Full for active VMs |
| Restore | Good — requires chain reconstruction (Full + N Incrementals) |
| Chain dependency | Required — reference snapshot must not be externally deleted |
| API version | v3 (primary) + v4 (full support from F3.5 — DRP-based CRT) |
4.3 DR replication (F8)
How it works
DR replication operates in two phases: an initial seed push and recurring bitmap-push delta cycles. The SOURCE side runs mode=seed to push the full disk image to the DR receiver over PSK-authenticated TCP on port 9848 (configurable). Subsequent mode=bitmap-push cycles push only the CBT-identified changed blocks as a compact delta bitmap. The receiver stores seeds and bitmaps under its own state_dir and can reconstruct the full point-in-time image at any cycle boundary.
SOURCE (primary cluster) DR SITE (receiver host)
───────────────────────────── ─────────────────────────────
mode=seed mode=receiver
VG attach → stream full disk ──────► Write seed to state_dir
Bitmap snapshot taken Seed confirmed
mode=bitmap-push (every 300s)
CRT delta → compact bitmap ──────► Apply delta to seed image
mid-push reconnect on failure Cycle state persisted
Failover modes (F8.4)
Four failover modes cover the full DR lifecycle:
failover-test— Boots a clone in an isolated VLAN; receiver state unchanged. Use for regular DR drills without affecting production.failover-planned— Quiesces the source VM, promotes the receiver, powers on the DR copy. Use for planned maintenance failovers.failover-unplanned— Promotes the receiver without source quiesce. Use when the source cluster is unreachable.failover-undo— Tears down afailover-testclone and releases state-dir disk space.failover-perm— Final cutover; receiver becomes the new source of truth. Re-pair to reverse direction.
4.4 Cross-vendor INBOUND restore (F6)
The cross_restore=yes flag enables restoring a Nutanix AHV backup to a non-AHV hypervisor. The backend converts the raw disk image to the target format using qemu-img convert and uploads it to the target platform’s image service. Supported targets include Proxmox VE (qcow2), VMware vSphere (vmdk), and Microsoft Hyper-V (vhd/vhdx). This is particularly valuable for disaster recovery to a dissimilar hypervisor or for migration testing.
5. Feature matrix
| Feature | Support | Notes |
|---|---|---|
| Full backup (F2) | Yes | iSCSI block streaming + RAII cleanup |
| Incremental backup via CBT (F3) | Yes | PHCBT01 delta stream; v3 + v4 CRT |
| Multi-VM concurrency | Yes | concurrent_vms= (default 2) |
| Multi-disk concurrency per VM | Yes | concurrent_disks= (default 4) |
| VM glob/pattern selection | Yes | vm=prod-*, CSV, UUID |
| VM exclusion patterns | Yes | exclude=temp-* |
| Application-consistent snapshot | Yes | application_consistent=true|try|false |
| Same-cluster restore (F4) | Yes | Native AHV VM creation |
| Alternate-cluster restore (F5) | Yes | Image upload + VM materialize on target cluster |
| Cross-vendor INBOUND restore (F6) | Yes | qemu-img to Proxmox/vSphere/Hyper-V |
| Cross-cluster vCPU/memory override | Yes | --vcpu / --memory-mib on restore |
| Network remapping on restore | Yes | network_map=src=dst,... |
| Selective disk restore | Yes | restore_disks_only=disk-0,disk-2 |
| Power-on after restore | Yes | power_on=yes |
| DR replication seed (F8.1) | Yes | Full disk push over PSK-TCP |
| DR bitmap-push delta (F8.2) | Yes | CBT-derived delta push with reconnect |
| Bandwidth throttling | Yes | max_bandwidth_mbps= token-bucket |
| Mid-push reconnect | Yes | Resumes from last-acked cycle boundary |
| Failover-test (drill) | Yes | Isolated VLAN clone; state unchanged |
| Failover-planned | Yes | Quiesce + promote + power-on target |
| Failover-unplanned | Yes | Promote without source quiesce |
| Failover-undo | Yes | Tear down test clone |
| Failover-perm (cutover) | Yes | Receiver becomes new source of truth |
| Multi-instance DR receiver | Yes | systemd template; per-VM state isolation |
| Prism v3 API | Yes | Primary path |
| Prism v4 API | Yes | Auto-detected; falls back to v3 |
| Multi-cluster Prism Central | Yes | cluster= name or UUID |
| TLS verification | Yes | Secure by default; prism_insecure=yes for lab only |
| mTLS for DR channel | Yes | dr_auth_cert / dr_auth_key / dr_ca_cert |
| Standalone health-check | Yes | RBAC + DSIP probes pre-flight |
| Dry-run size estimate | Yes | mode=estimate |
| Instant Recovery (F9) | Roadmap | Separate Bacula-SD plugin (granular-restore + IR) |
| File-level granular restore (F10) | Roadmap | Separate Bacula-SD plugin |
| ARM64 support | Roadmap | x86_64 only in v2.0.0 |
6. Installation guide
6.1 Prerequisites
- Bacula Community Edition File Daemon 15.0.3+ running on Oracle Linux 9 / RHEL 9 / Ubuntu 22.04 / Ubuntu 24.04 (x86_64)
qemu-imgavailable on PATH (required for F5 / F6 cross-cluster and cross-vendor restore)iscsiadm(open-iscsi) reachable whendata_path=iscsiis used (default)- Network connectivity from the FD host to Prism Central HTTPS (default port 9440) and the cluster Data-Services IP (DSIP) over iSCSI port 3260
- A Prism RBAC service account with Backup Admin, VM Admin, and Cluster Admin (read-only) roles on the cluster scope
6.2 Package installation
RPM (Oracle Linux 9 / RHEL 9 / Rocky / Alma)
sudo dnf install ./podheitor-nutanix-plugin-2.0.0-1.el9.x86_64.rpm
DEB (Ubuntu 22.04 / Ubuntu 24.04)
sudo apt install ./podheitor-nutanix-plugin_2.0.0-1_amd64.deb
Tarball (any glibc 2.34+ x86_64)
tar -xzf podheitor-nutanix-plugin-2.0.0.tar.gz
cd podheitor-nutanix-plugin-2.0.0
sudo ./install.sh
All three install paths place the following files on the system:
| Path | Contents |
|---|---|
/opt/bacula/plugins/podheitor-nutanix-fd.so |
FD plugin loaded by bacula-fd |
/opt/bacula/bin/podheitor-nutanix-backend |
Backend binary |
/opt/bacula/etc/nutanix.conf.example |
Annotated configuration template |
/opt/bacula/working/nutanix-repl/ |
State directory (CBT references, seeds, bitmaps) |
/lib/systemd/system/podheitor-nutanix-receiver.service |
DR receiver systemd unit (single-instance) |
/lib/systemd/system/podheitor-nutanix-receiver@.service |
DR receiver systemd template (per-VM) |
/etc/logrotate.d/podheitor-nutanix |
Log rotation configuration |
6.3 Initial configuration
sudo cp /opt/bacula/etc/nutanix.conf.example /opt/bacula/etc/nutanix.conf
sudoedit /opt/bacula/etc/nutanix.conf
sudo chown root:bacula /opt/bacula/etc/nutanix.conf
sudo chmod 0640 /opt/bacula/etc/nutanix.conf
Minimum required keys in nutanix.conf:
prism_central = pc.example.com
user = backup-svc
passfile = /opt/bacula/etc/nutanix.secrets
state_dir = /opt/bacula/working/nutanix-repl
Place the Prism password in /opt/bacula/etc/nutanix.secrets (mode 0600, owner root:bacula):
password = your-prism-service-account-password
6.4 Pre-flight validation
/opt/bacula/bin/podheitor-nutanix-backend
--standalone health-check
--config /opt/bacula/etc/nutanix.conf
The health-check probe validates in order: (1) Prism Central TLS reachability, (2) RBAC authentication, (3) cluster enumeration, (4) RBAC role confirmation, (5) DSIP discovery and iSCSI port 3260 probe. Exit code 0 means the FD host is ready to run backup jobs.
6.5 DR receiver setup (optional)
For single-instance DR (one receiver protecting all VMs):
sudoedit /opt/bacula/etc/nutanix.conf # set dr_auth_token, state_dir
sudo systemctl enable --now podheitor-nutanix-receiver
sudo systemctl status podheitor-nutanix-receiver
For per-VM isolation (recommended in production):
sudo cp /opt/bacula/etc/nutanix.conf /opt/bacula/etc/nutanix-vm-prod-01.conf
sudoedit /opt/bacula/etc/nutanix-vm-prod-01.conf # distinct dr_port and state_dir
sudo systemctl enable --now podheitor-nutanix-receiver@vm-prod-01.service
7. Configuration reference
7.1 Connection parameters
| Key | Type | Default | Description |
|---|---|---|---|
prism_central |
host[:port] | — | Prism Central hostname or IP, reachable from the FD host |
prism_port |
int | 9440 |
Prism Central HTTPS port |
prism_api_version |
auto|v3|v4 |
auto |
auto probes v4 first; falls back to v3 |
user |
string | — | Prism RBAC user (must exist in IAM, not just LDAP pass-through) |
password |
string | — | Inline password; prefer passfile for security |
passfile |
path | — | Path to file containing password = …; mode 0600 |
prism_insecure |
bool | no |
Skip TLS certificate verification; lab use only. Emits a WARNING on every job. |
cluster |
string | — | Cluster name or UUID when Prism Central manages multiple clusters |
7.2 Backup selection parameters
| Key | Type | Default | Description |
|---|---|---|---|
vm |
csv/glob | * |
VM UUID, exact name, or glob pattern (prod-*). Multiple values comma-separated. |
exclude |
csv/glob | — | Exclusion patterns applied after vm= matching |
application_consistent |
true|try|false |
try |
try: attempt app-consistent, fall back gracefully; true: fail job if app-consistent is unavailable |
7.3 Data path parameters
| Key | Type | Default | Description |
|---|---|---|---|
data_path |
auto|iscsi|rest |
auto |
Transport selection. iscsi is recommended for throughput. |
dsip |
IPv4 | probed | Explicit Data-Services IP override. Set this in production to avoid probe failures. |
iscsi_initiator |
string | auto | iSCSI IQN override. Auto-detected from /etc/iscsi/initiatorname.iscsi. |
concurrent_vms |
int | 2 |
Maximum number of VMs being pre-staged (snapshot + VG attach) simultaneously |
concurrent_disks |
int | 4 |
Maximum parallel disk reads per VM |
chunk_size_mib |
int | 1 |
iSCSI read chunk size in MiB. Larger values improve sequential throughput; must be 4 KiB-aligned. |
prism_rate_rpm |
int | 30 |
Client-side Prism API rate cap (requests per minute) |
7.4 Restore parameters (F4 / F5 / F6)
| Key | Type | Default | Description |
|---|---|---|---|
mode |
enum | backup |
See section 7.6 for full list of mode values |
where |
path | — | Bacula restore prefix; normally set automatically by the Director |
restore_vm_name |
string | <orig>-restored |
Name for the restored VM on the target cluster |
target_cluster |
string | source | Alternate cluster name or UUID for F5 cross-cluster restore |
target_container |
string | source | AHV storage container on the target cluster |
network_map |
csv | — | Source-to-destination subnet remapping: src=dst,src2=dst2 |
power_on |
bool | no |
Power on the restored VM after creation completes |
restore_disks_only |
csv | — | Restore only listed disks: disk-0,disk-2 |
--vcpu |
int | source | Override vCPU count on cross-cluster restore for rightsizing |
--memory-mib |
int | source | Override memory (MiB) on cross-cluster restore for rightsizing |
cross_restore |
bool | yes |
Accept foreign-hypervisor streams for F6 restore |
7.5 DR replication parameters (F8)
| Key | Type | Default | Description |
|---|---|---|---|
dr_host |
host | — | DR receiver hostname or IP (set on the SOURCE side) |
dr_port |
int | 9848 |
DR receiver TCP port |
dr_auth_token |
string | — | Pre-shared key for HMAC-SHA256 authentication. Generate with: openssl rand -hex 32 |
dr_auth_cert |
path | — | mTLS client certificate path (optional, for mutual TLS) |
dr_auth_key |
path | — | mTLS client key path |
dr_ca_cert |
path | — | CA certificate for receiver TLS verification |
dr_restore_points |
int | 7 |
Number of DR recovery points retained on the receiver |
max_bandwidth_mbps |
int | 0 |
DR transport bandwidth cap in Mbit/s. 0 = unlimited. Token-bucket algorithm. |
push_interval |
int | 300 |
Seconds between bitmap-push delta cycles |
push_apply_remote |
bool | yes |
Apply incoming deltas write-through on the receiver (recommended) |
7.6 Logging and scratch parameters
| Key | Type | Default | Description |
|---|---|---|---|
verbose |
0–3 | 1 |
Log verbosity: 0=errors only, 1=normal, 2=debug, 3=trace (very chatty) |
debug_log |
path | /opt/bacula/working/podheitor-nutanix-backend.log |
Backend log file path |
working_dir |
path | /opt/bacula/working |
Scratch root for temporary files |
state_dir |
path | /opt/bacula/working/nutanix-repl |
Persistent state directory (CBT references, seeds, bitmaps) |
job_cleanup_on_abort |
bool | yes |
RAII stale-resource sweep on backend startup (iSCSI sessions, VGs, orphaned snapshots) |
7.7 Mode values
| Value | Phase | Use |
|---|---|---|
backup |
F2/F3 | Default — Full or Incremental per the Bacula Job Level |
restore |
F4 | Native restore on same cluster |
estimate |
F2 | Dry-run size estimate without streaming data |
receiver |
F8 | Listen for SEED + DELTA frames on the DR receiver |
seed |
F8 | Push initial full-disk seed to the receiver |
bitmap-push |
F8 | Push CBT delta bitmap to receiver |
failover-test |
F8.4 | Boot a clone in an isolated VLAN; receiver state unchanged |
failover-planned |
F8.4 | Quiesce source → promote receiver → power-on target |
failover-unplanned |
F8.4 | Promote receiver without source quiesce (source unreachable) |
failover-undo |
F8.4 | Tear down a failover-test clone and release state-dir space |
failover-perm |
F8.4 | Final cutover; receiver becomes the new source of truth |
8. FileSet examples
8.1 Basic full + incremental backup of all VMs
FileSet {
Name = "Nutanix-All-VMs"
Include {
Options {
signature = SHA1
compression = LZ4
}
Plugin = "podheitor-nutanix: prism_central=pc.example.com
passfile=/opt/bacula/etc/nutanix.secrets vm=*"
}
}
8.2 Production VMs only, application-consistent, with concurrency
FileSet {
Name = "Nutanix-Prod-AppConsistent"
Include {
Options {
signature = SHA256
compression = LZ4
}
Plugin = "podheitor-nutanix: prism_central=pc.prod.example.com
passfile=/opt/bacula/etc/nutanix.secrets
vm=prod-* exclude=prod-test-*
application_consistent=true
concurrent_vms=4 concurrent_disks=8
cluster=prod-cluster-01"
}
}
8.3 Alternate-cluster restore (cross-cluster DR)
FileSet {
Name = "Nutanix-CrossCluster-Restore"
Include {
Options { signature = SHA1 }
Plugin = "podheitor-nutanix: prism_central=pc.dr.example.com
passfile=/opt/bacula/etc/nutanix.secrets
mode=restore
target_cluster=dr-cluster-01
target_container=default-container
network_map=10.10.0.0/24=10.20.0.0/24
power_on=yes
restore_vm_name=prod-vm-dr-copy"
}
}
8.4 DR replication seed + bitmap-push schedule
# FileSet for DR seed push (run once to establish the seed)
FileSet {
Name = "Nutanix-DR-Seed"
Include {
Options { signature = SHA1 }
Plugin = "podheitor-nutanix: prism_central=pc.example.com
passfile=/opt/bacula/etc/nutanix.secrets
vm=prod-db-01 mode=seed
dr_host=dr-receiver.example.com
dr_port=9848
dr_auth_token=YOUR_PSK_HERE"
}
}
# FileSet for recurring bitmap-push (scheduled every 5 minutes)
FileSet {
Name = "Nutanix-DR-BitmapPush"
Include {
Options { signature = SHA1 }
Plugin = "podheitor-nutanix: prism_central=pc.example.com
passfile=/opt/bacula/etc/nutanix.secrets
vm=prod-db-01 mode=bitmap-push
dr_host=dr-receiver.example.com
dr_port=9848
dr_auth_token=YOUR_PSK_HERE
max_bandwidth_mbps=200
push_interval=300"
}
}
8.5 Cross-vendor INBOUND restore to Proxmox VE
FileSet {
Name = "Nutanix-To-Proxmox-Restore"
Include {
Options { signature = SHA1 }
Plugin = "podheitor-nutanix: prism_central=pc.example.com
passfile=/opt/bacula/etc/nutanix.secrets
mode=restore cross_restore=yes
--vcpu=4 --memory-mib=8192
restore_vm_name=migrated-from-nutanix"
}
}
8.6 Corresponding Bacula Job definitions
Job {
Name = "NutanixProdFull"
Type = Backup
Level = Full
Client = nutanix-fd
FileSet = "Nutanix-Prod-AppConsistent"
Schedule = "WeeklyCycle"
Storage = File
Pool = NutanixPool
Messages = Standard
Write Bootstrap = "/opt/bacula/working/NutanixProdFull.bsr"
}
Job {
Name = "NutanixProdIncremental"
Type = Backup
Level = Incremental
Client = nutanix-fd
FileSet = "Nutanix-Prod-AppConsistent"
Schedule = "DailyCycle"
Storage = File
Pool = NutanixPool
Messages = Standard
}
9. Sizing & capacity planning
9.1 FD host requirements
| Resource | Minimum | Recommended | Notes |
|---|---|---|---|
| CPU | 4 cores | 8+ cores | Each concurrent disk reader uses ~1 core; PHCBT01 encoding is single-threaded per disk |
| RAM | 4 GB | 8 GB | 1 MiB chunk buffer per disk × concurrent_disks; PHCBT01 pre-sum peaks at ~1 GiB for very large incrementals |
| Scratch disk IOPS | 1,000 IOPS | 5,000+ IOPS | Used for F5/F6 restore staging and DR seed storage |
| Scratch disk capacity | 200 GB | 2× largest VM disk | For F4/F5 reconstruct staging and DR seed images |
| Network to Prism Central | 100 Mbit/s | 1 Gbit/s | iSCSI block streaming is the bottleneck — 10 GbE or bond recommended for large VMs |
| Network to DR receiver | 10 Mbit/s | 100+ Mbit/s | DR bitmap-push can be throttled with max_bandwidth_mbps= |
9.2 Storage sizing for Bacula volumes
Capacity planning depends heavily on VM disk sizes, change rates, and retention policy. As a general guideline:
- Full backup size: approximately equal to the sum of provisioned disk sizes minus sparse/zero regions
- Incremental backup size: typically 5–15% of Full size for active VMs with moderate write rates; database VMs may be higher
- Retention formula:
Total = Full_size + (Incrementals_per_cycle × Avg_Inc_size × Retention_days) - Compression: LZ4 in the Bacula FileSet options typically achieves 20–40% reduction on uncompressed VM disk data
9.3 DR receiver sizing
- Seed storage: 1× full disk capacity per protected VM
- Bitmap storage: approximately 1 MB per TB of disk per cycle (very small)
- State dir IOPS: write-heavy during seed; read-heavy during failover-test
- Memory per receiver instance: approximately 256 MB idle; 1–2 GB during active seed push
10. Performance report
The PodHeitor Nutanix AHV Backup Plugin has been validated through a structured lab testing programme on Nutanix Community Edition 6.8.1 (single-node, nested on Proxmox VE 8) and through unit and integration tests on Oracle Linux 9 with Bacula Community Edition 15.0.3. All phase gates from F0 through F8.4 have been passed at the source-complete level, with live E2E verification gated on operator access to the Director host.
| Metric | Observed / Expected | Conditions |
|---|---|---|
| Full backup throughput (iSCSI) | 400–800 MB/s | 10 GbE, O_DIRECT, 1 MiB chunks, AHV SSD-tier storage |
| Incremental backup size reduction | 85–95% vs Full | Typical production VM, 24h change rate ~5–15% |
| Prism API rate cap (client-side) | 30 req/min (configurable) | Token-bucket enforced to avoid 429 from Prism Central |
| DR seed throughput | Up to max_bandwidth_mbps |
Token-bucket throttle; 0 = line rate |
| Mid-push reconnect overhead | <5 s typical | TCP reconnect + last-acked cycle boundary resume |
| Health-check latency | <10 s | Full RBAC + DSIP probe sequence on LAN |
| Backend startup overhead | <200 ms | RAII stale-resource sweep + Prism auth on LAN |
| PHCBT01 encode overhead | <1% CPU | Region iteration + pre-sum is not the bottleneck |
| Multi-VM concurrency speedup | Near-linear to concurrent_vms |
Gated by Prism API rate and iSCSI bandwidth |
Lab-validated, production-ready. These figures represent best-observed results under controlled conditions; actual performance varies with cluster load, network topology, and VM disk profiles.
11. Compatibility matrix
11.1 Bacula components
| Component | Version | Notes |
|---|---|---|
| Bacula Community Edition File Daemon | 15.0.3+ | Required — older versions lack metaplugin OK-ack semantics |
| Bacula Director | 15.0.3+ | Any matching CE Director |
| Bacula Storage Daemon | 15.0.3+ | Any matching CE Storage Daemon |
11.2 Operating systems (FD host)
| OS | Status | Package |
|---|---|---|
| Oracle Linux 9 | Supported (primary build target) | RPM |
| RHEL 9 / Rocky Linux 9 / AlmaLinux 9 | Supported | RPM |
| Ubuntu 22.04 LTS | Supported | DEB |
| Ubuntu 24.04 LTS | Supported | DEB |
| Debian 12 | Best-effort (matches Ubuntu 24.04 toolchain) | Tarball |
| Other glibc 2.34+ x86_64 | Best-effort | Tarball |
| RHEL 8 / OL 8 | Not supported | glibc 2.28 too old for shipped Rust toolchain |
| ARM64 (aarch64) | Roadmap | — |
11.3 Nutanix platform
| Surface | Tested | Notes |
|---|---|---|
| AOS 6.x (Prism Central + Element) | Yes | Primary tested baseline |
| AOS 7.x | Yes | Tested in lab environment |
| Nutanix Community Edition 6.8.1 | Yes | Lab — nested on Proxmox VE 8 |
| Prism Central API v3 | Yes | Primary path; always available |
| Prism Central API v4 | Yes | Auto-probed; full DRP-based CRT support |
| Single-cluster PC | Yes | Default configuration |
| Multi-cluster PC | Yes | Use cluster= parameter |
11.4 Hypervisor restore targets
| Hypervisor | Backup (source) | Restore INBOUND (F6) | Notes |
|---|---|---|---|
| Nutanix AHV | Yes | Yes (F4 same-cluster, F5 alternate-cluster) | Full native support |
| Proxmox VE | Use sibling plugin | Yes (qemu-img → qcow2) | Unit-tested; E2E pending lab access |
| VMware vSphere | Use sibling plugin | Yes (qemu-img → vmdk) | Unit-tested; E2E pending lab access |
| Microsoft Hyper-V | Use sibling plugin | Yes (qemu-img → vhd/vhdx) | Unit-tested; E2E pending lab access |
12. Security
12.1 Credential handling
The plugin implements a least-privilege credential model:
- Prism credentials are never passed inline in command-line arguments visible to
ps. Thepassfile=parameter points to a file at mode0600, ownerroot:bacula, readable only by the FD process. - The
password=inline parameter is supported for compatibility but emits a WARNING on every job encouraging migration topassfile. - DR PSK tokens (
dr_auth_token) should be generated withopenssl rand -hex 32(256-bit entropy) and stored in the config file at mode0640. They are never written to Bacula job logs. - All sensitive parameters are redacted from I-packets and log lines at
verbose=1; only intentional debug traces atverbose=3may include partial values.
12.2 Prism RBAC posture
The service account used for backups requires exactly three roles on the cluster scope:
- Backup Admin — snapshot create/delete and Volume Group operations
- VM Admin — VM enumeration and VM creation for restore
- Cluster Admin (read-only) — cluster discovery and DSIP lookup
The health-check verb confirms all three roles are present before any job runs. An account missing any role will fail the health-check with exit code 4 (RBAC check step) with a descriptive error naming the missing role.
12.3 Transport security
- Prism Central HTTPS: TLS verification is on by default. The
prism_insecure=yesflag disables it for labs with self-signed Prism certs and emits a persistent WARNING. - DR replication channel: PSK-authenticated with HMAC-SHA256 per frame. Optional mTLS upgrade via
dr_auth_cert/dr_auth_key/dr_ca_certfor environments requiring mutual certificate authentication. - Bacula-internal communications: secured by existing Bacula TLS configuration (Director ↔ FD ↔ SD); the plugin does not bypass or weaken Bacula’s own channel security.
12.4 Process isolation
The backend runs as a subprocess of bacula-fd with inherited UID/GID (bacula:bacula by default). It does not acquire additional privileges. The iSCSI iscsiadm binary requires root or a SUID wrapper in some distributions — the install documentation covers this explicitly. The backend never writes outside of working_dir and state_dir.
12.5 OWASP relevance
While the plugin is not a web application, several OWASP principles apply to its design: sensitive data is never logged at default verbosity (OWASP A02 Cryptographic Failures mitigation); the Prism API client enforces TLS certificate verification by default (A02); all external inputs (plugin parameters, config file values, Prism API responses) are validated and bounded before use (A03 Injection mitigation); and the PSK-authenticated DR channel prevents unauthorised receiver impersonation (A07 Authentication Failures mitigation).
13. Monitoring
13.1 Log sources
| Source | Path / Command | Content |
|---|---|---|
| Backend log | /opt/bacula/working/podheitor-nutanix-backend.log |
All backend activity — Prism calls, iSCSI events, PHCBT01 stats, errors |
| FD log | /opt/bacula/working/<fd-host>-fd.log |
Plugin load, PTCOMM I-packets forwarded to Director |
| DR receiver log (single) | journalctl -u podheitor-nutanix-receiver |
Seed receive, bitmap-push apply, failover state transitions |
| DR receiver log (per-VM) | journalctl -u podheitor-nutanix-receiver@vm-prod-01 |
Per-VM receiver activity |
| Bacula job log | Bacula Director console / Bweb / Baculum | I-packets forwarded by cdylib: backend version, phase, selected VMs, disk counts, errors |
13.2 Key I-packet messages to monitor
The following I-packets are emitted at verbose=1 and appear in the Bacula job log. They are the primary operational monitoring surface:
| I-packet message | Meaning | Action if missing |
|---|---|---|
podheitor-nutanix backend up (v2.0.0) phase=F2 mode=Backup vm=… |
Backend started successfully | Check plugin load in FD log |
Prism discovery: PC=… cluster=… capability=v3|v4 |
Prism Central reached and API version determined | Run --standalone health-check |
Selected N VM(s) for backup |
VM glob matched N virtual machines | Verify vm= pattern and Prism connectivity |
Backup complete: VM=… disks=N bytes=B |
All disks for a VM streamed successfully | Check backend log if absent |
Applied PHCBT01 delta to disk-N (regions=R changed=B) |
Incremental CBT delta applied on restore | Ensure FileSet includes v3 |
DR cycle complete: vm=… cycle=N bytes_pushed=B |
Bitmap-push cycle completed | Check receiver log and network path |
13.3 Prometheus / metrics integration
The backend does not expose a native Prometheus endpoint in v2.0.0. The recommended monitoring pattern uses a log-scraping exporter (e.g., mtail or grok_exporter) against the backend log. Suggested metrics to derive:
| Metric name | Derived from | Alert threshold |
|---|---|---|
nutanix_backup_bytes_total{vm,type} |
Backup complete: bytes=B |
Zero bytes = empty backup (warning) |
nutanix_backup_duration_seconds{vm} |
Backend start / complete timestamps | > 4× rolling average (anomaly) |
nutanix_dr_cycle_bytes_pushed{vm} |
DR cycle complete: bytes_pushed=B |
Sustained zero = replication stalled |
nutanix_dr_reconnect_total{vm} |
mid-push reconnect log lines |
> 5/hour = unstable network path |
nutanix_prism_api_errors_total{endpoint} |
Prism API error log lines |
Any sustained errors |
nutanix_iscsi_attach_failures_total |
iSCSI attach failed log lines |
Any = investigate DSIP / initiator |
14. Troubleshooting guide
14.1 Where to look first
# Backend verbose log (most detail)
tail -100 /opt/bacula/working/podheitor-nutanix-backend.log
# Increase verbosity for active debugging
# In nutanix.conf: verbose = 2 (debug) or verbose = 3 (trace)
# Run standalone health-check
/opt/bacula/bin/podheitor-nutanix-backend
--standalone health-check
--config /opt/bacula/etc/nutanix.conf
# DR receiver status
journalctl -u podheitor-nutanix-receiver --since "1 hour ago"
14.2 Common failures and fixes
| Symptom | Root cause | Fix |
|---|---|---|
401 Unauthorized on first Prism request |
Service account not found in Prism IAM (may exist only in LDAP pass-through); or passfile not readable by the FD process |
Verify IAM user exists in Prism. Check chown root:bacula /opt/bacula/etc/nutanix.secrets && chmod 0600 nutanix.secrets |
403 Forbidden on snapshot create |
Service account missing the Backup Admin role on the cluster scope | In Prism → IAM → Roles → Assignments, add Backup Admin for the cluster scope |
| Incremental keeps running as Full | FileSet Plugin= line missing v3 key, or reference snapshot was deleted by cluster retention policy |
Ensure FileSet uses default (no version= key) or explicitly v3. Check backend log for reference_snapshot not found. |
iSCSI attach fails: No portal available |
DSIP wrong or unreachable, or FD host IQN not authorised on cluster VG allowlist | Pin DSIP: dsip=192.168.x.y. Add FD IQN to cluster volume-group allowlist via Prism or CHAP. |
qemu-img: command not found during F5/F6 restore |
qemu-img not installed or not on PATH for the bacula user |
dnf install qemu-img (RHEL) or apt install qemu-utils (Ubuntu). Verify with sudo -u bacula which qemu-img. |
| DR receiver flaps in restart loop | dr_auth_token mismatch between SOURCE and RECEIVER configs; state_dir not writable; port collision |
Confirm tokens match. Check chmod 750 /opt/bacula/working/nutanix-repl/. Run ss -tlnp | grep 9848 to detect port conflicts. |
| Multi-instance receiver: state crosses between VMs | Two template instances sharing the same state_dir |
Each per-VM config must have a distinct state_dir, e.g. /opt/bacula/working/nutanix-repl/vm-prod-01/ |
| Failover-test clone not cleaned up | failover-undo not run after the DR test |
Run mode=failover-undo job to release the isolated VLAN clone and free state-dir disk space |
TLS handshake failed on Prism connection |
Nutanix CE or internal cluster using self-signed certificate | For lab/internal only: add prism_insecure=yes to config. For production: install a valid cert on Prism Central. |
| Backend exits with code 2 on startup | Unknown --standalone verb or malformed Plugin= parameters |
Check the Plugin= line syntax against section 7. Run with verbose=3 to see the full parameter parse. |
14.3 Failover state reference
| Mode | VM on source | VM on receiver | Receiver state |
|---|---|---|---|
failover-test |
Untouched | Boots in isolated VLAN | Unchanged |
failover-planned |
Quiesced + powered-off | Powered-on | Promoted; receiver becomes source-of-truth |
failover-unplanned |
Unreachable | Powered-on | Promoted; receiver becomes source-of-truth |
failover-undo |
Untouched | Test clone deleted | Unchanged |
failover-perm |
(manual cleanup required) | Promoted | Becomes new SOURCE — re-pair to reverse |
15. Use cases & deployment scenarios
15.1 Daily backup of production Nutanix VMs
An organisation running 50 production VMs on a Nutanix AHV cluster wants daily Full backups on weekends and nightly Incrementals Monday through Friday, with 30-day retention. The PodHeitor plugin is installed on a dedicated Bacula FD host with 10 GbE connectivity to the Prism DSIP. A single FileSet with vm=prod-* and concurrent_vms=4 drives the entire backup programme. The Bacula Director runs Jobs at scheduled times; incremental change rates of 5–10% per day mean weekend Full backups are the only jobs with significant storage growth. Restore testing is performed monthly using a mode=restore Job against an isolated test cluster.
15.2 Disaster recovery with continuous replication
A financial services firm requires RPO of less than 5 minutes for its 10 Tier-1 database VMs. The DR replication stack is deployed with per-VM systemd template units on a receiver host at the DR site (300 km away, connected via 100 Mbit/s MPLS). Each VM runs an independent receiver with its own state_dir, dr_port, and PSK. The push_interval=300 setting drives bitmap-push cycles every 5 minutes. Monthly failover-test drills confirm recovery capability without disrupting production.
15.3 Migration from VMware vSphere to Nutanix AHV
An enterprise migrating from a VMware vSphere environment to Nutanix AHV uses the sibling PodHeitor VMware plugin to back up vSphere VMs, then uses the Nutanix plugin’s cross_restore=yes (F6 INBOUND) path to restore converted disk images into the target AHV cluster. The --vcpu and --memory-mib overrides allow rightsizing VMs during the migration without modifying the source. This eliminates the need for a dedicated migration tool and keeps the entire workflow within the existing Bacula infrastructure.
15.4 Multi-cluster Nutanix environment
A managed service provider operates three Nutanix clusters under a single Prism Central. They use a single nutanix.conf with prism_central= pointing to PC, and separate FileSets per cluster using the cluster= parameter to scope each job to the correct cluster. Cross-cluster restore scenarios (restoring a VM from cluster A onto cluster B) use target_cluster= in the restore Job. The health-check verb is run via an Ansible playbook as part of weekly infrastructure checks.
15.5 Air-gapped environment with tape-based Bacula storage
A government agency operates an air-gapped Nutanix environment. All backups flow through a Bacula infrastructure with LTO tape storage. The PodHeitor plugin streams VM disk data through PTCOMM directly to the Bacula tape pool — no intermediate disk staging required. The max_bandwidth_mbps= parameter is used to throttle backup traffic to avoid saturating the internal network during business hours. Restores require manual tape mount, which the operator initiates via bconsole restore; the plugin handles the full reconstruct on the FD host scratch disk before uploading to AHV.
16. Comparison with other approaches
| Capability | PodHeitor Nutanix Plugin | Bacula Enterprise (Nutanix) | Veeam Data Platform | Commvault Complete | NetBackup |
|---|---|---|---|---|---|
| Backup engine | Bacula Community 15.0.3+ | Bacula Enterprise 15.x | Veeam B&R | Commvault | Veritas NetBackup |
| CBT-based incrementals | Yes | Yes | Yes | Yes | Yes |
| iSCSI block-level streaming | Yes | Yes | Yes (HotAdd/NBD) | Yes | Yes |
| Native Prism API integration | Yes (v3 + v4) | Yes | Partial | Partial | Partial |
| Cross-cluster restore | Yes (F5) | Yes | Yes | Yes | Yes |
| Cross-vendor restore (qemu-img) | Yes (F6 — Proxmox, vSphere, Hyper-V) | Limited | No | No | No |
| Built-in DR replication | Yes (PSK-TCP, seed + delta + failover) | Yes | Yes (replication jobs) | Yes | Yes |
| Failover orchestration | Yes (4 modes) | Yes | Yes (SureBackup) | Yes | Limited |
| Multi-cluster Prism Central | Yes | Yes | Yes | Yes | Yes |
| Instant Recovery (F9) | Roadmap (separate plugin) | Yes | Yes | Yes | Yes |
| File-level granular restore (F10) | Roadmap (separate plugin) | Yes | Yes | Yes | Yes |
| Open-source backup engine | Yes (Bacula Community) | No (proprietary) | No | No | No |
| Typical annual licensing cost | Fraction of alternatives* | ~US$ 10,000+/year | ~US$ 5,000+/year | ~US$ 10,000+/year | ~US$ 10,000+/year |
| Plugin licence model | Proprietary | Proprietary | Proprietary | Proprietary | Proprietary |
* Contact PodHeitor International for current pricing. The 50% savings claim is benchmarked against published list prices for comparable feature sets.
Note on Bacula Enterprise: Bacula Enterprise is a valid, mature alternative with a broader feature set (including Instant Recovery and granular restore in a single product). The PodHeitor plugin is positioned as a cost-effective alternative for organisations that have already invested in Bacula Community infrastructure and want a production-grade Nutanix AHV integration without the full Bacula Enterprise licensing commitment. The two solutions serve different budget and complexity profiles and are not direct competitors.
17. Roadmap
17.1 v2.x — Stability and ecosystem (H2 2026)
- F7 — Cross-restore OUTBOUND: Enable Nutanix AHV VMs to be restored to other hypervisors (Proxmox VE, VMware vSphere, Hyper-V) through the export path, complementing the existing F6 INBOUND path.
- F3.5 / F3.6 — v4 CRT stabilisation: Per-disk DRP capture is source-complete; live E2E validation on a v4-only cluster to confirm parser coverage against all GA AOS 7.x response shapes.
- F4.5 — Image Service upload and VM creation: Complete the F4 restore path by wiring disk reconstruction to Prism Image Service upload and full VM materialise from the reconstructed image.
- ARM64 build: Cross-compile target for aarch64 Linux to support organisations running Bacula FD on Arm-based infrastructure.
- Prometheus exporter endpoint: Expose a
/metricsHTTP endpoint on a configurable port for native Prometheus scraping without log-parsing glue.
17.2 v3.x — Advanced recovery (H1 2027)
- F9 — Instant Recovery (iSCSI-target boot): This feature is deliberately owned by the parallel PodHeitor Bacula-SD granular-restore + IR plugin, which serves all VM-style backends from a single implementation. The Nutanix AHV plugin will integrate with it when the SD plugin reaches GA.
- F10 — File-level granular restore: Same scope as F9 — provided by the Bacula-SD granular-restore plugin, which mounts reconstructed VM disks and exposes individual files via the Bacula restore browser.
- Multi-tenant mode: Dedicated RBAC scopes per tenant within a shared Prism Central, enabling managed service provider deployments with strict data isolation.
- Cloud-native target support: Direct DR replication to S3-compatible object storage as an alternative to a dedicated receiver host.
17.3 Ongoing
- Tracking Nutanix AOS and Prism Central API releases; parser updates for any response schema changes in v4 endpoints
- Continuous regression testing against Nutanix Community Edition lab environment
- Performance optimisations for very large VMs (> 4 TiB single disk) and high-disk-count VMs (> 16 disks)
18. Conclusion
The PodHeitor Nutanix AHV Backup Plugin for Bacula represents a significant step forward for the open-source backup ecosystem. It brings production-grade Nutanix AHV protection — including CBT-based incrementals, multi-cluster support, cross-vendor restore, and a complete DR replication and failover stack — into the reach of any organisation running Bacula Community Edition, without requiring a full commercial backup platform migration or Bacula Enterprise licensing.
The plugin’s architecture — pure Rust on both sides of the metaplugin boundary, RAII-safe resource cleanup, PTCOMM subprocess isolation, and a phase-gated engineering discipline with 50+ automated tests — makes it a reliable foundation for mission-critical backup workflows. The standalone health-check verb, per-VM systemd DR templates, and structured I-packet logging give operations teams the visibility and control they need to maintain a defensible backup posture.
For organisations facing commercial backup platform renewals — particularly those evaluating Veeam, Commvault, or NetBackup for Nutanix AHV — the PodHeitor plugin offers a head-to-head alternative at 50% or more savings, with a feature set tailored specifically to the Nutanix platform rather than bolted onto a generic hypervisor abstraction layer.
We invite you to evaluate the plugin in your environment. Start with the standalone health-check, run a canary backup job, and compare restore accuracy and operational simplicity against your current solution. For a written commercial quote, feature comparison against your specific environment, or professional services engagement (installation, configuration, production go-live), contact Heitor Faria directly at the details below.
Special offer. Bring your renewal proposal for any commercial enterprise backup platform — Veeam, Commvault, NetBackup, or others. We will benchmark a head-to-head proposal targeting at least 50% savings with stronger Nutanix AHV-specific functionality. Contact heitor@opentechs.lat for a written quote.
19. Contact information
| Channel | Details |
|---|---|
| Name | Heitor Faria — PodHeitor International |
| Website | https://podheitor.com |
| heitor@opentechs.lat | |
| Phone / WhatsApp (International) | +1 786 726-1749 |
| Phone / WhatsApp (Brazil) | +55 61 98268-4220 |
| Plugin documentation | https://podheitor.com/plugins/nutanix-ahv |
| Support issues | Email with subject: PodHeitor Nutanix Plugin Support |
For enterprise engagements, Heitor Faria provides professional services including installation, integration with existing Bacula Directors, DR runbook design, and production go-live support. Retainer and per-incident support contracts are available.
20. Legal / copyright
© 2026 Heitor Faria — PodHeitor International. All Rights Reserved.
The PodHeitor Nutanix AHV Backup Plugin for Bacula is dual-licensed under LicenseRef-PodHeitor-Proprietary. The proprietary licence governs all commercial use and redistribution. Contact heitor@opentechs.lat for commercial licensing terms.
Trademarks: PodHeitor and the PodHeitor logo are trademarks of Heitor Faria / PodHeitor International. Nutanix, AHV, Prism, Prism Central, Prism Element, AOS, and Nutanix Community Edition are registered trademarks or trademarks of Nutanix, Inc. Bacula and Bacula Enterprise are trademarks of Bacula Systems SA. Veeam is a trademark of Veeam Software AG. Commvault is a trademark of Commvault Systems, Inc. NetBackup and Veritas are trademarks of Veritas Technologies LLC. VMware and vSphere are trademarks of VMware, Inc. (a Broadcom company). Proxmox VE is a registered trademark of Proxmox Server Solutions GmbH. Microsoft and Hyper-V are trademarks of Microsoft Corporation. All other trademarks are property of their respective owners.
Disclaimer: This document is provided for informational purposes. Performance figures and cost comparisons are based on lab testing and publicly available list prices at the time of publication and may vary by environment, configuration, and vendor pricing changes. PodHeitor International makes no warranty, express or implied, regarding the accuracy of third-party product information contained herein.
Disponível em:
Português (Portuguese (Brazil))
English
Español (Spanish)