Technical whitepaper — PodHeitor SFTP for Bacula

Technical whitepaper — PodHeitor SFTP for Bacula

Agentless backup over SFTP/SSH, zero deployment on the remote host, support for network equipment (Cisco/Juniper/MikroTik/…), NAS (Synology/QNAP/TrueNAS), cloud SFTP (AWS Transfer, Hetzner Storage Box), and cloud VMs. Replaces fragile SSHFS/NFS/CIFS mountpoints with stateless per-job SFTP connections.

Companion document to the PodHeitor SFTP plugin page.

1. The problem: SSHFS/NFS/CIFS mountpoints are fragile

The traditional “remote backup without a Bacula agent” pattern is to mount the remote filesystem via SSHFS/NFS/CIFS on the FD host. That sounds simple but fails in the field in many ways:

Mountpoint problem SFTP advantage
SSHFS/NFS/CIFS mounts go stale → backups fail silently SFTP opens a fresh connection per job — no stale state
Mount requires FUSE or a kernel module on the FD host SFTP/SSH is pure userspace (Rust ssh2 / libssh2) — no kernel deps
Credentials stored in /etc/fstab or systemd mount files Credentials live in Bacula config, encrypted at rest
Mount exposes the entire filesystem to the FD host SFTP scopes access to path — no local mount surface
NFS/CIFS firewalling is complex (portmapper, SMB ports) SFTP/SSH = single TCP port (22), easily firewalled
Stale mounts block the FD process (hung stat calls) SFTP timeout is per-operation — never blocks the daemon
Network devices (switches, routers, firewalls) can’t be mounted SFTP/SSH is native on network equipment
Cloud SFTP services (AWS Transfer, Hetzner Storage Box) Direct SFTP connection — no mount layer

2. Architectural model

┌──────────────┐     PTCOMM protocol      ┌──────────────────────────┐
│  Bacula FD   │◄═══════════════════════►  │ podheitor-sftp-backend   │
│              │     (stdin/stdout)         │ (Rust + libssh2)         │
│ loads .so:   │                           │                          │
│ podheitor-   │   Handshake → Job Info    │ connect() → walk() →    │
│ sftp-fd.so   │   → Params → Start →     │ FNAME/STAT/DATA →       │
│ (Rust cdylib,│   ← file data ←          │ include/exclude filter   │
│ metaplugin-  │                           │                          │
│ rs)          │                           │                          │
└──────────────┘                           └──────────────────────────┘

Same PodHeitor cdylib + backend architecture. The backend hosts the SFTP client (libssh2 via Rust ssh2), recursively walks the remote path, and streams metadata + data over PTCOMM. No intermediate disk; the FD receives bytes directly.

3. Features

  • Agentless — SFTP/SSH only, no software on remote hosts
  • Full / Incremental / Differential — native Bacula levels via mtime comparison
  • Include/exclude filters — glob-based (*.pdf, *.tmp) for files and directories
  • Metadata preserved — permissions, UID/GID, timestamps, symlinks
  • Authentication — SSH key (ed25519, RSA, ECDSA), agent forwarding, password
  • Host key verification — MITM protection via known_hosts
  • File listingestimate (pre-backup), catalog queries, restore tree browsing
  • Multiple sources — N SFTP servers in a single FileSet
  • Compression & Encryption — Bacula’s native LZ4/GZIP + AES

4. Documented use cases

4.1 Network equipment

Vendor Models / OS
Cisco Catalyst 9000 (IOS-XE), Nexus (NX-OS), ISR/ASR
Juniper EX, QFX, SRX, MX (Junos)
MikroTik All RouterOS devices
Arista 7000, 7500 series (EOS)
HPE Aruba CX switches, Instant AP
Dell PowerSwitch (OS10)
Fortinet FortiGate (FortiOS)
Palo Alto PA series (PAN-OS)
Ubiquiti UniFi, EdgeRouter
Others VyOS, OPNsense, pfSense

4.2 NAS and Storage

Synology DiskStation SFTP/SSH built-in (DSM)
QNAP SFTP/SSH built-in (QTS)
TrueNAS / FreeNAS SFTP/SSH built-in
Asustor, TerraMaster SFTP/SSH available

4.3 Cloud SFTP services

AWS Transfer Family Managed SFTP endpoint
Azure Blob Storage SFTP access (preview/GA)
Hetzner Storage Box Native SFTP/SSH (port 23)
Files.com, ExaVault, GoAnywhere MFT Enterprise SFTP

4.4 Cloud VMs and hosting

AWS EC2, GCP Compute, Azure VMs, DigitalOcean, Linode, Vultr, Hetzner Cloud, OVH; cPanel/WHM, Plesk, DirectAdmin — any hosting with SFTP/SSH.

4.5 Embedded and IoT

Raspberry Pi, IoT gateways — lightweight backup without mount overhead. Devices with limited storage — no FUSE/mount layer required.

5. Plugin parameters

Parameter Required Default Description
host yes SFTP server hostname or IP
port no 22 SFTP port
user yes SFTP username
password no SFTP password (prefer keyfile)
keyfile no Path to private key
passphrase no Key passphrase
path yes / Remote base directory
known_hosts no Path to known_hosts
verify_host no yes Verify host key
timeout no 30 Connection timeout (seconds)
include no Include glob patterns (e.g. *.pdf,*.doc)
exclude no Exclude glob patterns (e.g. *.tmp,*.log)
abort_on_error no no Abort job on read error

6. Quick start

# 1. Prepare key for SFTP access
sudo mkdir -p /etc/bacula/.ssh
sudo ssh-keygen -t ed25519 -f /etc/bacula/.ssh/id_ed25519 -N ""
sudo ssh-copy-id -i /etc/bacula/.ssh/id_ed25519.pub [email protected]

# 2. Configure FileSet
FileSet {
  Name = "SFTP-Backup"
  Include {
    Options { Signature = SHA256; Compression = LZ4 }
    Plugin = "podheitor-sftp: host=server.local user=backup keyfile=/etc/bacula/.ssh/id_ed25519 path=/data"
  }
}

Job {
  Name     = "SFTP-Daily"
  Type     = Backup
  Level    = Incremental
  FileSet  = "SFTP-Backup"
  Client   = myserver-fd
  Storage  = File1
  Pool     = Default
  Schedule = "WeeklyCycle"
}

7. Include/exclude examples and multi-source

# PDFs and Office docs only
Plugin = "podheitor-sftp: host=server user=backup path=/data include=*.pdf,*.docx,*.xlsx"

# Skip temp and cache
Plugin = "podheitor-sftp: host=server user=backup path=/data exclude=*.tmp,*.cache,*.log"

# Network switch — configs only
Plugin = "podheitor-sftp: host=10.0.0.1 user=admin path=/ include=*.conf,*.cfg verify_host=no"

# MikroTik — backup and export files
Plugin = "podheitor-sftp: host=10.0.0.3 user=admin path=/ include=*.backup,*.rsc verify_host=no"

Multi-source in a single FileSet:

FileSet {
  Name = "AllServers"
  Include {
    Options { Signature = SHA256; Compression = LZ4 }
    Plugin = "podheitor-sftp: host=web1.prod user=deploy keyfile=/etc/bacula/.ssh/key path=/var/www exclude=*.log"
    Plugin = "podheitor-sftp: host=web2.prod user=deploy keyfile=/etc/bacula/.ssh/key path=/var/www exclude=*.log"
    Plugin = "podheitor-sftp: host=nas.local user=admin keyfile=/etc/bacula/.ssh/key path=/volume1/shared"
    Plugin = "podheitor-sftp: host=switch-core user=admin keyfile=/etc/bacula/.ssh/key path=/ include=*.conf verify_host=no"
  }
}

8. Restore

Restore operates in the @sftp/host:port/path namespace:

bconsole
* restore where=/tmp/sftp-restore select all done yes

9. Requirements

Component Minimum Notes
Bacula Community 13.0.0+ Metaplugin framework required
libssh2 / openssl system Runtime libs (system packages)
Remote host SFTP/SSH access No agent

10. License posture

Proprietary — LicenseRef-PodHeitor-Proprietary (both the FD-side .so and the backend binary). No statically-linked third-party code in this product is AGPL or any other copyleft. Transitive Rust dependencies (ssh2, libssh2-sys, libc, parking_lot, openssl-sys) are all MIT / Apache-2.0.

Ready to evaluate?

30-day free trial for qualified SFTP deployments (network equipment, NAS, cloud, embedded). We guarantee at least 50% off vs Bacula Enterprise, Veeam, or Commvault, with more features — including pure agentless without the fragile mountpoint the competitor forces.

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

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

Leave a Reply