Skip to content

Quickstart

From an empty Linux management host to a signed, verified recovery report in ten commands. Everything below runs on the controller host; the Proxmox VE node only ever receives a short-lived worker over SSH and keeps nothing afterwards. Getting started covers what this page skips: release verification, upgrades, rollback, and the full credential contract.

Before you start

  • A Linux amd64 management host (a small VM is fine) with the OpenSSH client (ssh and scp) and SSH access to one Proxmox VE 9.x node (with PBS 4.x) that has qemu-img, proxmox-backup-client, and libguestfs-tools installed. Set up SSH access to the node first if you have not.
  • A PVE API token with the documented privileges and a read-only PBS credential.
  • One VM that has a PBS backup, and a TCP listener in it (SSH on port 22 is enough) to use as the health check.
  • An unused VMID on the node for the temporary sandbox VM.
  • File-based overlay storage with room for guest writes, firmware copies, and any full disk copies selected by copy_before_boot: auto. The 5 GiB reserve is only a minimum check; the default copy cap is unlimited.

Do this in a dedicated lab or staging cluster first. The SSH identity that uploads the worker is root-equivalent on that node.

1. Install the CLI

Download certistack-linux-amd64, CONTAINER-IMAGE.txt, SHA256SUMS-bin.txt, and SHA256SUMS-bin.txt.asc from the latest release, verify them as described in getting started, then:

sudo install -m 0755 certistack-linux-amd64 /usr/local/bin/certistack
certistack version

To build from source instead:

git clone https://github.com/cliftcloud/certistack.git && cd certistack
make build-linux && sudo install -m 0755 bin/certistack-linux-amd64 /usr/local/bin/certistack

2. Create the report signing key

sudo install -d -m 0700 -o "$USER" /secure/certistack
certistack keygen --private /secure/certistack/controller-signing.ed25519 \
  --public /secure/certistack/trusted-signers.pub

The private key stays on the controller and is never sent to PVE. The public key file doubles as your trusted-signer keyring for verification in step 8.

3. Write the controller environment file

Create /secure/certistack/controller.env with mode 0600. Values are read literally; the file is never shell-sourced.

PVE_URL=https://pve-node-01.example.com:8006
PVE_TOKEN_ID=certistack-svc@pve!automation
PVE_TOKEN_SECRET=your-generated-token-secret
# Only when PVE uses a private CA that is not in the host trust store.
CERTISTACK_PVE_CA_SOURCE=/secure/certistack/pve-ca.pem
PBS_REPOSITORY=certistack-ro@pbs!ro@pbs.example.com:8007:datastore
PBS_PASSWORD=your-pbs-token-secret
# Only when the PBS certificate is not signed by a trusted CA: copy the
# fingerprint exactly as the PBS dashboard shows it (Show Fingerprint).
PBS_FINGERPRINT=aa:bb:cc:...:ff
# Only for encrypted backups: the PBS encryption key (owner-only), and its
# passphrase if it has one.
# CERTISTACK_PBS_KEYFILE=/secure/certistack/pbs-encryption.key
# PBS_ENCRYPTION_PASSWORD=your-key-passphrase

CERTISTACK_NODE=pve-node-01
CERTISTACK_SSH_HOST=pve-node-01.example.com
CERTISTACK_SSH_USER=certistack
CERTISTACK_SSH_IDENTITY=/secure/certistack/id_ed25519
CERTISTACK_SSH_KNOWN_HOSTS=/secure/certistack/known_hosts
# Only if the SSH account is not root; it must then have passwordless sudo.
CERTISTACK_SSH_SUDO=true

Populate known_hosts from a host key you approved out of band; strict host key checking is always on.

4. Write your first plan

certistack init lists the VMs that have a complete PBS backup. It only reads.

certistack init --env-file /secure/certistack/controller.env
VMs with a complete backup in the PBS repository:

VMID  NOTES   NEWEST BACKUP         BACKUPS
100   web-01  2026-09-27 03:00 UTC  14
101   db-01   2026-09-27 03:04 UTC  14

Then write a plan for one of them:

certistack init --env-file /secure/certistack/controller.env \
  --vm 100 --key /secure/certistack/controller-signing.ed25519 \
  -o my-first-plan.yaml

init reads the VM's newest backup and the node, changing nothing, and writes a commented plan that validates. The plan contains:

  • a sandbox VM ID and SDN zone and VNet IDs that are free;
  • a storage for the sandbox's overlays;
  • a first probe chosen from the backup. A Linux VM is checked on SSH at 192.0.2.10, the address the run gives it on the isolated VNet. A Windows VM is checked through its guest agent.

If SSH is not what the VM is for, change the probe marked CHANGE to its service (see the test-plan reference). The 192.0.2.0/24 addresses are fine as they are: the sandbox is a disconnected L2 domain.

To write the plan by hand instead, start from examples/minimal-single-vm.yaml and change the three values marked CHANGE.

5. Validate and preview it

certistack validate my-first-plan.yaml
✓ Plan is valid
  Plan ID:        recovery-pve-node-01
  Name:           Recovery check of 1 VM(s) on pve-node-01
  Tiers:          1
  Ephemeral VMs:  1
  Probes:         1
  Network:        simple (zone=csZone1, vnet=csVnet1)
  Frameworks:     []

certistack plan then shows, step by step, what the run will do on the node, and changes nothing:

certistack plan my-first-plan.yaml --env-file /secure/certistack/controller.env

6. Run it

certistack run my-first-plan.yaml --env-file /secure/certistack/controller.env

The controller checks the node, uploads the worker, maps the latest backup read-only, creates the overlay and the isolated VNet, boots the sandbox VM, runs the probe, tears everything down, and signs the report. Expect a few minutes: the full mapped-image integrity pass reads the whole disk.

[00:00:01] INFO [Admission] Host admission check: RAM 41.2% (max 85.0%), IO wait 0.8% (max 12.0%) → ADMISSION GRANTED
[00:00:03] INFO [Storage] Mounted PBS snapshot vm/100/... (scsi0) read-only via /dev/loop2.
[00:00:18] INFO [Stage 1] QMP hypervisor handshake established → VM RUNNING.
[00:00:32] INFO [Stage 2] TCP 192.0.2.10:22 → PASSED (3ms).
[00:00:37] SUCCESS DR validation plan passed | duration: 34.1s | Ed25519 certificate: /var/lib/certistack/reports/recovery-pve-node-01/<report-id>.json

If the run is refused before anything is created, the message names the preflight or admission check that failed; see troubleshooting.

7. Read the timeline, then the evidence

The signed JSON under /var/lib/certistack/reports/<plan-id>/ is the evidence. It records the resolved snapshot, every mapped disk and its integrity result, each probe with its origin, the RTO breakdown, and the teardown evidence proving that every owned resource was removed.

8. Verify the signature with your pinned key

certistack verify-report --keyring /secure/certistack/trusted-signers.pub \
  /var/lib/certistack/reports/recovery-pve-node-01/<report-id>.json

A report never establishes trust in its own embedded key; verification succeeds only against the keyring you supply.

A valid signature says the report is unchanged and came from a key you trust. It does not say the recovery passed: read All Passed: in the output, or use --format json in a script. See Reports and verification.

9. Keep the evidence

Retain the signed JSON together with the public key that verifies it; that pair is what an auditor needs. Do not edit a report: any changed byte invalidates the signature. Human-readable HTML binders and PDF certificates are rendered from the same JSON by the Enterprise Edition daemon, which is what the Homelab+, Team, and MSP Fleet tiers add on top of this engine.

10. Make it yours