Repository navigation
Releases: tinkerologie/vault
Release list
v2026.10.02
Fixed
- Leaving the Logs page freezes the UI (console renders every log line) (#507)
SHA256 Checksum:
2ec0d24447a6b64373e22329aaa8c889f3aecf370a38a69007209c9f6d78ad32 vault-2026.10.02.txz
v2026.10.01
Added
-
Memory statistics and a memory limit (#500): Settings → General has a new Memory card. It shows the daemon's heap in use, total Go memory, resident and peak RSS, the share of CPU spent in garbage collection, GC cycles, goroutines, and the effective memory limit. The same figures are in
GET /api/v1/settings/runtimeand the diagnostics bundle. The new Memory limit (MiB) setting (memory_limit_mb, default 0 = off) applies a soft limit immediately, with no restart. AGOMEMLIMITenvironment variable still takes precedence. The card lists S3 jobs whose upload buffers (part size × 5 × parallel uploads) could exceed the limit, the run log warns when such an upload starts, and the S3 form shows the per-stream buffer size under Part size. -
Real-time progress (#456): the dashboard and restore wizard show the current item's phase and the work actually done, such as "Uploading 84.2 GB / 225 GB — 37%", with the transfer rate, an estimated time left, and a "No progress for Xm" hint when an operation goes quiet. Remote uploads, verification read-back, and restore downloads report real byte counts across all of an item's files, instead of 0% for hours followed by a jump to 99%. Folder backups first scan the source ("Scanning… 12,340 files") and then report archiving and chunking against its size; incremental runs count unchanged files as done. Deduplicated restores report against the files being restored, VM backups report libvirt's byte counters, VM restores report disk copies, ZFS reports send and receive bytes, and container volumes show live bytes and files. A phase whose total is unknown shows its counters without a percentage.
GET /runner/status, thebackup_progress/restore_progressWebSocket events, and the MCPget_runner_statustool carry the new fields. -
Troubleshooting detail in run logs (#455): backup and restore run logs now start with a "Run details" line (job, run, destination and storage type, restore point), time each stage of each item ("Phase upload completed for appdata in 4m12s"), and name the stage that failed. Retried storage requests, resumed streams, and restarted download parts each get a redacted warning line, capped at 20 per run. Item and summary lines report bytes stored, uploaded (including retries), read back, and restored, plus retry counts. Verification lines include bytes read and the time taken.
Changed
-
Lower memory churn on the data plane (#500): deduplicated backups no longer allocate an 8 MiB chunking buffer for every file. A backup of 40,000 small files on Unraid went from 4,877 garbage collections (about half of its CPU) to 16, and ran about 3× faster. Folder and container archives reuse pooled copy buffers instead of allocating 32 KiB per file. Deduplicated backups seal chunks straight into the pack buffer and no longer copy each pack (up to 24 MiB, more for large chunks) before uploading it. Chunk reads reuse a pooled buffer. VM disk, NVRAM and database-dump copies to local paths use the kernel's
copy_file_rangeagain, which allows block cloning on ZFS 2.2 and later. Progress reporting, cancellation and stall detection are unchanged. -
Test coverage (#458): backend statement coverage rises from about 79% to about 93%, and web library line coverage from 65% to 98%. The new tests cover the libvirt VM, ZFS, FUSE mount, container, runner, storage (SFTP, SMB, WebDAV), API, CLI and database paths. Narrow, unexported test seams were added where code talked to libvirt, ZFS, FUSE, SMB or the kernel directly; production behaviour is unchanged.
-
Selective restore size (#455): restoring a few files from a backup item reports the bytes actually restored instead of the whole item's recorded size, in History, the run log, and the restore summary. Restore summaries also name the job and destination.
-
mDNS advertisement control (#468): new Network Discovery (mDNS) setting (
MDNS_ADVERTISEinvault.cfg) and--mdnsflag onvault daemonandvault replicaturn LAN discovery off without changing the bind address. Both default to on. -
Ansible deploys install the real
plugin/rc.vaultinstead of an outdated inline copy, so live verification runs the same service script the plugin package ships (#468). -
Verification cost warning (#457): the job form now warns when Verify backups after writing is on and the destination is remote: under the toggle, beside the destination in the express form, and on the review step. After every backup, Vault reads all the data that backup references back from that destination, including chunks reused from earlier backups, so a large backup can mean significant bandwidth and a long wait near 100%. S3 and S3-compatible destinations also note possible request, retrieval, or egress charges. The warning suggests scheduled verification for large backups.
-
Formatting hooks no longer fail on a clean checkout: files merged without pre-commit were reformatted (no content changes), workflow YAML keeps the organization's single-quote style through
.prettierrc.json, and a new CI Format job runs the same pinned formatting hooks on every pull request. -
Pull requests get path labels and at most one type label,
bugorenhancement, from the shared tinkerologie PR labeler. Dependabot pull requests whose largest update is a patch or minor version merge automatically once the checks and commit statuses on their head commit have finished without a failure (a Codecov check that is still pending is not waited for; a Codecov failure blocks); majors, and pull requests with open review threads, stay for review. -
Dependabot also updates the Playwright end-to-end test dependencies (root
package.json) and the Docker base images in the rootDockerfile. -
Release workflows hardened (least-privilege job permissions, no expressions in shell scripts, no persisted checkout credentials, no dependency caches); scheduled workflows run on Australia/Brisbane time.
-
Workflow layout tidied after the release hardening (no behaviour change):
permissions:blocks moved back to the top of their workflow or job indocs.yml; blank-line layout restored inci.yml,codeql.yml,docker.yml,release.yml; theid-token: writecomment inci.ymlnames the Codecov OIDC upload. -
Releases push the updated PLG hash to
mainwith a token from the Tinkerologie-Release GitHub App, the only actor allowed to bypass the pull-request rule onmain, so the Unraid plugin manager and Community Applications see each new release. -
Tinkerologie-Bot governance report updated to v1.4.3: an older run can no longer post a stale report after a newer run passed. Workflow formatting tidied.
-
Repository standardised to the tinkerologie layout (STANDARDS.md in tinkerologie/.github): workflows renamed to the standard set (CI, Validate, Release, PR Governance, ...), every action pinned to a commit SHA, shared Dependabot,
.editorconfig,.gitattributesandCODEOWNERSfiles, and organization-wide labels and community files. -
Tinkerologie-Bot's comment is now a readable "Pull request checklist": each problem is a numbered heading with plain-English guidance and its items listed underneath, and report updates for a pull request are serialised so an older run can never overwrite a newer one.
-
Contribution governance: issues without a
bugorenhancementlabel are labeled automatically when their type can be determined (issues labeledquestion,duplicate,invalid,wontfix, orno-typeare skipped), so CodeRabbit can plan them. Pull requests that conflict with the default branch are labeled, and their authors are notified in a comment. Stale issues and pull requests are labeled after 14 days without activity and closed 30 days later. PR governance now re-validates a human-authored, non-draft PR when a bot such as CodeRabbit edits its description, instead of skipping the check and reporting a pass, only maintainers with write access may useNoneinstead of linking an issue, and a link to an issue that does not exist now fails the check. Codecov patch coverage must now reach 90%. -
Tinkerologie-Bot now posts one comment on pull requests that fail PR governance, listing every problem to fix, and updates it once they are fixed. PR governance runs from the default branch's workflow (
pull_request_target), so a pull request cannot change its own check. -
Tinkerologie-Bot governance report updated to v1.3.3: the report is capped below GitHub's comment size limit, and an older run that finishes late can no longer overwrite a newer report.
-
Updated repository, plugin template, documentation and docs-site URLs to
tinkerologieorganization (Go module path unchanged; GitHub redirects keep it working) -
Configured Codecov GitHub Actions workflow for tokenless coverage uploads
Fixed
- Flaky adaptive re-check test (#506):
TestAdaptivePostponeRecheckA458now waits for the re-checked run to finish finalizing before its temporary directory is removed. It had failed CI onmainwith "directory not empty" whenever the database backup was still writing. - WebDAV uploads failing with "423 Locked" during parallel uploads: gowebdav re-creates every parent directory before each upload. When two uploads into the same backup folder overlapped, a server that locks a directory while creating it (such as Go's
x/net/webdav) answered 423, and one upload failed even though the folder existed. An MKCOL answered 423 is now re-sent briefly (which also works for Digest-authenticated servers), and one that still returns 423 for a directory that exists is treated as "already exists", like the 403 case from #242. A 423 for a missing directory is still retried. - Unrealistic transfer rates for short operations (#455): a backup or restore that finishes in under a second no longer reports rates like "10000 GB/s". Run logs, Discord notifications, History and the Activity timeline only show a rate for operations of at le...
v2026.10.00
Added
-
Deduplicated backups can be recovered with the backup password (#451): Each dedup destination's master key was sealed only with the server's
vault.key, so losing that file made every dedup backup unreadable. Dedup backups now also keep a copy of the master key sealed with the backup password, written by the first dedup backup that runs with a password set. After a flash-drive loss or reinstall, the Recovery wizard uses the backup password to unlock dedup destinations and re-seal them with the new server's key; the previous_vault/repo.jsonis kept as a.bakcopy. Restore, browse, mounts andvault recoveralso open a destination with the password when the key does not match. Settings → Security → Server key downloadsvault.keyfor an off-server copy, and the emergency kit mentions it. Closes #451. -
Recover files without a Vault server, including on Windows (#313): The new
vault recovercommand reads backups straight from a storage destination and extracts files into a local folder. It needs no Vault database and runs on Linux, macOS and Windows. A Windows build,vault-windows-amd64.exe, is attached to each release.vault recover listshows the backups,contentslists an item's files, andextractrecovers whole items or chosen paths. Deduplicated backups, classic backups, incremental and differential chains, and age-encrypted backups are supported. Deduplicated backups need the original server'svault.key, or the backup passphrase if one was set when they ran (#451); encrypted classic backups need the backup passphrase. The passphrase is read from a file or theVAULT_PASSPHRASEenvironment variable. Containers are recovered as their volume files plus the Unraid template and configuration, and VMs as their disk images and definition. Storage is opened read-only and stored checksums are verified. On Windows, names Windows cannot store and names that differ only by case are renamed, and every rename is listed in a report written next to the recovered files. NFS exports must be mounted first. See the Disaster Recovery guide. Closes #313. -
Multi-job item restores in restore wizard (#438): The restore wizard now supports restoring selected items across different backup jobs in a single restore flow. In Step 2, restore points are aggregated across all relevant jobs with visible source job badges, item coverage counts, missing item explanations, and legacy membership indicators. Users can choose one restore point per source job, view a real-time coverage plan, and use one-click recovery actions to remove uncovered items. In Step 3, restore units are reviewed with scoped destination overrides, passphrases, clean destination controls, and per-unit pre-flight checks, followed by sequential execution and live multi-unit progress tracking. Closes #438.
-
Headless Playwright browser regression test suite: Added an end-to-end browser testing suite with GitHub Actions CI workflow (
playwright.yml) and deterministic REST/WebSocket mocks (tests/mocks/vault-api.ts), validating desktop shell navigation and multi-job restore wizard flows headless without requiring a live Unraid host. -
Expanded Playwright browser test coverage across core UI workflows: Added automated end-to-end browser specifications for Backup Jobs (creation wizard, item pickers, schedule/retention, run now, delete), Storage Management (destination creation, test connection, remote browser drawer, refresh, delete), Settings (tab navigation, dark/light theme, Discord webhook tests, diagnostics zip export), Activity Logs (search and level filters, wrap/details toggle, log purge), History (operation metrics, search, run log inspector), and Disaster Recovery (5-step cold recovery wizard).
Changed
- Uninstalling keeps
vault.key(#451): Removing the plugin no longer deletes the server key, so a reinstall opens the same deduplicated destinations. Delete/boot/config/plugins/vault/by hand to remove every trace of Vault.
Fixed
-
Clear message when a dedup destination needs a different
vault.key(#451): Browsing, restoring, mounting andvault dedupnow explain that the destination was sealed with another server's key and how to recover, instead of a generic error. Browsing returns HTTP 424 with codededup_key_mismatch. A backup that runs on a mis-keyed destination still completes, with a warning in its run log. -
Replication no longer bounces jobs between reciprocal peers (#440): When two Vault servers are configured as each other's replication source, a sync now imports only the jobs the remote server created itself (
source_id0) and skips jobs it had replicated from elsewhere. Previously each sync re-imported the other side's copies, creating ever-deeper[A] [B] …job names. Only the pulling server needs the update. Nested jobs created before this fix are not removed automatically; delete them manually, or delete and re-create the affected replication source. The Replication page now describes replication as pulling from the remote server. Closes #440. -
Partial folder restores keep directory owner and permissions (#442): Restoring selected files from a folder backup now applies each containing directory's recorded owner, group and mode, instead of creating it as
root:root 0755. Previously SMB users could not rename, move or delete files in the restored directory. Applies to deduplicated and classic backups. Unselected sibling files are still not restored. Closes #442. -
FUSE backup mounts moved out of
/mntroot (#446): Read-only backup mounts now live under Unraid's/mnt/addons/vault-fuse/mount-<id>, or under a temporary directory when/mnt/addonsdoes not exist, and each mount's directory is created only when that mount starts. If a mount directory cannot be created under/mnt/addons/vault-fuse, Vault retries once under the temporary directory (unlessfuse_mount_base_diris configured). This clears the Fix Common Problems warning "Invalid folder vault-fuse contained within /mnt". On startup Vault removes the old/mnt/vault-fusedirectory and leftover empty session directories, never deleting non-empty content. Uninstall removes both mount roots only when empty, and pool discovery never treatsaddonsorvault-fuseas storage pools./mnt/user/.vaultis Vault's database snapshot directory (<pool>/.vault, shown under/mnt/userbecause Unraid exposes top-level pool folders as shares); it is intentional and can be moved under Settings → General → Database Location. Closes #446. -
Partial-restore file browser no longer times out on slow remote storage (#449): Listing a large restore point on B2/S3 can take longer than the normal 10–15 second request limits, so the file browser failed with "vault daemon unavailable". Only the restore-point contents request now gets longer limits: 120 seconds in the daemon, 125 seconds in the Unraid plugin proxy and 130 seconds in the browser. All other requests keep their current limits. A listing that runs past 115 seconds now gets a clear timeout error from the daemon instead of a dropped connection. The proxy now reports a timed-out request as HTTP 504 "vault daemon request timed out" instead of "vault daemon unavailable", and stops waiting after 5 seconds when the daemon cannot be reached. When the file list fails to load because of a timeout, network error or server error, the picker now shows "Could not load the file list" with a Retry button, and no longer says a whole-archive extract will run. While a file list is loading or has failed to load, Start Restore is disabled until the list loads or you choose Restore whole item, so a timeout can no longer turn an intended partial restore into a full one. The whole-item fallback note is shown only when the restore point cannot be browsed. Closes #449.
Security
- The backup password now also protects deduplicated backups (#451): Anyone with the backup password and access to the backup storage can read deduplicated destinations, as with
vault.key. Use a strong backup password.
SHA256 Checksum:
ead73c4fcee217576e5fefd19aad651b583e50bd389b33a1a6266a641ce54e4a vault-2026.10.00.txz
v2026.09.01
Added
-
FUSE read-only mounts for backups (#312): Operators can now mount deduplicated backup snapshots as standard read-only filesystems on the host to inspect archive contents, compare versions, or copy out specific files using standard Linux shell tools and file managers. Backups can be mounted directly from Step 3 of the Restore Wizard via "Mount as Filesystem" or through the
vault mountCLI subcommand. Active mounts are tracked and managed via SQLite and exposed through REST API routes (GET /api/v1/mounts,GET /api/v1/mounts/{id},POST /api/v1/mounts/{id}/unmount,POST /api/v1/jobs/{id}/restore-points/{rpid}/mount), WebSocket event broadcasting (mount.started,mount.active,mount.unmounted,mount.failed), and an "Active Backup Mounts" card on the Restore page with path copying and one-click unmounting. Inactive mounts are automatically reclaimed by an idle timeout sweeper. Closes #312. -
Direct file downloads from storage browser modal (#311): Operators can now download individual files directly from the Storage browser dialog. Files streamed from destinations include an appropriate
Content-Disposition: attachment; filename="..."header, and the browser UI offers per-file download actions with in-flight loading indicators and toast feedback. Closes #311. -
Restore progress, live run logs, and completion status on restore page (#316): Surfaced real-time restore progress and results directly on the Restore page. Step 3 of the Restore Wizard now features an overall progress bar with active item, item type, elapsed time, and phase indicators driven by WebSocket runner events. Once the restore finishes, an outcome banner details item success/failure counts and total restored bytes with a quick reset action, and an embedded live restore log panel displays formatted, level-coded log messages streamed from the daemon. Additionally, global toast notifications alert operators when a restore operation completes or fails. Closes #316.
-
Future jobs queue visibility and cancellation (#302): Concurrently triggered backup, restore, and cleanup operations are now tracked with unique queue identifiers, operation kinds (
backup,restore,delete), and statuses (queued,running). An "Active & Queued Operations" card is now surfaced at the top of the History tab with real-time WebSocket synchronization. Operators can cancel queued backup and restore operations directly from the queue before execution begins viaPOST /api/v1/queue/{id}/canceland the web UI. Closes #302. -
Auto-add newly created containers to container backup jobs (#324): Jobs configured to protect Docker containers can now enable "Auto-include newly created containers". During preflight reconciliation, newly deployed containers on the host are automatically discovered, configured with
appdata_only=true(safely excluding bulk media mounts by default), and added to the job's item list. Containers tagged with thevault.excludelabel are respected and ignored. Additionally, containers missing past the configurable absence grace period are automatically pruned from the job. Closes #324. -
Appdata path prompt and default exclusion of non-appdata volumes (#317): When configuring container backup jobs, Vault now prompts for the host's appdata root paths (defaulting to
/mnt/user/appdataand/mnt/cache/appdata). Container bind mounts outside the configured appdata directories (such as bulk/mnt/user/mediastorage) are automatically excluded by default to avoid unintended multi-terabyte archives, while Docker named volumes and appdata mounts remain protected. Users can inspect excluded mounts and toggle them back in at any time. Closes #317. -
User override for auto-skipped shared-data mounts (#307): Added an explicit toggle in the container job editor allowing users to include bind mounts that Vault automatically skips as shared or media data, accompanied by clear warnings regarding backup duration and storage consumption. Closes #307.
-
Periodic full backups alongside differential and incremental chains (#322): Jobs using differential or incremental backup strategies can now configure an independent periodic full backup schedule (e.g.
@weeklyor custom cron). When due, the runner forces a full base archive and establishes a fresh chain root, preventing unbounded chain lengths and bounding restore replay depth. Closes #322. -
Custom cron expression schedule picker (#309): Added a "Custom" schedule mode to the Schedule Picker across job creation, job editing, and periodic full backup configuration. The field validates standard 5-part cron syntax with real-time cadence descriptions and prevents invalid schedules from being saved. Closes #309.
-
Post-backup chunk verification for deduplicated jobs (#382): Deduplicated backup jobs with
verify_backupenabled now immediately read back and hash all written chunks upon session completion. Verification status is recorded in restore point metadata and surfaced in the job run results, guarding against silent backend write corruption before staging files are cleared. Closes #382.
Changed
-
Go toolchain and dependency updates (#437): Updated Go from 1.26.5 to 1.27.1 across module configuration, CI workflows, contributor docs, and the Docker builder image (
golang:1.27-alpine). Adopted Go 1.27 standard library features (uuid,strings.CutLast) andgo fixmodernizers (range n,slices.Contains,strings.SplitSeq/FieldsSeq,sync.WaitGroup.Go,atomic.Int64/Bool,errors.AsType,testing.T.Context,min/max,strings.Cut/CutPrefix), replacing the directgithub.com/google/uuiddependency with the standard libraryuuidpackage, and updated Go and web npm dependencies to their latest releases. Closes #437. -
Unflatten file contents and modernize selection in restore UI (#323): Step 3 of the Restore Wizard now presents archive contents as an expandable, collapsible hierarchical tree rather than a single flat list. Directories lazily render their child nodes when expanded, maintaining fast DOM performance even with backups containing thousands of files. When contents load, all files and folders are selected by default, inverting the checkbox semantics so that checking an item explicitly includes it and unchecking explicitly excludes it. Folder checkboxes provide tri-state indication (
checked,indeterminate,unchecked), searching automatically expands matching branches and highlights relevant items, and "Select all", "Deselect all", "Expand all", and "Collapse all" controls offer convenient batch operations. Ambiguous "Clear to restore everything" messaging has been removed, and selecting 0 files blocks execution with an informative warning to safeguard against accidental whole-archive restores. Closes #323. -
Restore wizard page 2 timeline, size, and confirmation UX improvements (#332): In Step 2 of the Restore Wizard, clarified backup sizes by displaying explicit "Restore:" and "Backup:" labels instead of the ambiguous "selected" suffix, updated item counts to use type-specific nouns (e.g. "containers", "VMs", "flash drives") when the selection is homogeneous, simplified daily group headers to display aggregate sizes directly, added intermediate date labels to the activity density graph, added an introductory overview paragraph explaining the version list, and upgraded restore point deletion to an expanding two-click "Delete?" confirmation badge with descriptive accessibility labels. Closes #332.
-
Restore wizard page 1 UI and item selection improvements (#327): Reorganized Step 1 of the Restore Wizard with alphabetical item sorting by default, quick search filtering across labels and job names, distinct filter chips and dedicated icon badge for Unraid Flash Drive backups, compact card layouts with uppercase type badges, and a sticky bottom floating action bar with active selection counts so operators can proceed immediately without scrolling long item lists. Closes #327.
-
Storage scan and import support encrypted manifests (#325): Scanning a storage destination now automatically decrypts deduplicated manifests using the local server key and identifies age-encrypted manifests. When age-encrypted manifests are detected, the Import modal displays an encryption notice with a passphrase field and unlock button, allowing users to decrypt metadata and import backups securely. Closes #325.
-
Stage classic folder backups beside the selected local destination (#366): Classic folder backups targeting local storage now stage temporary archives directly in a
.vault-stagedirectory on the target destination rather than defaulting to the first available cache pool. This eliminates out-of-space (ENOSPC) failures when backing up shares larger than cache pool free capacity. The staging folder is automatically excluded from recursive source walks. Closes #366. -
Dropped job-run-ID prefix from backup run folders (#319): Backup run directories in classic storage destinations no longer include the internal auto-incrementing job run ID in their folder names, standardizing on
<timestamp>_<type>(e.g.2026-09-15_143000_full). Historical restore points formatted with the previous<run_id>_<timestamp>_<type>convention remain fully discoverable and restorable without manual migration. Closes #319.
Security
- Harden restore paths across VM, container, folder, and plugin restores against path injection: Validates restore destinations and file paths against traversal sequences (
../,..\) at the HTTP boundary inRestorePointPreflightandRestore. In the restore engines (VM disk/NVRAM restores, container volume restores, directory extractions, folder restores, and plugin restores), replaces direct directory creation withmkdirRestoredenforcing symlink rejection and traversal validation, enforces atomic descriptor-relative directory traversal withopenNoFollowon...
v2026.09.00
Added
-
Backup and restore progress overlay now shows the active item being processed (#368): The "Backup in progress" and "Restore in progress" overlay on the Dashboard previously showed only the completed/total item count and elapsed duration. It now displays the name and type of the container, VM, folder, or plugin currently being processed (for example
Backing up: nextcloud (container)), updating in real time across backup phases, deferred uploads, and reconnect resyncs. Closes #368. -
Display real plugin names and filter hidden files (#329): Plugin discovery now extracts readable plugin names from
.plgDOCTYPE entity declarations and root XML attributes, rendering human-friendly display names across the item picker and job configuration while keeping internal identifiers intact. AppleDouble files (._*) and other hidden files are now automatically filtered out from discovered plugin items. Closes #329. -
Restore points now show the base full backup for chained restores (#318): Differential and incremental restore points in the restore timeline and wizard now display the date and size of the root full backup they depend on, and the
Chain ×Nbadge includes an explanatory tooltip describing chain length and replay behavior. Closes #318. -
Configurable log level (#346): Added a global log level setting (
debug,info,warn,error) configurable via the web Settings page and the settings API (log_level). Daemon logging routes through a centralizedsloghandler with dynamic level adjustments applied at runtime without restarting the daemon. Closes #346. -
Codecov configuration and dual-stack test coverage: Added repository-root
codecov.ymldefining dual-stack coverage flags (backendandfrontend), subsystem component dashboards, and an 80% coverage target on PR patches. Added@vitest/coverage-v8frontend coverage reporting with LCOV output, unified CI web test runs, and updated Go coverage targets with cross-package atomic instrumentation. -
Replication targets now show what the last sync actually did. A target that connected but had nothing new to copy previously just read "Synced", which looked the same as a sync that transferred data — so replication could appear to "connect but not do much". Each target now shows an explicit Up to date state when a sync found nothing new, a per-sync summary (jobs synced, new restore points, bytes transferred, and any failures), and the time replication last fully succeeded. These counters are stored with each run, so they are visible on page load and after a refresh, not only in the moment a sync finishes. Closes #287.
-
Run logs (#328): backups and restores now write a real streaming log — run start, per-item start/success/failure with sizes and durations, phase changes (staging, deferred upload, stop-all), stalls, and an end-of-run summary (items, duration, percent successful). In the Logs tab, any backup or restore entry now expands to show its run's log in a scrollable box that appends in real time while the run is live. Entries persist with the run and are cleaned up with run history, backed by a defensive 10,000-line-per-run cap alongside the existing activity-log cap. Storage health checks now record per-destination outcomes in the activity log. New API:
GET /api/v1/runs/{runId}/logs?after=<id>; new MCP tool:get_run_logs; new WebSocket event:run_log. Closes #328.
Security
- Cleared vulnerability advisory in build-time dependency. Bumped
golang.org/x/modtov0.40.0(andgolang.org/x/toolstov0.49.0) to clear CVE-2026-56864 insumdb. The package is a build-time dependency, is not reachable from Vault's own code, and there is no runtime behaviour change. Verification viamake security-check(gosec, govulncheck, andgo mod verify) is clean with 0 vulnerabilities and all modules verified. Closes #367. - Updated Go and web dependencies to their latest compatible releases and resolved a high-severity advisory in the web
nanoiddependency. gosec, govulncheck, and the Go linter pass clean, and the MCP integration remains on the go-sdk v1.7.0 API.
Changed
-
The Logs console now shows the newest entry first. Logs were listed oldest-first, so the entry you almost always want — the one that just happened — was at the very bottom, and reading it meant scrolling through the entire history first. The console is now inverted: the newest line is at the top, scrolling down reaches further back, "Jump to present" returns to the top, and the "End of logs" marker sits at the bottom where the oldest entry is.
-
Log lines are colour-coded by level and far less cluttered. Every row used to render in the same two tones, so an error and a routine info line looked alike, and each row spent a third of its width repeating the same date. Errors, warnings, info, debug and success now each have their own left-border colour, level pill and row tint — always paired with a text label, so the console stays readable in the monochrome themes and for anyone who cannot distinguish the hues. Rows now show only a time, with a sticky day separator naming the date and counting that day's entries; the full timestamp is still in the tooltip. Runs of identical consecutive lines fold into a single row with an
×Ncount, and thekey=valuemetadata tail is hidden behind a new Details toggle. Both Collapse and Details can be switched off to get every line back verbatim, and copying a folded row still yields every line it stands for. -
Contributor setup docs now include the local build, test, lint, and deploy workflow. The repo-root contributor guide now documents the required toolchain, bootstrap steps, local development loop, daemon build/run path, frontend dev flow, and the safe boundaries around
make deploy/make verifyso new contributors can get working without digging through the README or the codebase. This keeps the contributor guidance in the canonical repo-root location while leaving the README focused on project overview and installation. -
The
vault mcpcommand has been removed. It started a second process that wrote directly to the running daemon's database from outside it, so anything it changed bypassed the scheduler, the flash-storage flush, and the live UI updates — which is what made assistant-created jobs silently never run. AI assistants should connect to the daemon's/mcpendpoint instead, which exposes the same tools with all of those protections in place. -
A scheduled deep verify no longer piles onto a destination that a backup is still writing to. Deep verification streams and re-hashes every stored item, so running it against the same storage destination as an in-progress backup saturated the shared disk and network — the backup slowed to a crawl and looked frozen, with nothing in the logs to explain it. Vault now defers a scheduled deep verify while a backup is running on the same destination and records why in the activity log, letting the deep verify run at its next scheduled tick instead. Quick verification (existence and size checks only) is light enough to keep running alongside a backup, and an on-demand verify you trigger yourself is never deferred. Closes #290.
-
Anomaly alert summaries are now concise and name the job or destination they refer to. The duration, size, reliability, and capacity anomaly summaries previously opened with a generic "This backup" or "Storage …" and folded raw figures into the message itself. Each now leads with the affected job or destination name and states the change as a plain percentage (for example
"media-backup" duration increased by 233%.), while the exact per-day rate, byte counts, and growth factor stay in the anomaly's investigation details. The direction wording always matches the numbers — a high-side rule that fires on a slightly-smaller-than-usual run now reads "decreased by" instead of contradicting its own figures. Closes #315.
Fixed
-
Anomaly pills on Jobs and History now separate warning and critical counts with hover tooltips. Jobs and backup runs with open anomalies previously collapsed all issues into a single pill displaying the worst severity, masking when a job had both warnings and critical errors, and rendered with an ambiguous circle dot with no explanation of what the pill meant. The Jobs and History pages now render distinct pills for each active severity level (critical, warning, info) with a clear warning-triangle icon, wrapped in interactive tooltips explaining the anomaly breakdown and how to inspect or resolve them. Closes #330.
-
Folder backup jobs now display the full folder path. When configuring folder backup jobs or viewing configured folder targets, only the folder basename was shown (for example
appdatainstead of/mnt/user/appdata), which obscured vital path context and made it impossible to distinguish between folders sharing the same name. The full path was already stored in item settings and IDs but was not rendered by the interface. Folder items across the Item Picker, exclusions, Dashboard protection widget, Restore Wizard, and History views now show their full path with hover tooltips, while discovered presets retain their friendly names. Closes #335. -
Restoring a renamed or newly added item no longer fails when an older chain step predates the item. When restoring an item from a backup chain where older restore points were created prior to the item being introduced or renamed, staging failed outright with a
no such file or directoryerror because the item's subdirectory did not exist in those older chain steps. Vault now treats not-found errors (fs.ErrNotExist) during item directory listing as missing restore data for that step, logging a warning and gracefully skipping to replay subsequent chain steps that contain the item data. Normalisation of missing directory errors tofs.ErrNotExistwas also extended to SFTP a...
v2026.08.00
Added
-
Database containers can now be backed up as a proper database dump, not just its files. PostgreSQL, MySQL, and MariaDB containers get an optional "Also save a … dump" tick in the job editor. Vault exports the databases with the server running and stores the dump alongside the usual file backup — copying a database's files while it is writing can capture a torn state that will not load, whereas a dump is always consistent and can be reloaded into a different server version. The dump covers the whole server including users and permissions, so a restore does not come back missing the accounts the application signs in with. It uses the credentials already set on the container, so there is nothing to configure and no second copy of a password for you to look after. If the container exposes no usable root password, the dump is scoped to the database named in its environment instead, since an application user cannot read the whole server. A container that is stopped or stuck restarting is reported plainly rather than silently skipped.
Restoring puts the folders back and leaves the dump alongside them for you to reload by hand or into a different server version. It is deliberately not replayed over the restored folders: the dump is taken while the server is still running, the folders are copied after it stops, so the folders are the newer copy — and a reload would drop anything written while the dump was still being taken. Where the folders were read from a running server, and so may be mid-write, the dump is the trustworthy copy and is reloaded automatically; if that reload fails the restore fails with it, rather than handing back a database that looks restored and is not. Off unless you tick it. Closes #259.
-
Exclude the same paths everywhere, without editing every job. Settings → Backup Exclusions takes a list of paths that every folder and container backup skips. They are added to whatever a job already excludes rather than replacing it, so a job can still exclude more of its own. Useful for the paths that are never worth backing up anywhere — caches, sockets, scratch directories. Closes #257.
-
Containers can declare their own exclusions with a Docker label. Setting
vault.exclude=/config/cache,/tmpon a container tells Vault to skip those paths when backing it up. The exclusions travel with the container, so a template or compose file carries its own answer and adding the container to a job needs no further setup. The paths are matched exactly like ones typed into the job wizard, and the job editor shows which mounts a label is skipping. A label can exclude paths but not the whole container — a catch-all pattern is refused, since the label is written by whoever built the image rather than by the person who owns the backup. On by default; it can be turned off in Settings → Backup Exclusions. Closes #258.
Fixed
- A container backup now fails instead of quietly saving nothing when every one of its folders is excluded. With a broad exclusion rule — from the global list or a container's own label — it was possible for every backup-eligible folder to be skipped, leaving a backup that reported success but held only the container's configuration. It now stops with an error naming where to look, matching how virtual-machine backups already refuse to run when a VM has no disks they can handle. This applies to deduplicated destinations as well as ordinary ones: the check originally guarded only the ordinary path, so a dedup destination could still record a restore point holding no folder data at all — one that retention might later keep in place of a complete one. The check also runs before the container is stopped, so a backup that cannot produce anything no longer costs it any downtime.
- Large backups to a dedup destination no longer slow to a crawl and appear frozen. Every time a backup finished a 24 MiB pack, Vault listed the entire
_vault/index/directory on the destination to work out the next index-blob number. That directory gains one entry per pack, so the cost grew with every pack written: a 500 GB folder flushes roughly 21,000 packs and so issued 21,000 listings of a directory holding up to 21,000 entries — hundreds of millions of directory entries pulled across the network in a single run. Progress crawled, got steadily worse the longer a backup ran, and on a remote destination (WebDAV/SFTP/S3) eventually degenerated into connection and DNS timeouts that failed the run. Vault now reads that directory once per backup and counts on from there, so index bookkeeping costs the same on the last pack as on the first. Existing destinations are picked up as-is — no re-upload, repair, or reconfiguration is needed. Closes #256. - A slow or stalled browser can no longer hold up a running backup. Live progress events were handed to the WebSocket layer with a blocking send, so once the outbound queue filled — a browser tab left open that had stopped reading, or a flaky connection — the backup goroutine itself waited on it. Progress updates are now discarded rather than queued when a connection cannot keep up, since the next update carries the same information. Events the interface cannot recover by waiting — run completion, item failures, queue changes — are still delivered reliably, so a busy backup never leaves a finished job showing as still running.
- Backup runs no longer log a spurious "malformed settings JSON" warning. A job item saved without any per-item settings stored an empty value rather than an empty set, so every run that checked it reported the item as corrupt in the log. Nothing was actually wrong and backups were unaffected, but the warning pointed at a healthy item. Items now always store a valid value, and existing ones are read correctly without the warning. Genuinely corrupt settings are still reported.
- A VM that ignores the request to shut down no longer fails the backup. Cold VM backups ask the guest to shut down gracefully and wait for it. If the guest accepted the request but never acted on it — no ACPI handler installed, sitting at a login prompt, or an application blocking shutdown — the wait timed out and the backup gave up with "waiting for domain … to shut off: timed out with state running", even though Vault already had a forced-stop step ready for exactly this situation. It now falls through to that forced stop, so a stubborn guest is powered off and the backup proceeds. A backup you cancel yourself is still never turned into a forced power-off. The same fix applies when a restore has to stop an existing VM before replacing it. Reported in #255.
- "No space left on device" now explains itself on an Unraid user share. A backup writing to a path under
/mnt/usercould fail with a bare out-of-space error while the array showed terabytes free, which reads like a permissions fault or a Vault bug. It is neither: a user share spreads its contents across disks, but any single file is written to one of them, so a large VM disk image fails as soon as the disk it landed on is full — no matter what the array total says. The error now names the directory that filled and, for a user share, explains this and points at the settings that govern it (the share's Minimum Free Space and Split Level), or suggests targeting a specific disk or pool such as/mnt/cacheinstead. Reported in #255. - A dedup backup can no longer report success while leaving data that a later repair would discard. After writing a pack of deduplicated data, Vault recorded it in its local database before writing the matching record to the destination. If the second write failed, the backup still completed successfully, but the destination held data with no record of what was in it — so
vault dedup repair, the tool for rebuilding the local database from the destination, would quietly drop it and reveal the backup had been unrestorable all along. The destination record is now written first, so a failure fails the backup immediately and visibly instead of surfacing later as missing data. - A backup can no longer be built on top of data that space reclamation has already marked for deletion. When reclaiming space, Vault records its intent to remove a pack before deleting it, so an interrupted run can be finished later. If the deletion itself failed — a destination that is reachable but refusing deletes, for instance — the pack's contents stayed listed as available, so the next backup of the same files would reuse them instead of storing its own copy. That backup then depended on data already marked for removal, and repairing the index afterwards would drop it, leaving the backup unrestorable. Contents of a pack marked for deletion are now excluded from reuse the moment that mark is recorded, while the record itself is kept so the next reclamation retries the delete. The affected backup simply stores its own copy instead. Existing destinations pick this up automatically on upgrade.
- Repairing a dedup index can no longer lose chunks that a surviving pack still holds.
vault dedup repairrebuilds the index by replaying the bookkeeping records stored alongside your backups, and it applied deletion records as it went. Because a chunk is recorded once and space reclamation copies chunks into a replacement pack before retiring the old one, replaying the retired pack's record last left the chunk attributed to it — and applying the deletion then removed the chunk outright, even though the replacement pack still held the data. Restores after a repair could then fail with missing chunks. The rebuild now determines which packs were retired before it replays anything, so the result no longer depends on record ordering. This only affected repair and disaster recovery; normal backups and restores were never at risk. - The interface now recovers on its own after losing contact with the daemon. A browser that stopped keeping up — a background tab, a flaky link — was...
v2026.07.10
Fixed
- Cancelling a differential backup now takes effect immediately, even during the pre-scan of unchanged data. The
changed_sincepre-scan (pathChangedSince) walked a volume or VM disk tree to completion before honouring a cancel request, so cancelling a differential backup of a large, mostly-unchanged path could hang until the whole tree had been read. The scan now checks for cancellation between files — matching the rest of the backup engine — so container (classic and dedup/chunked) and VM differential backups stop promptly. As part of making cancellation prompt: a cancelled container backup now always restarts the container it stopped (the restart no longer rides on the cancelled request context, which previously could leave a running container stopped), and an operator Cancel is no longer occasionally misclassified as a stall and scheduled for an automatic retry. Closes #251. - Differential backups of containers that bind-mount the host root no longer scan the entire host filesystem before skipping it. On the classic (tar) path the
changed_sincepre-scan ran before the mount-exclusion check, so a differential backup of a container mounting/(Telegraf, Netdata, Glances, cAdvisor, node-exporter) walked all of/only to then discard it — the same runaway walk originally fixed in #70. The exclusion check now runs first, matching the dedup/chunked path. - Container backups to dedup/chunked destinations now stop the container first for a consistent snapshot. The chunked/dedup backup path read container volumes live without stopping the container, unlike the classic tar path which stops running containers (unless Don't stop is set). In-flight writes during the copy could produce an inconsistent or corrupt dedup backup. The chunked path now mirrors the classic behaviour: it honours the
no_stopsetting, stops a running container before reading its volumes, and guarantees the container is restarted afterwards — even if the backup fails partway through. - Differential backups to dedup/chunked destinations now skip unchanged data at the filesystem level. The chunked/dedup backup path did not honour the
changed_sincereference that the classic tar path already used, so an incremental run re-read and re-processed files (and whole container volumes) that had not changed since the last backup. Files and volumes older than the reference are now skipped in the chunked walk — matching the differential behaviour of the tar path — while directories are still recorded. A malformed or absentchanged_sincesafely falls back to a full backup. Closes #249. - The web UI no longer hangs forever on the loading spinner in Brave (and other privacy-hardened browsers). Brave's default Shields can make
localStorageandmatchMediathrow aSecurityError— most often when the console is embedded in the Unraid page as an iframe — which aborted theme setup during boot before the app ever became interactive, so the tab loaded endlessly ("Tab is not responding"). Every storage/media-query access during startup is now guarded and degrades to sensible in-memory defaults, and a boot watchdog guarantees the interface always becomes usable even if a startup step stalls. Closes #250.
Changed
- A connection banner now appears when the live link to the Vault daemon is lost. Instead of a silently frozen page, a banner explains that data may be out of date and offers a one-click reload, so a persistent connection problem is visible and recoverable rather than invisible.
- The live-update WebSocket now reconnects with exponential backoff and jitter (capped at 30s) instead of a fixed 3-second loop, so an unreachable daemon no longer churns the network and many clients reconnecting after a restart don't stampede in lockstep.
Security
-
Direct LAN access to the daemon now answers browser Private Network Access preflights. Chrome and Brave (130+/Edge 143+) block requests from a more-public origin to a private LAN/loopback address unless the daemon echoes
Access-Control-Allow-Private-Network: trueon the preflight. The daemon now returns this header alongside its existing CORS response, without changing the origin allow-list, so the Unraid-embedded and LAN-IP access paths keep working across browsers. -
Cleared three high-severity advisories in build-time dependencies.
brace-expansion(reachable through ESLint) is updated to a patched release in the web UI's dependency tree. The repository root also carried a second, unused npm project whose only declared dependency was theshadcnCLI — it was referenced nowhere, excluded from every build, yet still accumulated advisories (brace-expansion,js-yaml) that had to be patched. It has been removed rather than patched again. None of these packages ship in the daemon or the web UI, so no runtime behaviour changes.
SHA256 Checksum:
d6dbe55c6729ab4a47eb23ecac28f90f4b6688fdf6a2fcd421cb46d00dc7bda4 vault-2026.07.10.txz
v2026.07.09
Added
- Changing the database location now reports whether the database actually moved. Saving a new location previously confirmed only "Snapshot path updated", which said nothing about the data itself. Settings now reports the migration — for example "Database migrated to /mnt/garbage/vault.db — 5 files removed from /mnt/cache/.vault/vault.db" — and if the move could not be completed it says so as an error, naming the reason, so it is clear the database is still readable at its previous location.
Fixed
- The directory browser no longer opens empty when the field holds a file path. The Database Location field stores a file (
vault.db), which the browse API rejects, so opening the browser from it silently failed and showed a blank dialog with no way to navigate. The browser now falls back to the containing directory — and to the top-level listing if that also fails — so it always opens somewhere usable.
Changed
- Browsing for a Database Location or Temporary Work Area no longer applies the change on its own. Confirming a folder in the directory browser saved and applied it immediately, while typing the same path required clicking Apply — so the two ways of setting the field behaved differently, and picking a folder could move the database before you had a chance to review it. Browsing now only fills the field; Apply commits it in both cases.
- Changing the database location now moves the database instead of copying it. Pointing Vault at a custom location (or reverting to the default) previously wrote a fresh copy at the new path but left the old one behind, so a stale
vault.db— plus its rotated copies — stayed on the previous drive indefinitely. Because startup picks the freshest valid database across all known locations, that leftover was not merely untidy: if the new drive was ever slow to mount (a ZFS pool importing after the cache pool, for example), the old copy could be selected and silently undo the move. The previous location is now retired once the new one is confirmed written, so nothing is left to go stale. Only Vault's own files are removed — a custom location that is a share or pool root keeps everything else in it untouched, and the old copy is always left in place if the new one did not write successfully.
SHA256 Checksum:
00ac50008de8dfa18168e715d4b5f801ebd9b076a663acc98b3df63a3ebf4bbd vault-2026.07.09.txz
v2026.07.08
Fixed
-
The web UI no longer keeps running the old version after a plugin upgrade.
index.htmlwas served with no cache headers at all, so browsers applied their own heuristic caching and could keep loading the previous JavaScript bundle against the freshly-upgraded API until the user manually hard-refreshed.index.htmlis now explicitly never cached, while the content-hashed assets under/assets/are cached permanently (they can never change identity), so upgrades take effect on the next normal page load. -
Deleting a storage destination that jobs still use no longer leaves those jobs silently broken. Confirming the deletion previously removed the destination but left every dependent job enabled and pointing at an id that no longer existed — the Jobs page showed the destination as "Unknown" and the scheduler kept starting runs that could never write anywhere. Dependent jobs are now disabled and their destination cleared, and the response reports how many were affected so the UI can tell you what to re-point.
-
Creating or editing a job with a storage destination that doesn't exist now explains the problem instead of failing with a generic "internal server error" (HTTP 500). The invalid reference is reported as a normal validation error naming the offending id.
-
Job settings are validated before they are saved. Unrecognised values for backup type, compression, encryption, container/VM mode, verification mode and notification trigger — plus negative retention counts, out-of-range retry overrides and excessively long job names — were previously accepted and stored, only surfacing later as a confusing failure mid-backup. They are now rejected with a message naming the field and its allowed values. (Upload concurrency continues to be clamped rather than rejected, as before.)
-
"Test Connection" against an unreachable host now reports the failure instead of returning nothing. When a remote host silently dropped packets, the connectivity probe and the server's response deadline expired at the same instant, so the error could never be sent and the browser saw an empty reply after a 15-second wait. Probes now finish early enough for the error to be delivered — a blackholed replication target reports a clear timeout error in about 10 seconds.
-
Dashboard protection counts no longer double-count items that belong to more than one job. A VM or container covered by two jobs was counted once per job, inflating the totals; and when only one of those jobs had produced a restore point the same item was reported as both "protected" and "pending" at once. Items are now counted once, and an item backed up by any job counts as protected.
-
Size anomaly messages no longer contradict their own numbers. A run marginally smaller than usual could be described as "This backup grew to 2.7 GB, about <1× its usual 2.8 GB". The wording now follows the actual direction of the change.
-
Restore item-type filters are labelled properly — the ZFS filter read "zfss" and virtual machines read "vms"; they now read "ZFS Datasets" and "VMs".
-
Jobs, Restore, and History stay responsive with production-scale data. The pages now hydrate job items, anomaly baselines, and run history through bounded bulk queries instead of issuing hundreds of browser requests; bulk queries are cancellation-aware and indexed for deterministic newest-first history.
-
Replica mode no longer requests daemon-only discovery APIs or exposes daemon-only pages through direct links. Jobs, Restore, Recovery, and Recover Vault now redirect to Dashboard on a replica, matching the hidden navigation and read-only API contract.
-
Custom folder jobs can no longer bypass the server's allowed-root policy by typing or crafting a path. Create and update validate
settings.pathand legacy path fields against the same/mnt,/boot, and discovered-ZFS boundary used by the browser, while retaining overlap protection against backing up into the destination itself. -
Recovery readiness now counts configured items consistently when discovery is unavailable. Duplicate items are counted once, custom folders remain tracked, Flash Drive follows its own setting, and the page no longer shows a false empty state beside a non-zero protected count.
-
Historical anomaly messages with the old “grew …
<1×” contradiction now render correctly in both Anomalies and Activity Log without rewriting stored audit history. -
Very large VM backups to Hetzner Storage Box (WebDAV) no longer fail on the final metadata write (closes #242). Hetzner answers a WebDAV "create directory" request for a directory that already exists with
403instead of the standard405, which made the very last, tiny file of a multi-hour VM upload fail and discard the entire finished backup. Vault now recognises this reply (and any other non-standard status) by verifying that the directory actually exists, and treats it as success. Long VM copies also log a progress milestone every 5 minutes (percent, bytes, elapsed time), so a failure hours into a large backup is finally diagnosable from the diagnostics bundle. -
Folder backups of cache-only shares no longer produce empty archives. On Unraid a cache-only share appears under
/mnt/user/as a symlink to/mnt/cache/, and the backup walk did not follow it — it saw a symlink rather than a directory, never descended, and wrote an archive containing nothing. Both the classic tar and dedup paths now resolve the source symlink before walking, so the share's real contents are captured. The recorded restore location still uses the path you configured (e.g./mnt/user/myshare), not the internal cache target, so restores land where you expect. -
Configuration — including the Temporary Work Area path — no longer reverts to an old state after a server reboot (closes #241). Two root causes fixed: (1) on boot Vault now restores the freshest valid database copy by comparing modification times across all persistence locations — including the USB-direct database written when a boot ran before the cache pool was mounted, which the old fixed-order chain never even considered, so newer settings were silently overwritten by a stale cache snapshot; (2) the plugin now installs Unraid event hooks:
stopping_svcsstops the daemon gracefully on reboot/array-stop so the final database flush gets the chance to complete before the OS kills processes, andstartedstarts the daemon after the array is up if the boot-time start raced the pool mount.
SHA256 Checksum:
bb744a795f67bb2733f9c3c23ea9a772550b2a387b5f312a37ed238ed396504a vault-2026.07.08.txz
v2026.07.07
Security
- Storage adapters now contain symlinks to the destination root. A symlink planted under a local destination can no longer redirect reads, listings, or deletes outside the configured base path (resolved-to-resolved comparison, so destinations that legitimately live behind symlinks like
/mnt/userfuse paths keep working). SFTP destinations get the same server-side check (viarealpath) on the browse/download surface. S3 has no symlinks; SMB/NFS client libraries offer no resolution primitive and follow their server's own symlink policy.
Fixed
-
Very large VM backups (100 GB+) no longer fail or destabilise the daemon (closes #239). The VM engine's fixed 2-hour wall-clock limit is replaced by a progress-resetting 30-minute idle timeout, so a huge disk that is still copying can run for as long as it needs — only a genuinely stalled job is aborted, and Vault now attempts (with retries and verification) to abort the in-flight libvirt job on stall or cancellation so the hypervisor stops writing into the staging area. Full VM backups check staging free space up front and fail fast with an actionable message instead of dying mid-copy, and background worker panics (uploads, downloads, health checks, async cleanups) are contained as failed runs instead of crashing the whole daemon.
-
Restoring an incremental/differential folder backup no longer resurrects deleted or excluded files (closes #231). Folder backups now record an authoritative point-in-time file listing alongside each restore point; after replaying the chain, the restore prunes files that earlier chain steps wrote but that no longer existed (or were excluded) at the selected point. Dedup folder restore points restore directly from their complete manifest — no chain replay at all, which also makes them faster. The restore wizard's file picker reflects the same rules, so it only offers files a restore will actually produce. Backups made before this version have no listing and keep the old overlay behavior.
-
Running and queued backups can now actually be cancelled from the UI (closes #235, closes #238). The documented Cancel flow never had a button: the Jobs page now shows a Cancel control on the running/queued job's row, and the Dashboard's "Backup in progress" tile has a Cancel button with live "Cancelling…" state. Cancelling a job that is queued behind another run (e.g. Run Now on a scheduled job while a backup is active) removes it from the queue before it starts — previously such runs could not be stopped at all. Deleting a running job now cancels its run first instead of leaving the backup goroutine running against deleted records.
Added
-
Adaptive backups — jobs can wait for their workloads to go idle (closes #240). A new per-job "Adaptive Backup" toggle checks each container's CPU/network activity (Docker stats), each VM's CPU activity (libvirt), and each folder's recent writes before a scheduled run starts. Busy workloads (Plex mid-stream, a download still writing) postpone the backup with a clear reason in History (new
postponedstatus), re-checking every few minutes until idle — or until a configurable max-postpone window elapses, when the backup runs anyway so busy systems still get protected. Manual Run Now always runs immediately. Idle thresholds and windows are configurable in the global settings; probe failures never block a backup (fail-open). -
Local destinations can now be throttled too (#237 follow-up). The per-destination bandwidth cap — previously hidden for local storage — now appears as a "Disk write speed limit" on local destinations, capping how fast backups write to disk so apps streaming from the same array (Plex, Jellyfin) keep their I/O headroom while a backup runs. The cap is write-only: restores and verification always read at full speed. Note the unit is megabits (8 Mbps = 1 MB/s). The cap applies to the copy-to-destination phase; keep staging on a different disk (cache, the default) for full effect.
-
Adaptive upload throttling — Vault now yields bandwidth to Plex, Jellyfin and friends (closes #237). An opt-in "Upload Throttling" section in Settings lets you enter your link's upload capacity; Vault then continuously measures non-Vault traffic on the network interface and slows its own uploads to network destinations so streaming keeps its bandwidth, speeding back up when the link goes quiet. A configurable minimum rate keeps backups progressing, per-destination bandwidth caps still apply (stricter limit wins), and changes take effect within seconds without a restart.
-
Browse the files on a storage destination like an FTP client (closes #236). Each destination card on the Storage page now has a Browse button that opens a read-only file browser — drill into folders with breadcrumb navigation and see file sizes and modification times for whatever Vault sees on the destination (local, SFTP, SMB, NFS, WebDAV, S3). Available in replica mode too since it never writes anything.
Changed
- The Replication and Storage Logging toggles in Settings now apply immediately — flipping the switch saves right away (rolling back with an error toast if the save fails), so the separate "Save Replication Settings" and "Save" buttons are gone.
- The Full Form "sensible defaults" note in the job editor is now a prominent info callout instead of small dim text, so it's clear why Advanced Options are not shown in Full Form mode (closes #232).
- Documentation is now linked directly from the sidebar — a new "Documentation" entry in the System section opens the Vault manual in a new tab (closes #233).
- Backup type docs and job editor now explain first-run behavior: selecting Incremental or Differential on a new job automatically performs a Full backup on the first run, so no manual initial Full is needed. Clarified in the manual, the API reference, the
backup_type_chainfield description, and the job editor's helper text (closes #234).
SHA256 Checksum:
d27263796d229ae51b8a9b2dd775b63e0b301b1cab1b1f5494baabe08d94bf34 vault-2026.07.07.txz