Extends TrueNAS SCALE's TrueCloud Backup to:
- back up to Backblaze B2 and any S3-compatible provider, not just Storj;
- snapshot datasets that have child datasets — which is every box running Apps.
Requires TrueNAS SCALE 24.10 or newer. TrueCloud Backup does not exist before
that, and install.sh will refuse.
Storj raised the price of their TrueNAS-integrated tier from $5/month to $50/month in 2026. TrueCloud Backup is the only native TrueNAS feature that gives you pre-backup ZFS snapshots, restic dedup, scheduled tasks with UI progress, and dataset-lock integration. Running restic by hand loses all of it. This gets the feature back with storage you already pay for.
Clone it onto a pool (not the boot device — that is wiped on TrueNAS upgrades),
then run install.sh as root:
git clone https://github.com/sudolulo/truenas-truecloud-patch.git \
/mnt/tank/truenas-truecloud-patch # replace `tank` with your pool
cd /mnt/tank/truenas-truecloud-patch
sudo bash install.shThat registers a PREINIT boot hook, patches middleware in a volatile overlay, and restarts middlewared. It survives TrueNAS updates — the patch is re-applied at every boot, never written to the system dataset.
Nested-dataset snapshots are opt-in:
sudo bash install.sh --enable-nested-snapshotsThen create a TrueCloud Backup task in the UI with a B2 or S3 credential, or from the CLI.
Check it worked:
sudo python3 patch/create_task.py verifyIf something is wrong, the reason is in apply.log — start at
Recovery.
| TrueNAS | B2/S3 providers | Nested snapshots | Hardware-verified |
|---|---|---|---|
| 24.10.2.4 | ok | ok | — |
| 25.04.2.6 | ok | ok | — |
| 25.10.4 | ok | ok | v0.7.0: 3 live tasks — 191-dataset nested backup of /mnt/Tap, a 215-filesystem/2-zvol backup of /mnt/Tank/backups, and a non-nested one; 0 orphans, 0 leaked mounts, byte-identical restore; the collector also reclaimed a real orphan the pool had been carrying |
| 24.10.2.5 (unreleased) | ok | ok | — |
| 25.10.5 (unreleased) | ok | ok | — |
| 26.0.0-BETA.3 (unreleased) | ok | ok | — |
| master (27-dev) | BROKEN | BROKEN | — |
| verdict | meaning |
|---|---|
| ok | Every assumption the patch makes about middleware still holds. |
| BROKEN | middleware changed underneath the patch. apply.sh refuses to apply that module on this version and leaves TrueNAS stock, so backups keep working — without the module's feature. |
| native | TrueNAS does this itself now. The module retires; it is not a failure. |
"ok" means the patch's assumptions hold, checked automatically against iX's source. It does not mean a human ran a backup on it — that is the Hardware-verified column, which is filled in by hand and only by doing it.
master is not the next release. iX branches each major off to its own
release/ line and master rolls straight on to the one after — so master is
27-dev while 26 is still in beta. A BROKEN master means iX has changed
something that will reach users a major release from now, not in the version you
are about to install. Read the numbered rows for that.
A row like 25.10.5 _(unreleased)_ is the next maintenance release: branched by iX,
not tagged yet, and the very next thing a 25.10.4 box gets. It is checked precisely
because it is the one unshipped ref that reaches real users without warning.
The table is regenerated daily by CI against iXsystems' actual middleware source — it is not a claim somebody typed once and forgot.
It is also static analysis: it proves the patch's assumptions still hold, which is a weaker claim than "a backup ran and a restore came back". For what has actually been run — which tasks, on which hardware, and the md5 of the file that came back — see docs/verification.md.
TrueNAS 26 is supported as of v0.7.0, and was verified on a real 26.0.0-BETA.1 install: a 274-snapshot recursive backup of a 292-dataset pool, and a byte-identical restore of a four-level-deep child dataset. The Hardware-verified column tracks the newest beta iX has tagged (currently BETA.3), so it does not carry that mark — a build nobody has actually run a backup on does not get credit for one.
26 rewrites cloud_backup from async to synchronous and deletes the private ZFS
methods this module used to call, so getting there took real work: the patch now
injects the wrapper flavour that matches the installed middleware, reads dataset and
snapshot lists from ZFS rather than middleware (whose queries hide TrueNAS's own
datasets — 84 of 270 on a real pool, including live app data), and owns the snapshot
sweep even when it stages nothing (26 decides recursive by a rule this patch does not
share, and would otherwise orphan one snapshot per zvol on every run).
And if a future TrueNAS breaks it, you get a missing feature, not a broken backup.
apply.sh re-checks the patch's assumptions at every boot and refuses to apply a
module whose assumptions no longer hold — TrueNAS is left stock, and the reason is
named in apply.log. Details: How it works.
cd /mnt/tank/truenas-truecloud-patch
sudo bash update.sh # newest release
sudo bash update.sh --check # what would change?
sudo bash update.sh --rollback # back to the previous versionupdate.sh refuses to run on a dirty checkout, pins you to a release tag, and keeps
the previous revision so a rollback is one command. There is no auto-update: this
patches system internals as root, and a bad commit reaching your box unattended would
detonate on the next reboot — v0.0.4 shipped exactly such a bug and took 54 apps
down. The manual step is the safety gate.
When a newer release exists, the patch raises a TrueNAS alert (the bell in the UI) telling you so. It's on by default and checks once a day.
bash install.sh --no-update-alerts # turn it off
bash install.sh --update-alerts # turn it back onIt will not nag you about a README. A release whose CHANGELOG contains only a
### Docs section changed no code, and raises nothing. Anything that touched the
system raises an INFO alert; a release with a ### Security section raises a
WARNING. The CHANGELOG's own section headings are the signal, and a security fix
anywhere in the range escalates the whole span — a docs-only release on top of a
security fix still reports as security.
Release candidates never alert. They are invisible to update.sh and to the
alert, which both take the newest plain vX.Y.Z tag. That is what lets debugging
happen in -rc tags instead of in your notification bell — see
Releasing.
The changelog is read from whichever forge origin points at, derived from the
remote rather than hard-coded. That is not cosmetic: when the changelog cannot be
read, the alert deliberately fires anyway rather than risk hiding a security
fix — so a wrong URL would not silence the alert, it would make it fire on every
release, including the documentation-only ones this section promises to suppress.
TrueNAS cannot raise an alert from the CLI — midclt exposes only
alert.dismiss, alert.list, alert.list_categories, alert.list_policies and
alert.restore. Alert creation is internal to middlewared, and none of its ~60
one-shot alert classes is generic enough to reuse. So the only way to get a real
alert is to register an AlertSource, which is what patch/alert_source.py does.
That is also the least invasive thing this patch does:
| providers module | modifies stock files (appends code to b2.py, restic.py) |
| nested module | modifies stock files (3 middleware modules) |
| update alert | adds one file. Modifies nothing. |
It's the native mechanism — the same one every built-in TrueNAS alert uses — and TrueNAS polls it itself, so there is no cron job and no systemd timer.
- Fail-safe. Every error path returns
None. It cannot take middlewared down. - Read-only.
git ls-remoteplus an HTTPS fetch of the CHANGELOG. It never writes to.git, so it cannot leave root-owned objects behind the way agit fetchfrom middlewared (which runs as root) would. - Removed by
uninstall.sh.
It only tells you. It never updates anything — see Updating.
bash /mnt/tank/truenas-truecloud-patch/uninstall.shReplace the path with your clone location. Removes the PREINIT hook, unmounts the overlay (restoring the original backend files immediately), and restores the original UI bundle from backup.
| Nested-dataset snapshots | Why stock refuses, what this does instead, and how to verify your backups actually contain the data |
| How it works | What is patched, how it survives updates, the boot sequence, and what happens when TrueNAS goes native |
| Recovery | middlewared won't start, blank web UI, verify shows FAIL |
| CLI | Creating tasks with create_task.py |
| Development | Tests, CI, and the release process |
| Path | What it is |
|---|---|
install.sh |
Register the boot hook, patch, restart middlewared. Also --enable/--disable-nested-snapshots. |
update.sh |
Fetch and apply a newer release. --check, --rollback, --to, --main. |
uninstall.sh |
Remove everything. |
recover.sh |
Emergency: kill switch + restart against stock files. |
patch/apply.sh |
The PREINIT script. Runs at every boot. |
patch/truecloud_nested.py |
Nested-dataset staging: plan, mount, verify, tear down, sweep snapshots. |
patch/create_task.py |
Create tasks with S3/B2 credentials; verify the patch state. |
tools/compat.py |
What the patch assumes about middlewared — and the checker. Run daily by CI and at every boot. |
release.sh |
Cut a release. Two stages, and the second is refused without the first. |
- This is unofficial and not affiliated with iXsystems.
- It patches internal middleware APIs with no stability contract. Every patch is fail-safe: if it cannot apply, middlewared starts normally and the reason is logged.
- Test your restores. True of any backup; more so here. See Verifying it works.
- Filing a TrueNAS bug? Remove the patch first and reproduce on a stock system.
- Provided as-is, no warranty. See LICENSE.
Parts of this project were written with AI assistance (Claude); all of it is reviewed and tested before release. Bugs are mine.