Technical whitepaper — PodHeitor MSSQL for Bacula

Technical whitepaper — PodHeitor MSSQL for Bacula

Author: Heitor Faria Contact: heitor@opentechs.lat | +1 786 726-1749 | +55 61 98268-4220 (WhatsApp) Version: 1.0.0 — General Availability | Date: 2026-04-28 License: Copyright © 2026 Heitor Faria. All rights reserved.


COMMERCIAL NOTICE

Trazendo sua proposta de contratação ou renovação do Bacula Enterprise, Veeam, Commvault ou Netbackup? Oferecemos no mínimo 50% de desconto, com muito mais funcionalidades.

heitor@opentechs.lat | +1 786 726-1749 | +55 61 98268-4220 (WhatsApp)


Table of Contents

  1. Executive Summary
  2. Business Case
  3. Architecture Overview
  4. Backup Engines Deep Dive
  5. Replication Capabilities
  6. Security & Compliance
  7. Observability
  8. Installation Guide
  9. Configuration Reference
  10. Restore Procedures & Use Cases
  11. Performance Reports
  12. Compatibility Matrix
  13. Sizing Guide
  14. Bacularis Integration
  15. Migration from Bacula Enterprise
  16. Troubleshooting
  17. Roadmap
  18. Validation Evidence

1. Executive Summary

The PodHeitor Microsoft SQL Backup and Replication Plugin for Bacula is a production-hardened, commercially supported SQL Server data protection solution built on top of Bacula Community. It fills a critical market gap: enterprises that run Microsoft SQL Server and want Bacula Community’s proven storage architecture without the cost and limitations of Bacula Enterprise or proprietary alternatives.

The Problem

SQL Server database administrators face a difficult trade-off:

  • Bacula Enterprise mssql-fd.dll works on Windows but costs significantly more, is limited to Windows, has no parallelism, no compression, no replication, and no observability.
  • Veeam, Commvault, and NetBackup SQL agents are expensive, often require separate infrastructure, and lock customers into proprietary storage formats.
  • Native SQL Server backup (BACKUP DATABASE TO DISK) has no centralized management, no retention policies, and no integration with enterprise storage.

The Solution

PodHeitor MSSQL delivers:

Capability Business Value
Feature parity + extensions vs. Enterprise Same investment, dramatically more features
Linux SQL Server support Protects the rapidly growing SQL Server on Linux footprint
4× throughput via striped I/O Faster backup windows, lower RPO
3 native replication modes DR without AG licensing overhead
Instant recovery RTO measured in minutes, not hours
Drop-in Enterprise migration Zero-downtime switch, no catalog changes
Prometheus + OTel observability Grafana dashboards, alerting, SLA monitoring
TDE-aware Cross-server encrypted database restore

Validation (v1.0.0 GA)

E2E Test Suite (T01–T13, 16 scenarios):   16 / 16 PASS  (3 consecutive runs)
OL9 unit/integration tests:               311 passed, 0 failed
Win2025 unit/integration tests:           381 passed, 0 failed
SQL Server tested:                        2022 on Windows Server 2025
Always On AG:                             2-node, CLUSTER_TYPE=NONE, SYNCHRONOUS_COMMIT
Bacula version:                           Community 15.0.3
Installed packages tested:                RPM v1.0.0 (OL9) + zip v1.0.0 (Win2025)

2. Business Case

2.1 Total Cost of Ownership Comparison

Solution Relative Cost SQL Linux Replication Observability Max Throughput
Bacula Enterprise MSSQL plugin $$$ No No No
Veeam for SQL Server $$$$ Limited Limited Basic 1–2×
Commvault for SQL Server $$$$$ Yes Yes Yes 1–2×
PodHeitor MSSQL $ Yes Yes (3 modes) Yes (full)

2.2 Key Differentiators

Linux SQL Server — SQL Server 2017+ on Linux is now mainstream. PodHeitor uses a native TDS/FIFO engine on Linux — no Windows agent proxy, no SMB shares, no complexity.

Striped Parallel I/O — A 1 TB database backed up in 180 minutes with a single stream finishes in 42 minutes with 8 stripes. For VLDB environments, this is the difference between meeting and missing backup windows.

Native Replication via Bacula — Bacula volumes are already durable, encrypted, offsite-replicated, and retention-managed. The PodHeitor replication engine uses them as the WAL transport between primary and secondary SQL instances — no separate replication infrastructure.

Instant Recovery — Instead of waiting hours for a 2 TB database to fully restore, mode=instant makes the database queryable within minutes by mounting the backup stream with WITH MOVE. The full restore continues in the background.

Drop-In Migration — Byte-identical namespace (@mssql/<INSTANCE>/<db>/data.bak) means existing Enterprise backups are restorable by PodHeitor and vice versa. Change one line in bacula-fd.conf, run one Full, done.

2.3 When to Choose PodHeitor

  • You run Bacula Community and need a robust SQL Server backup solution
  • You’re paying for Bacula Enterprise and want to reduce costs without losing features
  • You run SQL Server on Linux and need a native backup agent
  • You have VLDB (>500 GB) and need faster backup windows
  • You need DR without AG Enterprise Edition licensing complexity
  • You run Always On AG and want backup preference + automatic secondary seeding

3. Architecture Overview

3.1 Two-Tier Design

┌─────────────────────────────────────────────────────────────────────┐
│                      Bacula File Daemon                             │
│                                                                     │
│  ┌───────────────────────────────────────────────────────────────┐  │
│  │  podheitor-mssql-fd.{dll,so}     (pure Rust cdylib, v2.0.0+) │  │
│  │  - Built from ../PodHeitor Rust cdylib/crates/plugin-mssql/  │  │
│  │  - No Bacula AGPLv3 source statically linked                 │  │
│  │  - Implements Bacula FD plugin ABI v4 via metaplugin-rs      │  │
│  │  - Spawns Rust backend as a subprocess                       │  │
│  │  - Proxies all I/O through PTCOMM binary protocol            │  │
│  └─────────────────────────┬─────────────────────────────────────┘  │
└────────────────────────────│────────────────────────────────────────┘
                             │ stdin/stdout (PTCOMM binary frames)
                             │ stream IDs for parallel stripes
                             ▼
┌─────────────────────────────────────────────────────────────────────┐
│               podheitor-mssql-backend     (Rust binary)             │
│                                                                     │
│  Engine Layer:      vdi | tds_fifo | tds_url | snapshot            │
│  Replication:       logshipping | ag_bootstrap | fanout            │
│  Orchestration:     catalog | parallel striper | chain/integrity   │
│  BI Servers:        SSAS (XMLA) | SSRS | SSIS (SSISDB)            │
│  Security:          TDE capture/restore | Always Encrypted | ARD   │
│  Observability:     Prometheus /metrics | OpenTelemetry OTLP       │
│  Cross-cutting:     PTCOMM codec | Config | Logging | Compression  │
└────────────────────────────┬────────────────────────────────────────┘
                             │ TDS / ODBC / COM VDI
                             ▼
┌─────────────────────────────────────────────────────────────────────┐
│         Microsoft SQL Server   (Windows / Linux / Azure MI)         │
│  msdb — LSN chain, backupset | sys.availability_* — AG topology    │
└─────────────────────────────────────────────────────────────────────┘

3.2 PTCOMM Protocol

PTCOMM is a lightweight binary framing protocol — the communication layer between the in-FD Rust cdylib loader and the standalone Rust backend subprocess:

Frame Byte Purpose
Data D Backup data chunk (with stream ID for N stripes)
Command C Operation request
File F File start/end boundary
Info I Progress message to Bacula job log
Error E Fatal — job abort
Warning W Non-fatal warning
Terminate T Normal session end

Stream IDs allow N parallel stripes to be multiplexed over a single stdin/stdout pair. Bacula FD sees N separate files.

3.3 Engine Auto-Selection

mode=auto  (default)
    ├── Windows?   → mode=vdi    (IClientVirtualDevice COM API)
    ├── Linux?     → mode=tds_fifo  (TDS + POSIX FIFO)
    └── Azure MI?  → mode=tds_url   (BACKUP TO URL embedded S3)

Explicit override: mode=snapshot | mode=replicate_logshipping | ...

3.4 State Model

Working directory state (/opt/bacula/working/mssql-state/):

  • LSN chain cache — last applied LSN per database (differential/log health)
  • Follower state — replication last-applied-LSN per database
  • Backup manifest — database layout, compression, stripe count for restore
  • Recovery tokens — job resume after interruption

Total footprint: < 10 MB per SQL instance. Zero staging — no .bak files ever written to disk.


4. Backup Engines Deep Dive

4.1 VDI Engine (Windows)

Technology: Microsoft Virtual Device Interface — IClientVirtualDevice COM API (sqlvdi.dll), implemented via hand-written windows-sys COM vtables in Rust.

Threading model (per database, per stripe):

Thread A: ODBC → BACKUP DATABASE [db] TO VIRTUAL_DEVICE='...'   (blocks on SQL I/O)
Thread B: VDI pump → GetCommand() → Write/Flush → CompleteCommand  (pulls buffers)
Thread C: PTCOMM encoder → bounded channel(8) back-pressure → stdout → Bacula

Memory ceiling: stripes × parallel_dbs × 8 × maxtransfersize

  • Default (1 stripe, 1 DB, 64 KB): 512 KB
  • VLDB (8 stripes, 4 DBs, 4 MB): 1 GB — tune maxtransfersize accordingly

Backup sequence:

  1. ODBC connect + database catalog (include/exclude/recovery model filters)
  2. CreateEx(N devices) for stripe count
  3. Issue BACKUP DATABASE WITH BUFFERCOUNT, BLOCKSIZE, MAXTRANSFERSIZE, COMPRESSION, CHECKSUM, [DIFFERENTIAL|COPY_ONLY]
  4. VDI pump → PTCOMM stream → Bacula FD
  5. Query msdb.backupset for LSN (FirstLSN, LastLSN) after completion
  6. Emit __ph_manifest.json sidecar (LSN chain, compression stats)

4.2 TDS/FIFO Engine (Linux)

Technology: tiberius (pure-Rust async TDS 7.x client) + POSIX named pipes.

Flow:

tokio runtime
  ├── TDS session: BACKUP DATABASE [db] TO DISK='/fifo/job-0.fifo', '/fifo/job-1.fifo'...
  ├── Async FIFO reader [stripe 0] → decode → PTCOMM stream 0 → Bacula
  ├── Async FIFO reader [stripe 1] → decode → PTCOMM stream 1 → Bacula
  └── ...

SQL Server writes to FIFOs concurrently on its I/O threads
Plugin readers drain asynchronously → near-zero kernel overhead

FIFO lifecycle: Created mode 0600 in per-job directory. Cleaned up on exit and SIGTERM. tmpfs-backed when available.

4.3 TDS URL Engine (Azure MI / SQL Server 2022)

Embedded Hyper 1.x HTTPS server → CREATE CREDENTIAL + BACKUP TO URL=&#x27;https://127.0.0.1:&lt;port&gt;/...&#x27; → SQL PUT-streams → demux → PTCOMM. Credential dropped after job. Works for Azure SQL MI (no filesystem) and SQL 2022 S3 integration.

4.4 Snapshot Engines

VSS (Windows): IVssBackupComponents → SQL writer quiesce → DoSnapshotSet → mount read-only → stream MDF/NDF/LDF → DeleteSnapshot.

LVM (Linux): SQL quiesce → lvcreate --snapshot → mount read-only → stream files → lvremove.

ZFS: SQL quiesce → zfs snapshot → access .zfs/snapshot/ → stream files → zfs destroy.

NetApp ONTAP: Deferred to v1.1 — currently returns actionable error with v1.1 ETA.


5. Replication Capabilities

PodHeitor implements three SQL Server replication modes using Bacula volumes as the WAL transport. No separate replication infrastructure required.

5.1 Log Shipping

PRIMARY SQL (primary-fd plugin)
  → BACKUP LOG → Bacula volume (every 5 min Incremental job)
  
SECONDARY SQL (follower-fd plugin)
  → bconsole query: find latest log job for target DB
  → Download log backup
  → RESTORE LOG [db] WITH STANDBY='/standby_undo.bak'
  → DB online read-only (STANDBY mode)

RPO: 30 sec – 15 min. RTO: Minutes (secondary always in STANDBY/NORECOVERY).

5.2 AG Bootstrap

Provisions new AG replica from Bacula chain without touching the primary: Full → Diff → Logs (WITH NORECOVERY)ALTER DATABASE SET HADR AVAILABILITY GROUP.

5.3 Fanout (1→N Regions)

Single primary backup chain consumed by N secondary FD instances (São Paulo → US East → EU West). Zero extra SQL load on primary. Convergence within 2× log interval.

5.4 Follower State Machine

Persistent state ensures idempotency (re-applying an applied log is a no-op), gap detection (triggers alert + fallback to Full reseed), and crash recovery (resumes from last applied LSN).


6. Security & Compliance

6.1 Credential Handling

# Recommended: passfile (file permissions enforced)
passfile = /opt/bacula/etc/.mssql_pass  # mode 0640, warn if world-readable

# Inline (auto-redacted everywhere in logs)
password = &lt;secret&gt;  # → appears as "password=&lt;REDACTED&gt;" in all output

6.2 TDE Certificate Lifecycle

tde_capture=yes:

  1. Export DEK certificate + encrypted PVK via T-SQL → encrypt with job-derived key → transport via PTCOMM RestoreObject (never touches filesystem)

tde_restore_cert=AUTO on restore:

  1. Fetch from RestoreObject → CREATE MASTER KEY (if not exists) → CREATE CERTIFICATE FROM BINARYRESTORE DATABASE WITH MOVE

Fully automated cross-server TDE restore. No manual cert export/import.

6.3 Transport Security

Connection Encryption
TDS (non-localhost) TLS 1.2+ required by default
Azure MI TLS always enforced
PTCOMM (backend↔adapter) OS process pipe (same host, OS-isolated)
Metrics endpoint HTTP on loopback only

6.4 Minimum SQL Permissions

CREATE LOGIN [bacula_backup] WITH PASSWORD = '&lt;strong_password&gt;';
GRANT BACKUP DATABASE, BACKUP LOG, VIEW SERVER STATE TO [bacula_backup];

-- For TDE capture:
GRANT CONTROL SERVER TO [bacula_backup];
-- For system DBs: BACKUP DATABASE on master, msdb, model

6.5 Ransomware Hook

ransomware_hook=yes → pre-backup query to PodHeitor ARD daemon → anomaly reported → job aborts with error frame. Prevents backing up already-ransomed data.

6.6 Release Integrity

sha256sum -c releases/SHA256SUMS --ignore-missing
rpm --checksig podheitor-mssql-plugin-1.0.0-1.el9.x86_64.rpm

7. Observability

7.1 Prometheus Metrics

Enable: metrics_endpoint=0.0.0.0:9200

podheitor_mssql_jobs_started_total{instance,db}
podheitor_mssql_jobs_succeeded_total{instance,db}
podheitor_mssql_jobs_failed_total{instance,db}
podheitor_mssql_bytes_backed_up_total{instance,db}
podheitor_mssql_last_job_duration_ms{instance,db}
podheitor_mssql_last_backup_size_bytes{instance,db,level}
podheitor_mssql_replication_lag_seconds{db,role}
podheitor_mssql_dbs_backed_up_total

Health check: GET /healthz200 OK {&quot;status&quot;:&quot;ok&quot;,&quot;version&quot;:&quot;1.0.0&quot;}

7.2 OpenTelemetry Traces

Enable: otel_endpoint=http://otel-collector:4318

Each job produces nested spans: backup_jobcatalog_querybackup_db per database → vdi_stripe per stripe → verifyonly. Compatible with Jaeger, Zipkin, Tempo, AWS X-Ray, Azure Monitor.


8. Installation Guide

8.1 SQL Server Prerequisites

-- Create backup login
CREATE LOGIN [bacula_backup] WITH PASSWORD = '&lt;strong_password&gt;';
GRANT BACKUP DATABASE, BACKUP LOG, VIEW SERVER STATE TO [bacula_backup];

-- passfile (Linux)
echo '&lt;password&gt;' &gt; /opt/bacula/etc/.mssql_pass
chown root:bacula /opt/bacula/etc/.mssql_pass &amp;&amp; chmod 640 /opt/bacula/etc/.mssql_pass

Microsoft ODBC Driver 18 (Linux):

curl -fsSL https://packages.microsoft.com/config/rhel/9/prod.repo | tee /etc/yum.repos.d/mssql-release.repo
ACCEPT_EULA=Y dnf install -y msodbcsql18 unixODBC

8.2 Linux RPM Installation

# Verify SHA256
sha256sum -c SHA256SUMS --ignore-missing

# Install
rpm -Uvh podheitor-mssql-plugin-1.0.0-1.el9.x86_64.rpm
#   → /opt/bacula/plugins/podheitor-mssql-fd.so
#   → /opt/bacula/bin/podheitor-mssql-backend
#   → /opt/bacula/etc/podheitor-mssql.conf (from .sample if absent)

# Edit config and reload FD (done automatically by %post)
vi /opt/bacula/etc/podheitor-mssql.conf
systemctl reload bacula-fd

# Verify
/opt/bacula/bin/podheitor-mssql-backend --version
# → PodHeitor MSSQL Backend v1.0.0

8.3 Windows zip Installation

Expand-Archive podheitor-mssql-plugin-1.0.0-win64.zip -DestinationPath C:ph-install
Copy-Item C:ph-installpodheitor-mssql-fd.dll "C:Program FilesBaculaplugins"
Copy-Item C:ph-installpodheitor-mssql-backend.exe "C:Program FilesBaculabin"
Copy-Item C:ph-installpodheitor-mssql.conf.sample "C:ProgramDataBaculaetcpodheitor-mssql.conf"
net stop bacula-fd &amp;&amp; net start bacula-fd
&amp; "C:Program FilesBaculabinpodheitor-mssql-backend.exe" --version

8.4 Bacula Director Jobs

Job {
  Name = "MSSQL-Full"
  Type = Backup; Level = Full
  Client = mssql-server-fd
  FileSet = "MSSQL-Production"
  Schedule = "WeeklyCycle"
  Storage = File1; Pool = Full
}
Job {
  Name = "MSSQL-Diff"
  Type = Backup; Level = Differential
  Client = mssql-server-fd
  FileSet = "MSSQL-Production"
  Schedule = "DailyCycle"
  Storage = File1; Pool = Differential
}
Job {
  Name = "MSSQL-Log"
  Type = Backup; Level = Incremental
  Client = mssql-server-fd
  FileSet = "MSSQL-Production"
  Schedule = "HourlyCycle"
  Storage = File1; Pool = Incremental
}

9. Configuration Reference

9.1 Config File

# /opt/bacula/etc/podheitor-mssql.conf
user = bacula_backup
passfile = /opt/bacula/etc/.mssql_pass
authtype = server
checksum = yes
compress = native+zstd
compress_level = 3
metrics_endpoint = 0.0.0.0:9200

9.2 FileSet Examples

All databases, VDI, integrity check:

FileSet {
  Name = "MSSQL-Production"
  Enable VSS = no
  Include {
    Options { Signature = SHA256 }
    Plugin = "podheitor-mssql: checksum=yes verify_backup=yes compress=native+zstd include_server_objects=yes tde_capture=yes"
  }
}

Linux, 4 stripes, specific DBs:

FileSet {
  Name = "MSSQL-Linux"
  Include {
    Options { Signature = SHA256 }
    Plugin = "podheitor-mssql: mode=tds_fifo include=prod_orders include=prod_inventory stripes=4 compress=native+zstd passfile=/opt/bacula/etc/.mssql_pass"
  }
}

VLDB 8-stripe:

FileSet {
  Name = "MSSQL-VLDB"
  Include {
    Options { Signature = SHA256 }
    Plugin = "podheitor-mssql: database=data_warehouse stripes=8 parallel_dbs=1 compress=native+zstd buffercount=32 maxtransfersize=4194304 verify_backup=yes"
  }
}

Always On AG, backup from readable secondary:

FileSet {
  Name = "MSSQL-AG"
  Enable VSS = no
  Include {
    Options { Signature = SHA256 }
    Plugin = "podheitor-mssql: ag_preference=readable_secondary ag_failover_retry=3 copyonly tde_capture=yes"
  }
}

9.3 Complete Backup Options

# Option Default Description
1 database * DB glob (all except tempdb)
2 include Explicit include (multi)
3 exclude tempdb Exclusion glob (multi)
4 user SQL login
5 domain AD domain
6 password Inline password (auto-redacted)
7 passfile Password file path
8 authtype windows windows / server / azure_ad / managed_identity
9 instance MSSQLSERVER Instance name (multi)
10 all_instances off Auto-discover all instances
11 hostname (local) ODBC server string
12 abort_on_error off Fail job on any DB error
13 copyonly off COPY_ONLY: yes=all, incremental=logs only
14 fullbackup off Force Full regardless of Bacula level
15 skipreadonly off Skip read-only DBs on Incremental
16 dblayout off Store DB layout as RestoreObject
17 lock_timeout 0 ms SET LOCK_TIMEOUT
18 buffercount 10 VDI I/O buffer count
19 blocksize 65536 Physical block size (bytes)
20 maxtransfersize 65536 Max VDI transfer (bytes)
21 connection_string Override ODBC connection string
22 checksum yes WITH CHECKSUM
23 driver ODBC 18 ODBC driver name
24 target_backup_recovery_models all Filter: SIMPLE, FULL, BULK_LOGGED
25 simple_recovery_models_incremental_action make_full make_full / ignore_with_error / ignore
26 mode auto Engine: auto,vdi,tds_fifo,tds_url,snapshot,replicate_*
27 stripes 1 Parallel stripes (1–64)
28 parallel_dbs CPU cores Max concurrent DB backups
29 compress native+zstd none,native,zstd,lz4,native+zstd
30 compress_level 3 1–22 (zstd), 1–9 (gzip)
31 max_rate_kbps 0 Bandwidth throttle (0=unlimited)
32 resource_governor_classifier SQL Resource Governor classifier
33 page_verify none none,page_audit,torn_page
34 verify_backup no RESTORE VERIFYONLY after backup
35 dbcc_checkdb no no,physical_only,full
36 ag_preference auto auto,primary,secondary,readable_secondary
37 ag_failover_retry 3 Retries on AG failover mid-job
38 tde_capture no Capture TDE certs as RestoreObject
39 always_encrypted_cmk_report no CMK metadata snapshot
40 include_system_dbs master,msdb,model System DB granular control
41 include_server_objects no Export logins, linked servers, jobs
42 include_filestream yes FILESTREAM/FileTable consistency
43 include_hekaton_xtp yes In-Memory OLTP checkpoint files
44 include_service_broker no Service Broker queue export
45 include_ssas no SSAS servers for XMLA backup
46 include_ssrs no SSRS host:port
47 include_ssis no SSISDB + master key
48 url_endpoint S3/Azure URL for mode=tds_url
49 url_credential SAS token or MI identity
50 snapshot_provider platform vss,lvm,zfs
51 dry_run no Preflight validate, no write
52 ransomware_hook no Abort on ARD anomaly
53 metrics_endpoint Prometheus /metrics host:port
54 otel_endpoint OTLP traces URI
55 use_sudo no Elevate for Linux FIFO mgmt
56 cluster_id Multi-cluster namespace prefix
57 config_file platform default External config file path
58 virtual_full no Synthetic full from chain

9.4 Complete Restore Options

# Option Default Description
1 instance MSSQLSERVER Target SQL instance
2 database source name New database name
3 username SQL login
4 password Password
5 domain AD domain
6 hostname (local) Target server
7 authtype windows Auth mode
8 recovery yes WITH RECOVERY (no=NORECOVERY)
9 stop_before_mark STOPBEFOREMARK &#x27;mark&#x27;
10 stop_at_mark STOPATMARK &#x27;mark&#x27;
11 stop_at STOPAT &#x27;datetime&#x27;
12 restricted_user no WITH RESTRICT_USER
13 verify off Verify before restore
14 connection_string Override ODBC
15 checksum no WITH CHECKSUM on restore
16 driver ODBC 18 ODBC driver
17 mode auto auto,instant,in_place,new_db,to_disk,single_item,ag_reseed,master_restore
18 stripes backup value Must match backup stripes
19 dbcc_checkdb physical_only DBCC after restore
20 row_count_compare no Compare row counts vs manifest
21 single_item_target schema.table[,...]
22 sandbox_docker_image mssql/server:2022 Docker sandbox image
23 ag_target_group AG name for ag_reseed
24 file_relocation_map JSON {logical: physical} map
25 tde_restore_cert TDE cert bundle (AUTO=from RestoreObject)
26 drop_existing no no,yes,only_if_match
27 tail_log_before no Auto tail-log before destructive restore
28 point_in_time_tz UTC Timezone for stop_at
29 parallel_restore_dbs CPU cores Concurrent DB restores
30 progress_report_interval 30 s Job-log progress cadence
31 regexwhere Bacula file relocation regex

10. Restore Procedures & Use Cases

10.1 Standard In-Place Restore

# bconsole restore
Plugin Options: instance=MSSQLSERVER database=AdventureWorks recovery=yes

10.2 Point-in-Time Restore

Plugin Options: stop_at=2026-04-28T10:30:00 database=AdventureWorks_pit recovery=yes
Plugin Options: stop_at_mark=SavePoint1 database=AdventureWorks_pit recovery=yes

10.3 Rename + File Relocation

// file_relocation.json
{
  "AdventureWorks_Data": "D:NewDataAdventureWorks.mdf",
  "AdventureWorks_Log":  "L:NewLogsAdventureWorks_log.ldf"
}
Plugin Options: database=AdventureWorks_new file_relocation_map=C:relocation.json

10.4 Instant Recovery

Plugin Options: mode=instant database=AdventureWorks_instant
# → DB queryable in ~35 sec (100 GB example)
# → Full restore continues in background

10.5 Single-Item (Table) Restore

Plugin Options: mode=single_item single_item_target=dbo.Orders,dbo.OrderItems database=recovery_sandbox
# → Ephemeral Docker SQL container
# → Full restore to container
# → Table extraction via bcp → INSERT scripts / CSV
# → Container removed after extraction

10.6 AG Reseed (Secondary Lost)

Plugin Options: mode=ag_reseed ag_target_group=AG_Production database=prod_orders tail_log_before=yes recovery=no
# → Tail-log backup → Remove from AG → Restore Full+Diff+Logs → Rejoin AG

10.7 TDE Cross-Server Restore

Plugin Options: tde_restore_cert=AUTO database=SecureDB
# → Cert fetched from RestoreObject → installed → RESTORE DATABASE WITH MOVE

11. Performance Reports

11.1 Throughput by DB Size and Stripe Count

Environment: SQL Server 2022 / Win Server 2025 / 16 cores / NVMe / 10 Gbps to Bacula

DB Size stripes=1 stripes=2 stripes=4 stripes=8
10 GB 52 s 30 s 18 s 15 s
100 GB 8.0 min 4.7 min 2.8 min 2.2 min
1 TB 180 min 100 min 60 min 42 min

11.2 Compression Ratios

DB Type none native native+zstd
OLTP (AdventureWorks) 3.1× 3.8×
Data Warehouse 4.2× 5.0×
JSON-heavy 1.8× 2.7×

11.3 Restore Performance

DB Size Standard Instant (queryable in)
10 GB 45 s 8 s
100 GB 11 min 35 s
1 TB 75 min 3 min

11.4 E2E Validation (v1.0.0 GA — Run 9)

PASS: 16 / 16   FAIL: 0 / 16
Duration: 22:01 to 22:13 (12 minutes, installed packages)
Environment: Bacula 15.0.3 + SQL Server 2022 + 2-node AG

12. Compatibility Matrix

SQL Server Versions

Version Windows VDI Linux TDS/FIFO Azure MI Always On
2012, 2014 ✅ Basic
2016, 2017
2019, 2022
Azure SQL MI ✅ Auto

Bacula Community Versions

Version Status
14.0.x Compatible
15.0.x ✅ Tested and certified

13. Sizing Guide

File Daemon Host

Workload RAM CPU Network
Small (< 100 GB) 1 GB 2 cores 1 Gbps
Medium (100 GB–1 TB) 4 GB 4 cores 1 Gbps
Large (1–10 TB) 8 GB 8 cores 10 Gbps
VLDB (> 10 TB) 16 GB 16 cores 10 Gbps+

Memory formula: parallel_dbs × stripes × buffercount × maxtransfersize

Default: 1 × 1 × 10 × 64 KB = 640 KB VLDB: 4 × 8 × 32 × 4 MB = 4 GB

SQL Server Impact

Setting CPU I/O Recommendation
compress=native+zstd +8–20% −72% Default — strongly recommended
stripes=4 SQL parallel +4× IOPS VLDB standard
dbcc_checkdb=physical_only +10–30% +30% Weekly schedule
resource_governor_classifier Policy-bounded Policy-bounded OLTP hosts

14. Bacularis Integration

RestoreObject Schemas

mssql-dblayout.json (emitted with dblayout=yes):

{
  "schema_version": "1.0",
  "instance": "MSSQLSERVER",
  "databases": [{
    "name": "AdventureWorks",
    "level": "Full",
    "compressed_bytes": 82000000,
    "original_bytes": 256000000,
    "first_lsn": "34000000001234500001",
    "last_lsn": "34000000012345600001",
    "recovery_model": "FULL",
    "stripes": 4,
    "compression": "native+zstd",
    "files": [
      {"logical": "AdventureWorks_Data", "physical": "C:DataAW.mdf", "size_bytes": 230000000},
      {"logical": "AdventureWorks_Log",  "physical": "C:LogAW_log.ldf", "size_bytes": 26000000}
    ]
  }]
}

mssql-tde-{db}.json (emitted with tde_capture=yes):

{
  "schema_version": "1.0",
  "database": "SecureDB",
  "certificate_name": "SecureDB_Cert",
  "certificate_der_b64": "&lt;base64&gt;",
  "encrypted_pvk_b64": "&lt;base64&gt;"
}

bconsole Access

list restoreobjects jobid=5123
llist restoreobject jobid=5123 objectname=mssql-dblayout.json

15. Migration from Bacula Enterprise

15.1 Compatibility Promise

Byte-identical namespace. Enterprise backups are 100% restorable by PodHeitor and vice versa.

15.2 Zero-Downtime Procedure

Day 1: Install PodHeitor alongside Enterprise (both present on FD)
Day 1: dry_run=yes → validate connectivity + permissions
Day 2: First Full backup with PodHeitor → verify in Bacula log
Day 3: Switch FileSet: "mssql:" → "podheitor-mssql:"
Day 30+: Remove Enterprise plugin after retention cycle (optional)

15.3 Notable Differences

Aspect Enterprise PodHeitor
Default checksum No Yes (safer)
Default compression None native+zstd (smaller backups)
Differential filename data.bak data.diff.bak (distinguishable, both recognized)
__ph_* sidecars Not produced Produced (Enterprise ignores on restore)

16. Troubleshooting

16.1 Diagnostic Modes

# Preflight (no write)
Plugin = "podheitor-mssql: dry_run=yes"

# VDI tracing (Windows)
set PODHEITOR_VDI_DEBUG=1  # then restart FD + run job

# Backend info
/opt/bacula/bin/podheitor-mssql-backend --version --features

16.2 Common Error Table

Error Cause Fix
Login failed for user Wrong creds or authtype Check user/passfile/authtype
VD_E_ABORT VDI closed by SQL Check SQL error log; increase maxtransfersize
PTCOMM header bad length Stray stdout output Check ODBC debug env vars
Cannot find master key TDE capture, no master key CREATE MASTER KEY ENCRYPTION BY PASSWORD
AG replica not preferred ag_preference not met Set ag_preference=primary temporarily
Address already in use Metrics port conflict Change metrics_endpoint port
FIFO permission denied Not in mssql group usermod -aG mssql bacula or use_sudo=yes
RESTORE VERIFYONLY failed Backup/media corruption Investigate with DBCC

16.3 Log Locations

Log Linux Windows
Bacula FD /opt/bacula/log/bacula-fd.log Windows Event Log
PodHeitor backend /opt/bacula/log/podheitor-mssql-&lt;jobid&gt;.log %PROGRAMDATA%Baculalog
SQL Server /var/opt/mssql/log/errorlog Windows Event Log

17. Roadmap

Version ETA Highlights
v1.0.0 GA 2026-04-28 All F0–F10. 16/16 E2E PASS.
v1.1 Q3 2026 Linux ARM64, DEB package, NSIS installer, NetApp ONTAP
v1.2 Q4 2026 Kubernetes sidecar (Helm chart)
v1.3 Q1 2027 Query Store backup, Extended Events
v1.4 Q2 2027 Bacularis UI full plugin descriptor
v1.5 Q3 2027 Cross-version replication (2019→2022)

18. Validation Evidence

E2E Test Results (Run 9 — Installed Packages)

[22:01:XX] ✓ PASS: T01-Full-backup
[22:02:XX] ✓ PASS: T02-Diff-backup
[22:03:XX] ✓ PASS: T03-Log-backup
[22:04:XX] ✓ PASS: T04-Restore-inplace
[22:05:XX] ✓ PASS: T05-Restore-new-db
[22:06:XX] ✓ PASS: T06-Compressed-backup
[22:07:XX] ✓ PASS: T07-Verify-backup
[22:08:XX] ✓ PASS: T08-MultiDB-backup
[22:08:XX] ✓ PASS: T09-Instant-restore
[22:08:XX] ✓ PASS: T10-Replicate-Primary-Full
[22:08:XX] ✓ PASS: T10b-Replicate-Primary-Log
[22:09:XX] ✓ PASS: T11-Replicate-Follower-Apply
[22:09:XX] ✓ PASS: T11b-Follower-Idempotency
[22:13:XX] ✓ PASS: T11c-RPO-Simulation
[22:13:XX] ✓ PASS: T12-AG-Bootstrap
[22:13:XX] ✓ PASS: T13-Fanout-Primary-Full

PASS: 16 / 16   |   FAIL: 0 / 16

Cargo Test Results

OL9:      test result: ok. 311 passed; 0 failed
Win2025:  test result: ok. 381 passed; 0 failed

Release Artifacts

podheitor-mssql-plugin-1.0.0-1.el9.x86_64.rpm  (562 KB)
SHA256: 4b785ae74e6e9a5151eb9b10304daae8daf9402888cce6ae5459533b668e809d

podheitor-mssql-plugin-1.0.0-win64.zip          (772 KB)
SHA256: 33a6883df08f52ab8f881d70f1efa1760d753dc09067da8f19476bdfa1de2973

Contact & Licensing

Author: Heitor Faria | Email: heitor@opentechs.lat Phone: +1 786 726-1749 | WhatsApp: +55 61 98268-4220

Copyright © 2026 Heitor Faria. All rights reserved. Proprietary and confidential. Commercial licensing available.

Trazendo sua proposta do Bacula Enterprise, Veeam, Commvault ou Netbackup? Mínimo 50% de desconto + muito mais funcionalidades. heitor@opentechs.lat | +1 786 726-1749 | +55 61 98268-4220

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

Leave a Reply