Changelog
Only released versions appear here. What is still being built is on the Roadmap.
This page lists the releases published when the site was last built; the latest release is always on GitHub ↗.
0.4.7 beta · 29 October 2026
Restow 0.4.7 makes Microsoft 365 backups steadier. Mailboxes of disabled or unlicensed accounts and OneDrives and sites that Microsoft locked are marked "Not backed up" with the reason instead of failing every night, and every object Restow stops backing up waits amber for an admin's confirmation. Backups that Microsoft Graph or the storage held back no longer count as failed, a permission granted to the app takes effect without a new client secret, and one queued backup can no longer block the backup queue of every tenant. Reminders go out before the client secret or certificate of an app registration expires, and many errors of mailbox, OneDrive, SharePoint and IMAP backups and restores now cost an area or an item instead of the whole run. Proxmox VE VMs and containers can be excluded from backup on purpose, and the inventory shows what a guest's first and follow-up backups store as text (#58). The recommended job can switch the mail archive on, and the owner of the installation can enter monthly prices that each tenant's overview turns into an estimate for the provider. Four database migrations (Upgrade Notes).
Breaking changes
status: "orphaned" now also covers objects that are still at the source but are no longer
backed up (account disabled, no licence, or locked by Microsoft): before, it meant removed from
the source. This applies in GET /api/v1/objects, GET /api/v1/users, the object.status of
GET /api/v1/jobs and objects.orphaned of GET /api/v1/status. The new statusReason
(account_disabled, no_license, locked, null for an object removed from the source) of
GET /api/v1/objects, of the object of GET /api/v1/jobs and of the objects of
GET /api/v1/users tells them apart. An integration that treats orphaned as a deleted
account should check statusReason before it acts.
Nothing else changes in a breaking way. The integration API contract moves to 1.7.0, additively.
In GET /api/v1/users the objects also carry inactiveSince and statusConfirmedAt, and a
user's recoveryReadiness reads at least yellow (never green) while one of its objects waits
for a confirmation (yellow instead of null when such an object has no backup to rate). The
readiness summary of GET /api/v1/status and GET /api/v1/verify/latest reports toConfirm;
GET /api/v1/status leaves excluded Proxmox VE guests out of guests.total and
guests.withoutJob and reports them as guests.excluded. POST /api/v1/jobs/backup can skip
an object with the reason inactive, the job.completed and job.failed webhooks of a
completed restore carry data.job.result, and alerts (report.alert) can name the new events
object.inactive, object.inactive_many, source.app_secret_expiring and
installation.app_secret_expiring; clients that handle skip reasons or event names exhaustively
should accept the new values. Some values follow new rules: backups held back by a service and
objects no longer backed up raise no job.failed, objects removed from their source are rated
without the age of their last backup, and objects.failed of GET /api/v1/status leaves out
objects whose latest backup was held back by a service or ended neutral for an object no longer
backed up; the run before it counts instead (Changed, Fixed, Upgrade Notes).
Added
- Microsoft 365: objects that are no longer backed up because the source says they are gone,
disabled, unlicensed or locked first read amber "Not backed up any more, please confirm" with
reason and date (docs/MICROSOFT.md "Nicht mehr gesichert: Bestätigung"). No failure, no red,
not overdue, but they keep the warnings, the dashboard and the tenant's readiness at least at
yellow (never green), are counted in the integration API's
readiness.toConfirm, and providers seeinactive_to_confirmamong a tenant's alerts, until an admin confirms "Yes, intended": per object, or for several in the list by selection or by the list's filter (at most 1000). Through the API (POST /api/v1/directory/objects/:id/inactive/confirm,POST /api/v1/directory/objects/inactive/confirm) a confirmation can carry an optional note (up to 1000 characters) for the audit log; the interface does not ask for one. Tenant administrators and provider teams with configure rights may confirm; each confirmation is audited (directory.inactive.confirmed,directory.inactive.bulk_confirmed). Confirmed objects read grey "Intentionally no longer backed up", with who confirmed it and when. When the object becomes active again, the confirmation is dropped and backups resume; if it stops again later, Restow asks again. An excluded object that disappears from the source was never backed up and counts as confirmed. The list of protected objects filters byto_confirmandconfirmed. Migration 0069 stores the state. - Notification events
object.inactive(a notice, once per object and transition, in the bell by default) andobject.inactive_many(one warning per tenant and day instead of the notices, when more than 10 objects, or more than 20% of the tenant's objects backed up before and at least two, stop being backed up within a day; part of the "Errors and warnings" choice). Both link to the protected objects waiting for a confirmation. Neither has an event of its own in the webhook stream (job.*); a webhook gets them asreport.alertonce they are ticked in the matrix. - Microsoft 365: reminders before the client secret or certificate of the central app
registration expires (docs/ENTRA-SETUP.md, Teil 3, "Ablauf und Erinnerungen"): 30 days
before, again 7 days before and once it has expired, each stage once per expiry date. A new
date or another registration starts over; a date first seen late raises only the stage it is
in, not the earlier ones. The api checks every 15 minutes (the first time two minutes after it
starts; not in the public demo) and raises the new installation event
installation.app_secret_expiring(a warning, an error once expired) for the provider administrators: in the bell, and in the mails and webhooks of every rule that lists it. The date is the one entered under Installation › Microsoft multi-tenant app,ENTRA_CLIENT_SECRET_EXPIRES_ATfor a secret from the environment, or the certificate's own end (notAfter). Restow does not read it from Microsoft Graph, which would needApplication.Read.All. The last reminder is kept insettings.app_secret_alerts(migration 0068) and claimed under a row lock, so neither a restart nor a second api replica repeats it. - Microsoft 365: the same reminders for a source connected through its own app registration
(docs/ENTRA-SETUP.md "Alternative: eine eigene App pro Quelle"), as the new event
source.app_secret_expiringamong the job events, so tenant administrators can choose it as well. The worker checks every Microsoft 365 source of an active tenant that is not disabled (also one in error) every five minutes; a certificate counts by its own end, a client secret by the date noted for it. "Own Graph app" takes the secret's expiry date ("Secret expires on", optional, as Entra shows it under Certificates & secrets), and the source's connection card shows "Expires" with the expiry badge and, within 30 days or once expired, what to do. For a secret the date can be noted, changed or removed there without entering the secret again ("Note expiry date", "Change expiry date";PATCH /api/v1/sources/:id/own-app/expiry, audited assource.own_app.expiry_updated, for tenant administrators and provider roles that may configure). A new secret, a new certificate or a changed date starts the reminders over; saving the same date again does not. - Both expiry events name their stage in the bell ("expires in 5 days", "Renew it now", "has
expired") and lead to the source's page or to Installation › Microsoft multi-tenant app. The
alert mail lists the expiry date, the days left, the kind of credential, the application
(client) ID and how to renew it without a gap (create the new secret, enter it with its new
date, only then delete the old one in Entra); chat webhooks of
source.app_secret_expiringlink to the source's page. Both events are part of the "Errors and warnings" choice and of the bell's default, and each stage passes a rule's throttle on its own. - Restores that did not restore anything: a completed restore with failed items and none
restored reads "Nothing restored" (red) and raises
restore.failedand thejob.failedwebhook, with "Nothing was restored: N items failed." as its error; one that skipped every item reads "Everything skipped" (amber) and staysrestore.completed, saying so instead of claiming it restored something (info, not success, in chat channels). Thejob.completedandjob.failedwebhooks of a completed restore carry its counters indata.job.result(restored,skipped,failures,unverified). - Proxmox VE: an admin can exclude a VM or container from backup on purpose (docs/PVE.md
"Excluding a guest"), for test VMs or guests backed up elsewhere: "Exclude from backup" on
the guest's page, or for a selection of a node's guests in the inventory; "Include again" on
the page and in the guest's inventory row takes it back. Tenant administrators and the
administrators of the provider team may do so. No job backs an excluded guest up, not even
the job for all guests, and the recommended job leaves it out; its own job stays recorded,
so including it again restores what it had. Excluding withdraws its queued backups (one
already handed to the node gets a cancel request, a running one ends as it would); a backup
queued in the same moment is withdrawn at the node's next heartbeat, before the node gets it,
and shows in History as a cancelled run with "The guest was excluded from backup before it
started." "Back up now" and putting the guest into a job are refused (409
urn:restow:problem:pve-guest-excluded) until it is included again; taking it out of its job stays possible. Its restore points stay restorable, retention treats them as before and the weekly restore check of its newest restore point goes on; the job's monthly restore test and boot tests no longer cover it. New routePUT /api/v1/pve/guests/:id/excluded({ "excluded": true }orfalse), audited aspve.guest.excludedwith the number of queued backups withdrawn before reaching the node (one already handed to the node is not counted); excluding a guest twice changes nothing.GET /api/v1/pvereportsexcludedAtandexcludedByper guest (migration 0066). - Proxmox VE: excluded guests count nowhere as unprotected: not in the overviews, the recovery
readiness, the warnings,
backup.overdue, the statistics, the job counts of the all-tenants view or the provider view, and they never keep a tenant from green. The guest counts report them asexcluded; their restore points stay in the count of restore points and their earlier runs in the history. In VMs & containers an excluded guest reads "Excluded" (grey, never a problem, whatever its earlier runs said), matches only the new state filter "Excluded" and sorts after every problem. The inventory shows "Excluded: N" per node, the guest's detail panel says "Excluded from backup" under its job, which cannot be changed there, and the job dialog no longer offers excluded guests. - Recommended job with the mail archive (docs/ARCHIVE.md "Archiv im Mail-Job"): while the
installation offers capture from the mailbox (
archive.mailboxCapture) and the tenant has mailboxes or accounts an archive can capture, "Create recommended job" and "Protect everything without a job" make the new mail job over all objects back up and archive. A mail job over all objects that exists and does not archive yet is switched to archiving instead ("Switch archiving on in …"); its schedules stay, and the change is audited asbackup_job.updatedwithchanges.archive, like an edit in the job editor. The click uses the default capture ways, which need nothing set up in the tenant: Microsoft Graph for Microsoft 365 mailboxes, IMAP sync for the other accounts. Journaling and forwarding stay a choice in the editor. Without the capture the recommended job is made as before. - The recommended job never changes a mail job over selected objects; its preview names them ("Archiving stays as it is in …"). A mail job over all objects whose capture ways were chosen by hand (Microsoft Graph or the IMAP sync switched off, for example journaling only) is left as it is, and the preview says so. A new job over all objects is made only when it covers something that is in no job yet. The dialog says when the job over all objects archives already, the confirmation names the jobs in which archiving was switched on apart from the jobs set up, and the preview of a paused job names no schedule.
- The list of mail jobs shows "Switch archiving on" while the installation offers the capture and the recommendation would switch an archive on in the mail job over all objects (not for tenants with only OneDrives, SharePoint sites or groups, nor for jobs whose capture ways were chosen by hand).
- Installation › Prices: a monthly price per mailbox and per GB or TB of stored backups, in
EUR, CHF, USD or GBP (docs/ARCHITECTURE.md "Mandantenmodell"). Mailboxes are counted as in
the mailbox usage (a OneDrive does not count twice); storage is the tenant's stored backups,
deduplicated and compressed, in decimal units (1 GB = 10^9 bytes, 1 TB = 10^12 bytes). Prices
take at most four decimals; a price left empty is not shown. Provider admins with every
tenant read them, only the owner changes them, audited as
settings.pricing.updatedwith the values before and after (GETandPUT /api/v1/settings/pricing). Tenant users never see them. - Each tenant's overview shows provider admins with every tenant a card "Price per month": what the tenant comes to at these prices, one line for the mailboxes and one for the storage, with a link to the prices. It appears once a price is set. Restow bills nothing and limits nothing.
Changed
- Backups that Microsoft Graph or the storage held back no longer count as failed. A Mail &
SaaS backup that ends, after all its retries, with
graph.throttled,graph.service_unavailableorstorage.rate_limitedreads "Held back by the service, retried" in amber instead of "Failed" in red (in the objects list "Held back by the service") in every view: History, the all-tenants job overview, the objects list, the runs of a job, the job page, the recent jobs on the dashboard and the run details. It is left out of the failed counts of the statistics and the dashboard, does not make the job failing (the run before it stands), does not make a warning red or end its acknowledgement, and raises nobackup.failedalert and nojob.failedwebhook. The next scheduled run tries again; an object held back for longer is caught by the overdue and readiness rules, which look at the age of the newest backup. A restore, export, import or check that a service held back has no next run and stays a failure, withrestore.failedand the like. - Microsoft Graph: a background job honours a Retry-After of up to 5 minutes (before, waits were capped at about a minute, and Exchange refuses a retry before its mailbox concurrency penalty is over); a cancelled or expired job still stops at once. Requests from the interface keep the one-minute cap.
- Overdue backups (
backup.overdue): a mailbox member with a schedule of its own is judged by that schedule or by the tenant's mail jobs (the most relaxed one), whichever allows longer, for that member only; a weekly member of a daily job is no longer overdue two days after its backup. An object whose backup has started and is still running (up to 21 days, a large first backup) is not overdue; a backup that only waits in the queue does not hold the alert back. - The expiry badge of the central app (Installation › Microsoft multi-tenant app) turns amber
30 days before the end instead of 60 and red from 7 days, counted in whole calendar days
(UTC); a source's own app shows the same badge. For a secret from the environment without
ENTRA_CLIENT_SECRET_EXPIRES_ATthe page says how to set it. The setup texts and docs/MICROSOFT.md say 30 days, 7 days and expired instead of 60 days. - The job page names the phases of a SharePoint site backup ("Reading the site", "Lists") and of the discovery of sites and group mailboxes. The retention notes read "the newest restore point of each object is always kept" in the singular.
- Proxmox VE inventory (#58): the size of a guest's first backup ("First backup on : stored") and what a follow-up backup adds on average ("Average per follow-up backup: + (last N backups)") are shown as text in the guest's row, not only in its tooltip, so they can be read and copied.
- Dependencies: better-auth 1.7.14, mailparser 3.14.1, nodemailer 10.3.1, smtp-server 3.21.3, msgreader 1.29.0, Biome 2.6.4, Vitest 5.0.5, Vite 8.3.5, React 19.3.1 and, in the release pipelines, docker/login-action 4.7.0 (Dependabot #82 to #85). pg-boss 10.4.2 carries a patch of Restow's (Fixed, Upgrade Notes). zod 4 (Dependabot #86) is not part of this release.
Fixed
- Microsoft 365: a mailbox whose account is disabled or has no Exchange Online licence
(mailbox not provisioned, licence removed, inactive mailbox) no longer fails its backup until
the next directory sync ("Microsoft rejected the request"). The backup confirms with
Microsoft that the user exists but has no mailbox, marks the mailbox "Not backed up: account
disabled" or "Not backed up: no licence" and ends neutral (
graph.mailbox_inactive): no failed count, no red readiness because of age, not overdue, nobackup.failedalert orjob.failedwebhook, existing backups kept. The directory sync sets the same state from its mailbox probe. Only Exchange's answer counts: a disabled account whose mailbox Microsoft still serves (a shared mailbox, a former employee) is backed up as before. A 401 or 403 is never read this way; those stay permission failures. Backups resume right after the directory sync that finds the account enabled or licensed again, and the mailbox is not reported overdue because of its old backup. A restore into the original mailbox is refused with the reason (docs/MICROSOFT.md "Deaktivierte und unlizenzierte Konten"). - Microsoft 365: a OneDrive or SharePoint site that Microsoft locked (HTTP 423
resourceLocked, "Access to this site has been blocked"; Microsoft does this to the OneDrive of an unlicensed account) no longer fails every night as "Microsoft rejected the request". The backup confirms the lock with one cheap read, marks the object "Not backed up: locked" (graph.object_locked, neutral, no retries) and the scheduler stops planning it. The directory sync's drive probe recognises the same lock and marks a known OneDrive "Not backed up: locked" at once, before a backup meets it. A 423 that this read does not confirm stays a counted failure with itsbackup.failedalert. A site whose libraries all answer 423 ends without a restore point instead of carrying old copies forward. The directory sync never turns a locked object active again just because it is still listed: it reads each locked drive or site once more and resumes the backup only when that read succeeds; otherwise it keeps the lock and reports that the check failed. A 423 on a single file stays an item problem (docs/MICROSOFT.md "OneDrive: Sichern und Wiederherstellen"). - Job queue: one queued backup of an object whose backup was still running (the next scheduled run, "Back up now") blocked the backup queue of every tenant until that backup ended, because pg-boss kept offering it first and handed out nothing. Such a job now waits for its object's running backup and lets every other backup pass (pg-boss 10.4.2 with patches/pg-boss@10.4.2.patch).
- Microsoft 365: a permission an administrator grants to the app after Restow fetched its
token now takes effect without a new client secret. A token carries the app's permissions as
they were when it was issued, and Restow kept it until shortly before it expired (up to about
an hour), so a permission granted in between was refused until then or until a new secret
was entered. When Graph answers 401 or 403, Restow now fetches a new token once and sends the
request again, at most once a minute per tenant or source, so a run of refusals (an item the
app may not read) does not fetch a token each time. Every request refused with the same old
token is sent again with the new one, not only the first, and refused sub-requests of a
$batchare sent again once as well; a wall clock that stepped back (NTP, VM resume) no longer holds back the refresh. A request refused again with the new token fails as before. This applies to backups, restores, the directory sync, the archive capture, the group search, the connection test and the consent check. A token endpoint that cannot be reached is asked twice more (after 1 and 4 seconds, 60 seconds per request) and then fails the run, which resumes from its checkpoint, instead of recording every item as failed; the test of an app registration still reports it at once. - Microsoft 365: every
$batchnow sends an identical read only once and gives the answer to every caller, also in the retries after 429 and a new token, on top of the unique request ids of 0.3.2, so a delta page or a retry that names the same message twice can never repeat an id ("Request Id ... has to be unique in a batch"). Writes are never merged. - Microsoft Graph: a connection broken before Graph answered (reset, refused, a temporary DNS
failure, a timeout) is retried with backoff for requests that only read,
$batchof reads included, and is not reported as throttling. - Mailbox backup: a 403 for calendars or contacts (permission missing, an access policy that covers mail only) fails that area and keeps the mail of the run, instead of discarding the snapshot. A 401 in the middle of a run checkpoints and resumes, instead of abandoning a partial first backup. A well-known folder Graph refuses to resolve (400, 403) is treated as absent and logged, instead of failing the mailbox before a single folder was read. A stored delta link Graph rejects as an invalid sync state (400) leads to a full resync, instead of failing on every run. A message Exchange cannot convert to MIME (conversion error, or a plain 500 after one more try) is stored as JSON with its attachments, like an oversized one. A subfolder deleted while its parent was listed no longer fails the run; it is recorded as a failure of the parent, and what the snapshot holds below the parent stays until the parent is gone.
- SharePoint: a library or list Graph will not give (400, 501, or a lasting 500 for a list template or a library's permissions) is reported as an item failure and keeps its last copy; the rest of the site gets its snapshot, instead of the whole site failing; a 500 or 502 for a library's contents still fails the job, which resumes from its checkpoint. A resumed site counts the item failures of the libraries an earlier attempt finished. A library that is gone no longer reads "this user has no OneDrive".
- OneDrive and SharePoint: an item that failed and came through later in the same run (listed
twice in a delta feed, retried) no longer stays among the failures. Files a host sends
compressed are no longer taken for truncated downloads. A OneDrive whose drive Graph no
longer finds (user deleted, retention over) fails as "OneDrive unavailable"
(
graph.onedrive_unavailable), permanent and not retried, instead of as an item. Cancelling a job during a throttling wait no longer records items as failed. - Recovery readiness: an object removed from its source (a mailbox deleted in Microsoft 365,
for example) no longer turns red because its last backup grows old. Nothing backs it up any
more, and retention always keeps its newest restore point, so it is rated by whether that
backup restores, without the age reasons ("The latest backup is N days old"), and is never
overdue. A kept backup that does not restore stays red. The same rating applies in the
statistics, the objects list, the tenant's overall readiness, the integration API
(
GET /api/v1/status,/objects,/users,/verify/latest, wherereport.reasonsleaves the age out), theverify.completednotification and the rating a new check stores. The table says "Removed from source, backups kept" instead of "Removed from source"; the report page opens with a note "Removed from the source" and lists the age of the kept backup as "Not counted: ..." instead of among the findings. An object whose newest backup was never checked gets one more check from the weekly verify planner and from a job's verify; after that nothing is planned for it. - IMAP backup: a login the server refuses for now (too many connections for the account, an
authentication backend that is down, [UNAVAILABLE], [INUSE] or [LIMIT]) is the new transient
cause
imap.server_limit, retried by itself, instead of a wrong password; the connection test of an IMAP source says "The server temporarily refused the login". [LIMIT], [OVERQUOTA] or a quota text on a read is no longer reported as a full mailbox but asimap.server_limittoo: such a download cap fails the folder as a whole and the next run resumes, instead of one failure per message (a cap the server reports on a read without such a code or text still costs one failure per message). Messages deleted or moved during the backup are not counted as failures. A run whose connection dropped too often fails asimap.connection_lost(transient) instead of "cause not identified". - IMAP archive capture: a message the server refuses or does not deliver no longer holds back its folder or the whole pass; the other messages are captured, and after three passes the capture gives up on it. A refused folder costs only that folder for this pass. Folders a pass did not reach keep their state, so older mail is no longer skipped after an interrupted pass. A limit of the account stops the pass with its state kept. A watch refused with a NO on EXAMINE or IDLE is retried after minutes (doubling up to an hour), instead of being taken for a server without IDLE for a day.
- Restore: an IMAP restore whose connection drops fails and is retried on a fresh connection, where the Message-ID check skips what was already appended, instead of ending "completed" with every remaining item failed. A retried SharePoint list restore in "keep both" mode no longer adds the items the interrupted attempt added, and a retried contacts restore in that mode no longer creates contacts twice. A restore into a mailbox given by address resolves the same mailbox the API accepted (through the directory user's address, never an orphaned or imported mailbox), so a duplicate address no longer picks another one.
- Jobs: the time limit of a job counts from when the queue handed it out, also while it waits
for a free slot of its tenant; a job that waited out its time stops before it starts, so no
retry runs beside a late attempt. A cancel that lands while the attempt starts or fails is
kept (it was overwritten by "active" or "failed" and retried). A job the reconciler ends for
good raises the
job.failedwebhook, the bell and the notification rules like any other, and its checkpoint is cleared. - Public demo: the seed no longer fails when Restow queued an object's first backup itself
after the seed had listed the objects. "Back up now" then answered
already_queued, the seed counted the object as failed, the nightly reset leftwebdown and the demo kept showing its "being reset" page. The seed now waits for the backup that is already queued. - "Remove from Restow" no longer says file restore stops offering the backups of a removed machine: since 0.4.6 its page offers their restore until they are deleted.
- The licence page no longer lists sign-in with Microsoft among what Business unlocks; it is still experimental and off by default.
Security
- ip-address 10.7.5 (from 10.7.2), a transitive dependency through socks, resolves a moderate advisory reported by Dependabot. socks comes with the IMAP library imapflow and is used only for connections through a SOCKS proxy, which Restow does not configure.
- seroval 1.7.5 (from 1.6.7, through the TanStack router) resolves GHSA-7rv5-mrxv-xcr9, GHSA-wp97-x4hv-qvhp and further advisories of its deserializer; zod 4.6.7 (from 4.6.5, an override for the copy better-auth uses) resolves GHSA-mrhx-c7xc-m3w5 there.
- GHSA-mrhx-c7xc-m3w5 (memory use when validating a large array) also names zod 3, which
Restow's own request validation uses and for which no fixed 3.x exists. The audit of CI
ignores this one advisory (
pnpm.auditConfigin package.json) until 0.4.8 moves Restow to zod 4. The request body limit of the api (1 MB on most routes,apps/api/src/middleware/body-limit.ts) bounds it: a 1 MB array that fails element by element costs about 150 MB and a second of the api for that request.
Upgrade notes
New images and four database migrations; no new required environment variable, and the
docker-compose.yml of 0.4.0 stays.
- Four database migrations run at the api's start; none of them rewrites a table:
- 0066 adds two nullable columns to
pve_guests(excluded_at,excluded_by). - 0067 adds the column
settings.pricing(jsonb, default{}, no price set). - 0068 adds the column
settings.app_secret_alerts(jsonb, default{}). - 0069 adds five nullable columns to
protected_objects(status_reason,inactive_since,inactive_notified_at,status_confirmed_at,status_confirmed_by), two check constraints and a trigger that keeps them in step with the object's status; it updates only the orphaned rows.
- 0066 adds two nullable columns to
- New optional environment variable
ENTRA_CLIENT_SECRET_EXPIRES_AT(YYYY-MM-DD, as Entra shows it): a central app whose secret comes from the environment gets expiry reminders only with it in.envand the api restarted. A certificate needs none; an unreadable value counts as unknown. - Sources connected through their own app before 0.4.7 have no expiry date, so no reminder: for a client secret, note the date on the source's page under "Expires"; a certificate gets its date when it is entered again.
- Webhooks and notification recipients get the new events (
object.inactive,object.inactive_many,source.app_secret_expiring,installation.app_secret_expiring) only once they are ticked in the notification matrix (docs/ARCHITECTURE.md "Benachrichtigungsmatrix: welcher Kanal welches Ereignis bekommt"); a bell column that was never changed shows them. - Objects already orphaned before the update count as confirmed (
migration) and raise no notice, so no long amber list appears. Disabled and unlicensed mailboxes, and OneDrives and sites that Microsoft locked, that failed every night are recognised by the first directory sync or backup after the update (a locked site by its first backup): expect oneobject.inactivenotice for each object (an unlicensed user often has two, mailbox and OneDrive), or oneobject.inactive_manywarning for a tenant with many, and confirm them under protected objects once they are intended. - Webhooks and alerts: a backup that Graph or the storage held back (
graph.throttled,graph.service_unavailable,storage.rate_limited) and a backup that ends neutral for an object no longer backed up (graph.mailbox_inactive,graph.object_locked,config.object_inactive) raise nobackup.failedalert and nojob.failedwebhook; the overdue rules speak up when no backup gets through. A restore that completed without restoring anything while items failed now raisesrestore.failedandjob.failed, withdata.job.statusstillcompleted. A job the reconciler ends for good raisesjob.failed. - Integrations:
GET /api/v1/jobskeeps the statusfailedfor these held-back and neutral backups; readfailure.code(and the object'sstatusReason) and treat them like the object's state, not as a failure. - The counting reads the cause stored with each run, so earlier runs that ended with
graph.throttled,graph.service_unavailableorstorage.rate_limitedalso read "Held back by the service, retried" after the update and leave the failed counts of past periods in the statistics and the dashboard. Objects removed from their source are rated without the age of their last backup from the update on, without a new check; those whose newest backup was never checked are checked once by the next planned check. - Proxmox VE: no guest is excluded by the update; guests in no job keep counting as "in no backup job" until an admin excludes them. restow-pve needs no update in this release (no change to the agents or the Proxmox VE plugin); the server withdraws the backups of an excluded guest before a node gets them.
- Prices are empty after the update; the price card appears only once the owner enters a price
under Installation › Prices. Existing mail jobs change only when an administrator applies
the recommended job, and only where the installation offers
archive.mailboxCapture. - Building from source: pg-boss 10.4.2 is patched through
pnpm.patchedDependencies(patches/pg-boss@10.4.2.patch), whichpnpm installapplies; keep thepatchesfolder. The images carry the patch. - Rollback to 0.4.6 after the migrations was not tested: take a database backup before the update and restore it to go back.
Known issues
- The detection of disabled, unlicensed and locked objects was tested against a simulated Graph API. An answer of Microsoft that Restow does not recognise as "no mailbox" or as a lock stays a counted failure, as before.
GET /api/v1/jobshas no field that sets a held-back or neutral backup apart from a failed one; integrations that open tickets from it can readfailure.code.- Notification mail sent through a Microsoft 365 app registration does not fetch a new token
after a refusal: a
Mail.Sendpermission granted later takes effect once the cached token expires. - The expiry date of a client secret is not read from Microsoft: without a date entered (under
Installation, in
ENTRA_CLIENT_SECRET_EXPIRES_ATor on the source) there is no reminder. Sources with an older own secret without an own app (shared client ID) have no date in Restow; switch them to an own app or the central app (docs/ENTRA-SETUP.md "Alternative: eine eigene App pro Quelle"). - On the Recovery readiness reports list, a filter on the rating matches the stored rating. A report stored before the update, or while the object was still at its source, counts the backup's age, so such a report of an object removed from its source can be listed under red while it reads green. Checks from 0.4.7 on store the rating without the age. The readiness history of earlier days does not show the yellow of unconfirmed objects: only the current confirmation is stored, not when it was given.
- A message the IMAP archive capture gives up on (after three passes), messages the backfill could not read and a well-known mail folder Graph refused to resolve are named in the worker log only, not in the interface.
- Excluding a selection of Proxmox VE guests in the inventory sends one request per guest (there is no bulk route); when one fails, the guests before it stay excluded and the list refreshes.
- Group calendars and conversations were tested against a simulated Graph API, never against a real Microsoft 365 tenant (as in 0.4.5).
- IMAP sources with OAuth2 still cannot be created in the API or the interface.
- The known issues of 0.4.4 still apply.
Verification
Before the tag, on the release branch (0.4.6 plus the 0.4.7 work): lint and typecheck of all
workspaces, the unit and Postgres suites of every package on Postgres 16 (with the Postgres
guard), among them migrations 0066 to 0069 with the trigger and backfill of 0069, the directory
sync and backup tests of disabled, unlicensed and locked objects, the $batch and token tests,
the job queue with the pg-boss patch, the guest exclusion, the prices and the recommended job
with the archive; restic-backed tests with restic 0.19, the web DOM tests, the v1 OpenAPI test,
the tests of the CI scripts and the third-party notices. The handling of locked OneDrives and
sites and of disabled and unlicensed accounts was not run against a real Microsoft 365 tenant
before the tag, beyond the production error data that led to it; the guest exclusion was not
run against a real Proxmox VE node.
Release pipeline, 2026-10-29:
| Check | Result | Duration |
|---|---|---|
| 1. Install: images, compose up, migrations (new install and upgrade) | PASS | 92s |
| 2. Health endpoints, worker and scheduler registered | PASS | 0s |
| 3. Passkey sign-in E2E with a virtual authenticator, tenant creation, i18n de/en | PASS | 2m 10s |
| 4. Microsoft 365 backup and restore against the dev tenant | SKIPPED | 0s |
| 5. IMAP backup and restore against Dovecot, hash comparison | PASS | 8s |
| 6. Journal receipt, chain verification, export | PASS | 3s |
| 7. Standalone restore with restow-restore from the storage of check 5 | PASS | 25s |
| 8. Storage targets: S3 (Garage), local path, mounted directory | PASS | 20s |
| 9. Image scan (Trivy) and dependency audit (pnpm audit) | PASS | 19s |
| 10. Endpoint backup: the agent enrolls, backs up a folder, restores it | PASS | 11s |
| 11. Mail import (folder and upload) and export (EML ZIP, MBOX), compared by hash | PASS | 8s |
| 12. File share backup against Samba: back up, browse, ZIP, restore with permissions, copy job | PASS | 2m 17s |
| 13. Archive capture: IMAP sync with IDLE and backfill, forwarded copies, dedupe, chain | PASS | 2m 09s |
12 of 13 checks passed in full, 0 partly, 1 skipped, 0 failed. The full report, including the reason for every skipped or partial check, is attached to the release as smoke-report.md.
The Community images were smoke-tested on their own (smoke-report-community.md, smoke-report-community-arm64.md): amd64 PASS with gaps: 9 of 13 checks passed in full, 0 partly, 4 skipped (see the reasons below); arm64 PASS with gaps: 9 of 13 checks passed in full, 0 partly, 4 skipped (see the reasons below).
Release notes of 0.4.7 on GitHub ↗
0.4.6 beta · 23 October 2026
Restow 0.4.6 fixes restoring Proxmox VE containers, which failed on current Proxmox VE for every restore and every boot test of a container, and stops a new backup chain from being flagged as a ransomware attack. Removed machines keep their backups browsable until they are deleted, a requested restore check says it is queued until its result is in, and error texts open in a card that can be copied. No database migration.
Breaking changes
None. The integration API contract stays at 1.6.0.
Added
- Removed machines (#65): until their backups are deleted, the page of a removed machine offers their restore (and nothing else), and the restore points, browsing, the ZIP download and the repository key keep working for them, audited as before. The list of removed machines links "Restore files". Restoring onto the machine and every other action stay refused (404); once the backups are deleted the machine is gone for every reader (docs/AGENT.md).
Changed
- Error texts behind a badge or a cut-off line (the boot test, the restore check of a VM or container on its page and in the recovery readiness table) open on hover or keyboard focus in a card with the whole text, selectable and with a Copy button, instead of a browser tooltip.
- A boot test that did not boot shows the node's message under its badge, on the guest's page and in the recovery readiness table.
- "Check now" of a VM or container: the row says "Check queued" under the last check, and the guest's page above the earlier result, until the result is in; both refresh every few seconds meanwhile.
- Dependencies: happy-dom 21.0.4 (tests of the web interface only; Dependabot #78).
Fixed
- Proxmox VE: restoring a container from Restow failed with
Can't use string ("restow") as a HASH refon current Proxmox VE, which passes the storage ID to the backup provider's container restore before the info hash. Boot tests of containers ("Did not boot") and "Restore as a new guest" of containers were affected; VMs were not. The plugin of restow-pve accepts both forms; it is installed with the restow-pve update, which reloads the PVE daemons. - Ransomware detection: the first backup of a container's new backup chain (its repository was missing, or reset) was judged against the old chain and flagged as suspicious, since every file is new in an empty repository. The baseline now begins with the newest chain. A restore point flagged that way before the update stays flagged; mark it as a false alarm.
- The first backup of a container's new backup chain is called "First backup", not "Incremental".
Upgrade notes
- No database migration and no new setting.
- Update restow-pve on every Proxmox VE node (the server offers it after its own update; nodes update themselves when idle) before relying on container restores or boot tests. Until then, container restores keep failing as before; VMs are not affected.
- Container restore points that read "Did not boot" or a failed restore check from before 0.4.3: run "Boot test now" or "Check now" once the node is updated.
Known issues
- The known issues of 0.4.5 still apply.
Verification
Before the tag, on the release branch (0.4.5 plus the 0.4.6 fixes): lint and typecheck of all workspaces, the unit and Postgres suites of every package on Postgres 16 (with the Postgres guard), restic-backed tests with restic 0.19, the web DOM tests, the Perl tests of the Proxmox VE plugin (the container restore with and without the storage ID) and the third-party notices. The release smoke runs in the release pipeline; the container restore fix was not run against a real Proxmox VE node before the tag.
Release pipeline, 2026-10-23:
| Check | Result | Duration |
|---|---|---|
| 1. Install: images, compose up, migrations (new install and upgrade) | PASS | 81s |
| 2. Health endpoints, worker and scheduler registered | PASS | 0s |
| 3. Passkey sign-in E2E with a virtual authenticator, tenant creation, i18n de/en | PASS | 2m 06s |
| 4. Microsoft 365 backup and restore against the dev tenant | SKIPPED | 0s |
| 5. IMAP backup and restore against Dovecot, hash comparison | PASS | 8s |
| 6. Journal receipt, chain verification, export | PASS | 3s |
| 7. Standalone restore with restow-restore from the storage of check 5 | PASS | 24s |
| 8. Storage targets: S3 (Garage), local path, mounted directory | PASS | 21s |
| 9. Image scan (Trivy) and dependency audit (pnpm audit) | PASS | 18s |
| 10. Endpoint backup: the agent enrolls, backs up a folder, restores it | PASS | 11s |
| 11. Mail import (folder and upload) and export (EML ZIP, MBOX), compared by hash | PASS | 10s |
| 12. File share backup against Samba: back up, browse, ZIP, restore with permissions, copy job | PASS | 2m 14s |
| 13. Archive capture: IMAP sync with IDLE and backfill, forwarded copies, dedupe, chain | PASS | 2m 12s |
12 of 13 checks passed in full, 0 partly, 1 skipped, 0 failed. The full report, including the reason for every skipped or partial check, is attached to the release as smoke-report.md.
The Community images were smoke-tested on their own (smoke-report-community.md, smoke-report-community-arm64.md): amd64 PASS with gaps: 9 of 13 checks passed in full, 0 partly, 4 skipped (see the reasons below); arm64 PASS with gaps: 9 of 13 checks passed in full, 0 partly, 4 skipped (see the reasons below).
Release notes of 0.4.6 on GitHub ↗
0.4.5 beta · 23 October 2026
Restow 0.4.5 prepares the licence for paid update periods: keys can carry an update period and a series, the image knows its release date, and an installation is offered, told about and allowed to install only the releases its Business or Service Provider key covers. Every key issued so far is a beta licence and keeps covering every 0.x and 1.x release. Revoked machines can be removed from Restow, with or without their backups; Microsoft 365 group calendars and conversations are backed up as protected objects of their own (opt-in); both job lists can start and stop every job and open a detail panel like VMs & containers. The public demo resets from a saved template in about a minute. Three database migrations (Upgrade Notes).
Breaking changes
None. The integration API contract moves to 1.6.0, additively: ObjectKind includes
group and the status reports lastSuccess.groups; clients that handle object kinds
exhaustively should accept the new value. Licence keys with the new optional members verify
only on 0.4.5 and later (ee/licensing/KEY-FORMAT.md).
Added
- Licence keys carry update terms (docs/LICENSING.md). The key format
restow-license-v1accepts the optional membersseries,updates_untilandkidand ignores members it does not know, so keys the licence server issues later keep verifying; the six members of today stay required and keep their rules (mailbox_limitunchanged). Every key issued so far is a beta licence: it covers every 0.x and 1.x release. A key withupdates_untilcovers every release made up to the end of that day (UTC).kidnames the vendor signing key, for a rotation without breaking issued keys. The contract for the issuer, with examples, is ee/licensing/KEY-FORMAT.md. - The image carries its release date (
RESTOW_RELEASED_AT, from the build argumentRESTOW_CREATED, the commit date of the release tag);GET /api/v1/statusreports it asreleasedAt. Release notes publish the same date in areleased:line next to the digests. - The update check marks releases the installed Business or Service Provider key does not
cover, says so calmly on the Updates page (the installed version keeps working; the newest
covered version installs as usual) and refuses to install or plan them (409
urn:restow:problem:update-not-covered). The bell and theupdate.availablealert rules announce only the newest covered release. - Started by hand, a release the installed Business or Service Provider key does not cover
stops before its database migrations, with a log line naming the version, the end of the
update period and the last covered release date; nothing needs to be restored to go back.
With
RESTOW_RUN_AS_COMMUNITY=1it starts as Community instead (the modules stay off, no data is changed). Never for beta licences, development builds, installations without a key or the demo. - The licence page shows which updates the key includes ("all 1.x (beta license)" or the end
of the update period) and, from 30 days before the end, a short notice on the same page.
A new key of the same edition replaces the active one even while it is in force (a
renewal); earlier keys stay in the history, and every installation or replacement is
audited with the key's update terms and whether they cover the running version. A key that
does not cover the running version is refused (
outside_update_period). - Public demo, fast nightly reset (deploy/demo/README.md "Fast reset"): after a full seed
deploy/demo/reset.shsaves a template (apg_dumpof the demo database, its roles and a tar of every volume the seed writes) keyed by the release, the image ids and.env, and restores it the following nights instead of seeding: the database is restored next to the running one and swapped in, so the demo is offline for about 15 to 25 seconds instead of many minutes. The restore moves every system time forward by the template's age (deploy/demo/seed/sql/ shift-time.sql), so the history still ends today and "last backup 12 minutes ago" stays true; the audit log, the mail archive and restic's own snapshot times keep their real times, sign-ins are removed. A template older thanRESTOW_DEMO_TEMPLATE_MAX_AGE_DAYS(default 7), a new release, new images, a changed.envor a failed restore lead to a full seed. Two templates are kept, with a free-space check before saving (RESTOW_DEMO_TEMPLATE_*settings,RESTOW_DEMO_TEMPLATES=falseswitches it off). - Machines: a revoked machine can be removed from Restow (#65). "Remove from Restow" in its
danger zone offers "Remove from the list, keep the backups until they expire" (the newest
backup plus the longest rule of the machine's retention, read as time) or "Delete the machine
and its backups", with the hostname typed and a recent sign-in. The machine leaves every list,
picker, dashboard and count and its backup job; its runs, reports and audit entries stay. The
worker deletes its restic repository (
endpoints/<id>/) on every target that holds files of it: the primary, its copies and retired primaries. Deleting now is refused while a legal hold over the tenant or an unresolved suspicious restore point holds the backups, and the worker waits for such a hold when the backups expire. Objects the storage still locks (S3 Object Lock, a directory's deletion delay) are deferred until their lock runs out, recorded like a refused prune and left out of the lock renewal. The machines page lists the removals still pending, with the date, the hold or the lock. Every step is audited. Database migration 0064 adds the removal state toendpoints. - Machines: an uninstall that waits for a machine that is off now offers "Revoke now", and a machine without contact for longer than a configurable number of days (default 30, under Alerts of the machine) suggests revoking it, on its page and among what needs attention (#65). The machine's detail panel on Inventory shows the suggestion as a state and offers "Revoke now" and, once the machine is revoked, "Remove from Restow" as well.
- Repositories: a warning when a directory repository lies on the system disk of the Restow server, the disk under the operating system, Docker and the database (a full system disk stops everything, as on 16 October 2026). The api recognises it from inside its container by device and block device of the path, the installation default and the mounter's shares included; it is shown on the Repositories page, in the "Backups land in" line of the job editors and on the dashboard's storage tile, where provider admins also see the free space, its trend over the last week and when the disk would be full. The api now also watches the system disk for low disk space. Nothing is blocked.
- Microsoft 365 group mailboxes as protected objects of their own kind
group(docs/MICROSOFT.md "Gruppenpostfächer"): the calendar and the conversations of Microsoft 365 groups and of the groups behind Teams. The directory sync readsgroups/deltaas a second delta stream next to the users (a full enumeration on the first and every full sync, only changes in between, its delta link stored with the rows), keeps only Microsoft 365 groups and orphans removed ones. Group mailboxes are opt-in: the protection rules get a switch "Protect all Microsoft 365 groups" (groups:selectedby default, orall), and single groups can be included in the list of protected objects. A mail job with scope "all" covers every protected group, like sites. Group mailboxes never count as protected mailboxes. - Group mailbox backup: the group calendar through the same calendar code as a user's (series
masters with their exceptions), falling back to the group's
calendarViewand one read per series master when Graph refuses the event listing to the app; a calendar Graph refuses entirely is a warning on the run, not a failure. Every post of every conversation is stored as an RFC 5322 message undermail/Conversations(with its file attachments when Graph hands them out, otherwise their absence is recorded); only threads with new posts are read again. A group that no longer exists fails with the new causegraph.group_unavailable. Migration0065_group_mailboxes(an enum value only). - Group mailbox restore: the restore explorer lists groups in the "SharePoint & Teams" section
with the areas "Group calendar" and "Conversations"; download (EML, JSON) and export (EML,
MBOX, PST) work as for mailboxes. A restore into a user's mailbox brings back events and
conversations; a restore into the group brings back calendar events only, because Microsoft
Graph lets no app post into a group conversation (each post is reported as not restorable,
with the ways that work). Restoring a group is no impersonation. The integration API reports
lastSuccess.groupsandObjectKindincludesgroup(integration API contract 1.6.0, additive; clients that handle object kinds exhaustively should accept the new value), and the dashboard lists group calendars and conversations among the last backups. No new Microsoft permission is needed:Group.Read.AllandCalendars.ReadWritewere always required. - Start now and Stop on every row of both job lists (Servers & clients and Mail & SaaS) and in
the row's "…" menu: one button of a fixed size that says "Start now", "Stop" while the
job's backups run or wait, and "Stopping…" once asked. Stopping asks first and says what it
means for the kind of job. New API routes:
POST /api/v1/backup-jobs/:id/cancel(every queued or running backup of a mail, machine, share or copy job in one set-based request; audited asbackup_job.run_cancelledon the job and per machine),POST /api/v1/pve/jobs/:id/run(a backup of every guest a Proxmox VE job covers, like its schedule would, without moving the schedule;pve.job.run_requested) andPOST /api/v1/pve/jobs/:id/cancel(pve.job.cancelled). For tenant administrators and provider roles that may operate, closed in the public demo. - Detail panels on Inventory and on the job lists, like the one of VMs & containers: a click on
a row, its name or its state opens it (
?machine=<id>on Inventory,?job=<id>on the job lists, so a link opens it). It lists every state, the full error and warning texts of the newest backup or failed run in a copyable monospace block (for a machine also the end of the log), what needs attention, the restore checks, the key facts, the newest runs and the actions.
Changed
- Dependencies: major update of the translation libraries, i18next 26 and react-i18next 17 (updated together). Texts, plurals and number and date formats read exactly as before in English and German.
@azure/msal-nodeis no longer a dependency: nothing used it. The Microsoft 365 app tokens, the admin consent and the IMAP OAuth2 sign-in use Restow's own token client, unchanged.- Public demo: the health checks of postgres, Dovecot and the api run every 2 to 5 seconds instead of every 10 to 15 (with the same grace period), and the full reset stops the old project with a 10 second timeout, so a reset waits less for services that are already ready or about to be discarded.
- Inventory and the job lists keep their layout like VMs & containers: fixed column widths, one row height (loading rows included), one compact state badge per row (the most urgent of what applies; green only for a passed restore check), the progress of a running backup beside it, one-line cells without tooltips, and action buttons of one size. Columns that do not fit a screen give way from the right; the panel has everything.
- "Run now" of a job is called "Start now".
Fixed
- Public demo:
reset.shnow readsRESTOW_DEMO_COMPOSE_FILESfromdeploy/demo/.envas the README always said, not only from its environment, and logs which compose files a run uses. A list from.envthat would publishwebon every interface (the own-VM override with 80/443) is used only together with the newRESTOW_DEMO_PUBLISH_PUBLIC=true, so a co-hosted host whose.envcarries that line keeps running as before; entries that are not compose files indeploy/demo/are refused before anything is stopped. - Restore explorer: a mail message stored as JSON (too large for a plain message) no longer
shows an empty
<subject>.attachmentsfolder next to it. Its attachments are restored with the message and were never listed; the folder row the backup records for them is now hidden from the listing, the folder tree and search as well. - The job dialog of Proxmox VE: on a phone (390 px) the three restore-point fields stack, so "Monatliche Sicherungsstände" no longer runs over the dialog's edge.
Security
- The setup wizard accepts the Entra tenant for Graph mail only as a tenant id (GUID) or a
verified domain, as Settings already did; anything else is refused (422) before it can reach
the token URL. The URL was never open to another host (the value is URL-encoded, the login
host is fixed), but a value such as
..changed its path. - An IMAP OAuth2 secret is refused unless its
tenantIdis a tenant id, a verified domain,common,organizationsorconsumers, and itstokenEndpointis an https URL. Neither the API nor the UI creates such secrets yet; the token request will also need the address policy of IMAP connections before XOAUTH2 is offered.
Upgrade notes
New images and three database migrations; no new required environment variable, and the
docker-compose.yml of 0.4.0 stays.
- Three database migrations run at the api's start: 0063 adds three nullable columns to the
licensetable, 0064 the removal state of a machine toendpoints(three nullable columns and a partial index over the removals that are due), 0065 the protected object kindgroup(an enum value only). None of them rewrites a table. - Installed licence keys keep working unchanged: every key issued before 0.4.5 is a beta
licence and covers every 0.x and 1.x release. From now on, an image started with a key that
does not cover it stops before the migrations and says so in the log;
RESTOW_RUN_AS_COMMUNITY=1starts it as Community instead (docs/LICENSING.md). - Group mailboxes are opt-in: after the update no group is backed up until the protection rules switch "Protect all Microsoft 365 groups" on or single groups are included. The first directory sync after the update reads the groups of every Microsoft 365 source once in full.
- Rollback to 0.4.4 after the migrations was not tested: take a database backup before the update and restore it to go back.
Known issues
- Group calendars and conversations were tested against a simulated Graph API, never against a
real Microsoft 365 tenant; which calendar reads Graph allows an app on a group can differ by
tenant (the backup falls back to
calendarViewand reports what it could not read). - Machines removed from Restow cannot be restored from in the interface while their backups are kept until expiry.
- IMAP sources with OAuth2 still cannot be created in the API or the interface.
- The known issues of 0.4.4 still apply.
Verification
Before the tag, on the release branch (0.4.4 plus the 0.4.5 workstreams): lint and typecheck of all workspaces, the unit and Postgres suites of every package on Postgres 16 (with the Postgres guard), the web DOM tests, the v1 OpenAPI test, drizzle-kit without drift, the Go tests of the agents, the server installer and demo reset tests and the third-party notices. The release smoke ran on the release branch; no real Microsoft 365 tenant was used before the tag.
Release pipeline, 2026-10-23:
| Check | Result | Duration |
|---|---|---|
| 1. Install: images, compose up, migrations (new install and upgrade) | PASS | 89s |
| 2. Health endpoints, worker and scheduler registered | PASS | 0s |
| 3. Passkey sign-in E2E with a virtual authenticator, tenant creation, i18n de/en | PASS | 2m 06s |
| 4. Microsoft 365 backup and restore against the dev tenant | SKIPPED | 0s |
| 5. IMAP backup and restore against Dovecot, hash comparison | PASS | 8s |
| 6. Journal receipt, chain verification, export | PASS | 3s |
| 7. Standalone restore with restow-restore from the storage of check 5 | PASS | 24s |
| 8. Storage targets: S3 (Garage), local path, mounted directory | PASS | 21s |
| 9. Image scan (Trivy) and dependency audit (pnpm audit) | PASS | 19s |
| 10. Endpoint backup: the agent enrolls, backs up a folder, restores it | PASS | 11s |
| 11. Mail import (folder and upload) and export (EML ZIP, MBOX), compared by hash | PASS | 10s |
| 12. File share backup against Samba: back up, browse, ZIP, restore with permissions, copy job | PASS | 2m 14s |
| 13. Archive capture: IMAP sync with IDLE and backfill, forwarded copies, dedupe, chain | PASS | 2m 09s |
12 of 13 checks passed in full, 0 partly, 1 skipped, 0 failed. The full report, including the reason for every skipped or partial check, is attached to the release as smoke-report.md.
The Community images were smoke-tested on their own (smoke-report-community.md, smoke-report-community-arm64.md): amd64 PASS with gaps: 9 of 13 checks passed in full, 0 partly, 4 skipped (see the reasons below); arm64 PASS with gaps: 9 of 13 checks passed in full, 0 partly, 4 skipped (see the reasons below).
Release notes of 0.4.5 on GitHub ↗
0.4.4 beta · 22 October 2026
Restow 0.4.4 archives mail beyond Exchange journaling (Business): Microsoft 365 mailboxes by Graph change notifications, IMAP and Google Workspace accounts by IMAP sync with IDLE, and the copies a mail provider forwards, each with a one-time takeover of the mail already there and one archived item per mail however many ways captured it. The job editor offers only the capture ways that fit each kind of mailbox, the archive page shows the capture, the hold confirmation, ready-made Exchange Online PowerShell commands and the forwarding addresses, and alerts say when capture stops. The restore explorer opens large mailboxes and OneDrives in milliseconds instead of seconds to minutes; four database migrations (Upgrade Notes). Mail jobs that archived before keep doing exactly that after the update: nothing new starts by itself.
Breaking changes
None. The integration API contract stays at 1.5.0: its archive status and report count
forwarded copies in their totals, but do not list them as a channel of their own yet. In the
session API, mail jobs carry archiveCapture and their members captureKind, and the archive
search hides captures a stronger one superseded unless includeSuperseded=true; a job that
archives without any capture way for a kind of its mailboxes is refused with 422
archive_capture_no_way, which the defaults never cause. The IMAP and Google ways of a job
from before the update may carry pending: true (no way chosen yet): the API accepts such a
job until a way is chosen, keeps the marker only where the job had it, and never sets it.
Added
- Archive capture foundation for mail beyond Exchange journaling (docs/ARCHIVE.md
"Erfassungswege"). A mail job's archive keeps its capture ways per kind of mail object
(
archiveCaptureon create, change and read of a mail job: Microsoft 365 by Graph and/or journaling, IMAP and Google Workspace by IMAP sync and/or a forwarded copy; the defaults of a new job are Graph and IMAP sync, jobs that archived before keep journaling only, see Upgrade Notes). A job that archives needs at least one way for every kind of mailbox it covers (422archive_capture_no_way). Members and candidates carrycaptureKind(m365,google,imap,imap_microsoft), andGET /api/v1/backup-jobs/defaults?kind=mailnames the tenant'smailSourceKinds. - Archive page (Business): a notice that can be put away names the mail jobs where Graph or
IMAP archiving could be switched on, each with a link to the job, and the capture status
lists the jobs whose IMAP or Google Workspace accounts wait for a capture way
(
GET /api/v1/archive/capturereturns them asjobs). - Worker: a capture service keeps one stream per archived mailbox and mailbox way, claimed
across worker processes with
FOR UPDATE SKIP LOCKEDand a lease, reconciled from the mail jobs every two minutes without a job row per mail. Pausing a job stops its scheduled backups only; capture goes on until archiving or the way is switched off in the job. It idles while the edition does not include capture from the mailbox, stops on shutdown and update drain, and pauses a tenant while its storage switches targets. New settingsARCHIVE_CAPTURE_CONCURRENCY(16),ARCHIVE_CAPTURE_TENANT_CONCURRENCY(4) andARCHIVE_BACKFILL_CONCURRENCY(4). Thearchivequeue has a handler: the one-time takeover of a mailbox's existing mail once its stream is live (marked as captured late), and archive schedules of older releases make the named mailboxes due. The Graph and IMAP ways plug into this framework. GET /api/v1/archive/capture(state per capture way: availability, streams, how mail is noticed, counts of 24 hours and 7 days, takeover progress),GET /archive/capture/mailboxes,POST /archive/capture/mailboxes/:objectId/resync(audited asarchive.capture.resync_requested) andGET/PUT /archive/capture/holds/:sourceId, the confirmation that a retention policy or litigation hold keeps deleted mail in a Microsoft 365 tenant (audited asarchive.hold.confirmedandarchive.hold.unconfirmed).- Business: new capability
archive.mailboxCapture; the gated featuresarchive.mailboxCaptureandarchive.smtpCapture(API/meand the worker's newfeaturesextension point).archive.journalReceiveralso covers forwarded copies. - Archive capture of Microsoft 365 mailboxes by Graph (docs/MICROSOFT.md "Archiv-Erfassung per
Graph"): one change notification subscription per archived mailbox (created and renewed in
$batch, at most 20 per request and one request per second and Microsoft tenant; renewed with less than a day left; reauthorized, recreated after removal, after expiry and when the Entra app changes), immutable message ids, notified mail fetched within minutes (drafts skipped), a delta catch-up over every folder and the recoverable deletions with its own state (everyARCHIVE_GRAPH_CATCHUP_MINUTES, default 360, and at once after a missed notification), and the one-time takeover of the existing mail in resumable pages. No new Microsoft permission is needed. Without a public HTTPS URL the mailboxes are polled everyARCHIVE_GRAPH_POLL_MINUTES(default 15) and the capture status names the reason (graph.notification_url_unavailable); a subscription Microsoft refuses is retried hourly meanwhile (graph.subscription_failed). - Public hooks
POST /api/v1/hooks/graph/notificationsand/lifecyclefor Graph: the validation token is echoed, everything else answered 202; only notifications whoseclientStatematches the subscription's stored SHA-256 count, and they only queue a fetch. Bodies are limited to 1 MiB, and the hooks answer 404 while the edition does not include capture from the mailbox. GET /api/v1/archive/capture/holdsalso returnschecks: read-only Exchange Online and Security & Compliance PowerShell commands to check litigation holds and retention policies before confirming a hold. Graph v1.0 offers no way to read them (spike result in docs/MICROSOFT.md).- IMAP archiving (Business; docs/IMAP.md "Archivierung"): the capture way
imap_syncarchives an IMAP account continuously. Activation records where every folder stands, the one-time takeover brings in the mail that was there (resumable, flagged captured late), and every pass reads only new mail: STATUS per folder, metadata first, sources only of mail the archive does not hold for the account yet. All folders but Drafts; on Gmail and Google Workspace only All Mail, Spam and Trash. A changed UIDVALIDITY reads the folder again without archiving anything twice. Up toARCHIVE_IMAP_IDLE_MAX(300) accounts per worker are watched with IMAP IDLE, shared between worker processes through Postgres locks, with a STATUS round over the other folders everyARCHIVE_IMAP_POLL_MINUTES(5); other accounts are polled everyARCHIVE_IMAP_LOGIN_POLL_MINUTES(10). New source settingarchiveIdle(auto,off) for providers with a tight connection limit. Why an account is polled is shown asimap.idle_unsupported,imap.idle_disabledorimap.idle_budget. - Forwarded copies into the archive (Business; docs/IMAP.md "Weiterleitung"): the receiver takes
plain copies a provider forwards (Postfix BCC maps, mailcow, Sieve
redirect :copy, Google Workspace routing) as sourcesmtp_forward, next to Exchange journal reports. Every IMAP account can have its own archive addressarchive+<token>@<journal host>; a copy to it belongs to exactly that account, a copy to the tenant's journal address is assigned by its headers andMAIL FROM. New routesGET /api/v1/archive/forwarding(accounts, addresses, copies of the last 7 days; issues nothing),POST /api/v1/archive/forwarding/:objectId/address(issued once, auditedarchive.forward.address_created) andPOST /api/v1/archive/forwarding/:objectId/rotate(auditedarchive.forward.address_rotated, fingerprints only). GET /api/v1/archive/journalnamesfirstReportAt, the first journal report that arrived.- Release smoke check 13: IMAP archiving with backfill and IDLE, forwarded copies and their dedupe against the synced mail, against the Dovecot of check 5.
- Job editor: the archive's capture ways per kind of mailbox in the job (Microsoft 365: Graph and Exchange journaling; IMAP and Google Workspace: IMAP archiving and forwarding), each labelled as captured at transport or near-time, locked with the edition that has it, at least one per kind; IMAP accounts on a Microsoft host get the hint to connect a Microsoft 365 source instead.
- Archive page: capture from the mailboxes (Graph and IMAP archiving: streams, how mail is
noticed and why a mailbox is checked at intervals, counts, takeover of existing mail), the
capture per mailbox with a catch-up, and the hold confirmation of each Microsoft 365 source
with the Exchange Online PowerShell commands that check it. Search and detail name forwarded
copies, mail captured afterwards and the other ways that captured the same mail again; a
capture replaced by a stronger one is hidden unless asked for (
includeSupersededonGET /api/v1/archive/search), and the detail links both (supersededBy,captures). - Business: Exchange journaling as ready-made Exchange Online PowerShell commands (connector,
mailbox for undeliverable journal reports, journal rule) and a wait for the first report that
checks every 5 seconds. Forwarding to the archive: an address per IMAP account (created,
copied, renewed after a question), its status, and instructions for Postfix and mailcow BCC
maps, Sieve
redirect :copyand Google Workspace routing to the tenant's journal address. - IMAP source page: the archive setting
archiveIdle, IMAP IDLE (automatic) or checks at intervals only, saved at once and locked without the edition that archives from the mailbox. - Exporting the current archive search leaves out the captures a stronger one superseded, as
the search does, so the export holds what the search counted; with superseded captures shown,
the export includes them (
includeSupersededin the export's search filter). - Alerts of the archive capture as
archive.failed(bell, rules, mail): a mailbox whose capture has been in error for more than an hour, an IMAP account whose forwarded copies stopped for 24 hours while its job wants them, and a journal address without reports for 24 hours, each after it received before and once per stretch. The bell and the mail say which of these it is and lead to its section of the archive page.
Changed
- Every archive writer (journal receiver, file import, capture ways) appends through one shared
path. The original is uploaded before the tenant's chain lock is taken, so a large report no
longer holds up the other deliveries of the tenant. Mail captured more than once is recognised
by Message-ID and content: a capture not stronger than what the archive holds becomes a capture
row on that item instead of a second item, a stronger one names the item it supersedes, and the
same Message-ID with another body is kept and flagged
message-id-conflict. Journal reports and imported messages are still stored as items of their own. - The archive record format gains the optional
bodyHashandsupersedesItemIdand the sourcesmtp_forward(packages/core archive/FORMAT.md); records written before stay as they are. - Journal receiver: one SMTP transaction delivers into one tenant's archive; a recipient of a
second tenant is answered 452 and delivered by the sender separately. The limit per sending IP
rises from 60 to 600 connections per minute (
JOURNAL_RATE_PER_IP), and a new limit per archive address (JOURNAL_RATE_PER_ADDRESS, 300 per minute) answers 451. Mail to the journal address that is not a journal report (noX-MS-Journal-Reportheader and not built like one) is archived as a forwarded copy with its own headers' envelope instead of as an incomplete report. - Pausing a mail job stops only its scheduled backups; archiving goes on, and the pause question says so. The forwarding page counts the accounts of a paused job as wanting their forwarded copy. Switching archiving or a capture way off in the editor asks first: new mail of these mailboxes is no longer archived from then on, the archive kept so far stays.
- docs/ARCHIVE.md describes the capture ways of 0.4.4; docs/ARCHITECTURE.md describes the Graph throttling as it is built (no shared token bucket per source).
- Restore explorer: the restore points of an account load alongside its owner's entry instead of after it, one round trip less before a mailbox or OneDrive shows its folders.
- The Proxmox VE job dialog is twice as wide and lays its settings out in two columns from tablet width on (name and schedule with the next runs on the left; scope, restore points, restore check, boot test and guests on the right), so a job's settings fit on a desktop screen without scrolling; on a phone it stays one column at full width.
Fixed
- Opening the job editor of a job with only IMAP accounts no longer creates the tenant's journal address: it is requested only while Exchange journaling is chosen for Microsoft 365 mailboxes.
- The archive chain could fork when two writers of one tenant stamped their items with the same time: the predecessor was taken by time alone. Writers now take the last item in the order the chain check walks (time, then id) and stamp their item strictly later.
- Restore explorer: opening a large mailbox or OneDrive no longer takes seconds to minutes
before its folders show. Every folder listing read all entries of the restore point (every
message of the mailbox, every file of the OneDrive, with their metadata), twice per request,
and matched the folders below against them without an index; a folder whose subfolders
have no entry of their own (the mailbox root with
mailandcalendar, a OneDrive root) grew with the square of the entries and could time out. The listing now reads the folder's children and the folders below it from two new indexes (migration0061_explorer_parent_index; on a largemanifest_versionstable the update takes longer, docs/UPDATING.md): with 120 000 messages and 200 000 files, the mail folder list went from 2.5 s to 15 ms, the root of the mailbox and of the OneDrive from over a minute to 15 ms, and every OneDrive folder from 1.5–2.5 s to about 10 ms. A folder of 50 000 messages lists its first page in about 0.5 s (was 1.9 s). Restore points of 0.3.4 and earlier that the worker has not converted yet are listed as before. - Restore explorer: the versions OneDrive kept of a file and the calendar's month view read the same indexes instead of every entry of the restore point (130 ms and 260 ms to under 50 ms for the sizes above).
- Mail preview, attachment download and calendar series: each chunk of the message is read from storage with a byte-range request for exactly its bytes instead of downloading its whole pack (up to 64 MiB) for every preview; a chunk damaged on every target is still read through its whole pack as before. The archive's content check reads the same way.
Upgrade notes
New images and four database migrations; no new required environment variable, and the
docker-compose.yml of 0.4.0 stays.
- Migrations 0059 to 0062 run at the api's start: the capture tables, new columns and five
indexes on
archive_itemsandbackup_jobs, two indexes onmanifest_versionsfor the restore explorer, and the capture ways of the mail jobs that already archive (below). The indexes are built inside the migration, where writes to the table wait; expect about a minute per ten million rows on SSD storage. Countarchive_itemsandmanifest_versionsbeforehand and plan a large installation's update outside the backup window (docs/UPDATING.md). - Capture from the mailbox needs the capability
archive.mailboxCapture. Capabilities follow the license's edition (ee/licensing), so every Business and Service Provider license includes it without a new key; the Community build keeps the capture service idle. - Mail jobs that archive keep doing exactly what they did before the update: migration 0062 gives each of them Exchange journaling only for Microsoft 365 mailboxes and no capture way yet for IMAP and Google Workspace accounts (marked as waiting for a choice), so no capture and no takeover of existing mail starts by itself. An administrator switches Graph or IMAP archiving on in the job; the job editor says at the IMAP card that no way is chosen yet, the archive page names the jobs where a way could be switched on (with a notice that can be put away, Business only), and the capture status lists the jobs waiting for a choice. Edits that do not touch the ways (name, schedule) save as before. Jobs created after the update, and a job that switches archiving on, start with the defaults: Graph for Microsoft 365 mailboxes, IMAP sync for IMAP and Google Workspace accounts, each with a one-time takeover of the mail already in the mailbox (marked as captured late).
- Graph capture is notified of new mail only when the installation's public URL is
https://on port 443 and reachable from Microsoft (/api/v1/hooks/graph/*; a reverse proxy of your own must pass it through, with at most 1 MiB per request). Otherwise every mailbox is polled everyARCHIVE_GRAPH_POLL_MINUTES(15) and the capture status says why. No new Microsoft permission is needed. - IMAP archiving keeps one connection per account open (IDLE) where the server offers it.
For providers with a tight connection limit, set the source to "Check at intervals only"
(
archiveIdle: "off"). - New optional settings: the journal receiver's
JOURNAL_RATE_PER_IP(now 600 per minute, was a fixed 60) andJOURNAL_RATE_PER_ADDRESS(300), and the capture'sARCHIVE_CAPTURE_CONCURRENCY,ARCHIVE_CAPTURE_TENANT_CONCURRENCY,ARCHIVE_BACKFILL_CONCURRENCY,ARCHIVE_GRAPH_CATCHUP_MINUTES,ARCHIVE_GRAPH_POLL_MINUTES,ARCHIVE_IMAP_IDLE_MAX,ARCHIVE_IMAP_POLL_MINUTESandARCHIVE_IMAP_LOGIN_POLL_MINUTES(.env.example). - Rollback to 0.4.3 after the migrations was not tested: take a database backup before the update and restore it to go back.
Known issues
- Graph capture was tested against a simulated Graph API, IMAP archiving against a simulated IMAP server and the Dovecot of the release smoke, forwarding against the receiver itself; never against a real Microsoft 365 or Google Workspace tenant, Postfix, mailcow or hoster.
- Holds in a Microsoft 365 tenant are not detected: Graph v1.0 offers no way to read them, so an administrator confirms them per source. Of the Recoverable Items only the deletions folder is read; mail purged before Restow fetched it can be missing. Graph cannot export mail over about 150 MB; such mail is logged, not archived.
- The receiver does not check the sender (SPF), and its limits count per api process.
- IMAP sources with OAuth2 still cannot be created in the API or the interface, so a Google Workspace account is recognised by its host or folder list only.
- The integration API's archive status and report do not list forwarded copies as a channel of their own (see Breaking Changes).
- The known issues of 0.4.3 still apply.
Verification
Before the tag, on the release branch (0.4.3 plus the 0.4.4 workstreams): lint and typecheck of all workspaces, the unit and Postgres suites of every package on Postgres 16 (with the Postgres guard), the web DOM tests on happy-dom including the layout-width and green-is-proof checks, the v1 OpenAPI test, drizzle-kit without drift, gofmt, go vet and the Go tests of the agents, the Perl tests of the Proxmox VE plugin, the server installer and demo reset tests, shellcheck, the CI script tests, the smoke runner's unit tests and the third-party notices. The release smoke, including the new check of the archive capture against Dovecot, runs in the release pipeline; no real Microsoft 365 tenant, Google Workspace or mail provider was used before the tag.
Release pipeline, 2026-10-22:
| Check | Result | Duration |
|---|---|---|
| 1. Install: images, compose up, migrations (new install and upgrade) | PASS | 94s |
| 2. Health endpoints, worker and scheduler registered | PASS | 0s |
| 3. Passkey sign-in E2E with a virtual authenticator, tenant creation, i18n de/en | PASS | 2m 06s |
| 4. Microsoft 365 backup and restore against the dev tenant | SKIPPED | 0s |
| 5. IMAP backup and restore against Dovecot, hash comparison | PASS | 8s |
| 6. Journal receipt, chain verification, export | PASS | 3s |
| 7. Standalone restore with restow-restore from the storage of check 5 | PASS | 24s |
| 8. Storage targets: S3 (Garage), local path, mounted directory | PASS | 21s |
| 9. Image scan (Trivy) and dependency audit (pnpm audit) | PASS | 23s |
| 10. Endpoint backup: the agent enrolls, backs up a folder, restores it | PASS | 12s |
| 11. Mail import (folder and upload) and export (EML ZIP, MBOX), compared by hash | PASS | 8s |
| 12. File share backup against Samba: back up, browse, ZIP, restore with permissions, copy job | PASS | 2m 30s |
| 13. Archive capture: IMAP sync with IDLE and backfill, forwarded copies, dedupe, chain | PASS | 117s |
12 of 13 checks passed in full, 0 partly, 1 skipped, 0 failed. The full report, including the reason for every skipped or partial check, is attached to the release as smoke-report.md.
The Community images were smoke-tested on their own (smoke-report-community.md, smoke-report-community-arm64.md): amd64 PASS with gaps: 9 of 13 checks passed in full, 0 partly, 4 skipped (see the reasons below); arm64 PASS with gaps: 9 of 13 checks passed in full, 0 partly, 4 skipped (see the reasons below).
Release notes of 0.4.4 on GitHub ↗
0.4.3 beta · 22 October 2026
Restow 0.4.3 stops a locked repository from failing a Proxmox VE restore check (it is postponed and, if the lock stays, reads "Could not check" in amber with who holds the lock) and tidies VMs & containers up into tabs with search, filters, a detail panel and tables that no longer jump while backups run. Proxmox VE nodes and agents follow a server update even when its announcement went unanswered, "Update nodes and agents now" starts that step by hand, and restow-pve restarts the PVE daemons after it updates itself. No database migration; nodes still on restow-pve 0.4.1 need one update by hand (Upgrade Notes).
Breaking changes
None. The integration API contract stays at 1.5.0. In the session API, a Proxmox VE guest's
lastVerify.state can be yellow (with notChecked) and a node carries offeredVersion
and outdated; both are additive.
Added
- Updates tab: Update nodes and agents now on the card of nodes and agents. When the
server runs a newer release than its Proxmox VE nodes and agents but no update led into the
step where they follow it (an update by hand, or one whose announcement went unanswered),
an administrator with every tenant starts that step: new runs pause for at most 15 minutes,
reachable nodes and agents install the release when idle, and the pause ends once they are
done (
POST /api/v1/updates/fleet/update-now, audited asupdate.fleet_startedwithmanual: true). Outside an update, nodes and agents that are behind show as "Update available". - Proxmox VE inventory (
GET /api/v1/pve): every node carriesofferedVersion(the restow-pve release the server offers) andoutdatednext to itshelperVersion. - Releases publish the two images of the public demo as well (
restow-demo-seedandrestow-demo-dovecot, amd64 and arm64, signed, with SBOMs and the release's tags), anddeploy/demo/reset.shcan follow a channel or a fixed version (RESTOW_DEMO_FOLLOWindeploy/demo/.env): the nightly reset pulls the images of one release and stops before it changes anything when they do not match or a pull fails (deploy/demo/README.md).
Changed
- VMs & containers is tidied up into three tabs with counts: Guests (the default), Backup
jobs and Nodes. The note that the Proxmox VE host itself is not backed up is one line on the
Nodes tab and can be hidden. The Nodes tab shows each node's heartbeat, its restow-pve
version with "Update to …" when the server offers a newer release (
outdated), its Proxmox VE version, how many guests it has, its queue and its problems; queue control and revoking are in the row menu. - The guests table has a search over ID and name, filters by node, kind (VM or container), backup job (also "No backup job") and state, a "Problems only" button (failed, warnings or gaps, a restore check that failed or could not run, waiting for a disconnected or paused node, not backed up), sortable columns (ID, name, node, last backup, backup size, state), pages of 25 to 250 guests and the count of what the filters leave. Tab, search, filters, order, page and the open guest live in the address, so a link opens the same view.
- A guest's row shows one state badge, chosen by priority, with a small progress bar while its backup runs or "+N" for further problems. Clicking the row (or its name) opens a detail panel with every state, the complete error and warnings of the last run in a scrollable, copyable block, the restore check with what it found (or, when a locked repository kept it out, who holds the lock, amber), the backup sizes, the backup job, the newest runs and the actions (back up now, stop, withdraw, check now, open the guest page).
- The Backup column says how large a guest's first (full) backup was and what the last follow-up added, for example "First: 32 GB on Oct 3 · Last: +210 MB"; the detail panel shows the new data of each of the last 30 backups as small bars (#58).
- The README screenshots show 0.4.2, with two new views (the mail archive and a server).
- Development: the web tests run on happy-dom 21.
Fixed
- Proxmox VE: right after a container backup, the new restore check often failed with
restic check failed (exit code 11) the "unlock" command can be used to remove stale locksand the guest showed a red "Restore check failed", although the check never ran: another run (usually the guest's own backup, whose lock a cancel or an API restart had left behind) held the repository's lock. A check a lock keeps out is no longer a failed check: it is tried again ten minutes later, the previous result standing, and after two hours the restore point reads "Could not check: repository locked" (yellow, attention, never red or green) with restic's report of who holds the lock. The worker removes stale restic locks before the check and before retention, restic waits up to two minutes for a lock during the check and up to 15 minutes during the container backup (restow-pve), a guest is not checked while a run of it is going or its node took on a backup of it, and a new restore point is checked two minutes after its commit at the earliest. A cancelled or ended run may still delete its own restic lock (every other request of it is refused and audited). A failed check keeps restic's explaining lines, not only its last one. No database migration. - Updates: announcing an update could report "The updater is not running" although the
updater accepted it a few seconds later. The api waited 5 seconds for an answer while the
updater checks Docker, the Compose configuration, the disk space and the running version
first (5 to 12 seconds on real installations). The update then ran without the pause of new
runs, without the
update.scheduledaudit entry and without the step in which Proxmox VE nodes and agents follow the server, so they stayed on the old release. The api now waits up to a minute for the announcement (30 seconds for cancelling, dismissing, restarts and deleting a dump); when the answer is still lost it reads the updater's state again and treats the update as accepted when the updater shows it. The pause is lifted only on a clear refusal or an idle updater, and "Start now" after waiting for runs no longer sends a false failure notification. The updater answers faster: it checks once per announcement and reuses the measured database and image sizes of the last ten minutes (the run's first step measures again). The Updates tab reads the state again after a failed announcement and says it is checking whether the update was accepted instead of "not running". - Updates: the Updates tab could say "The updater is not running" for a few seconds every now and then. When the updater's check of the last 30 seconds had expired, it checked again before it answered (several short-lived helper containers), which could take longer than the 5 seconds the api waits for the state, and the api then remembered "no updater" for 8 seconds. The updater now answers at once with its last check and checks again in the background (an update still checks anew before it starts), and a plain state read that times out keeps the updater's last state for up to 30 seconds after its last answer.
- Proxmox VE: after a self-update (automatic, "update now" or
restow-pve update) the PVE daemons kept the old storage plugin loaded until they restarted. restow-pve now restarts pvedaemon, pveproxy, pvestatd and pvescheduler like the installer does (systemctl reload-or-restarton the running ones; a failure is logged and does not undo the update).restow-pve updatealso restarts the running restow-pve service, so the node reports the new release with its next heartbeat. A node that an older restow-pve updated restarts the daemons once by itself (it remembers the plugin files it restarted them for). - VMs & containers no longer jumps while backups run: the guest and job tables keep fixed column widths and one row height (loading rows included), long texts are cut to one line instead of wrapping, the progress has a reserved slot, and "Back up now", "Stop", "Withdraw" and "Stopping…" share one button size. Error texts no longer appear in red under a guest's row, and warnings no longer only in a tooltip.
- Demo: during the nightly reset the demo shows a short page in English and German that reloads every minute, instead of a 502. After signing in, the overview opens on a customer tenant instead of the empty provider organization, and the simulated machines report the running release as their agent version instead of 0.2.1.
Upgrade notes
New image, no database migration, no new required environment variables, and the
docker-compose.yml of 0.4.0 stays.
- Proxmox VE nodes should run restow-pve 0.4.3: it waits for the server's repository lock
during a container backup and restarts the PVE daemons after it updates itself. Nodes on
restow-pve 0.4.2 install it with the step after the server update (or "Update nodes and
agents now"). Nodes on restow-pve 0.4.1 ignore "update now" and that step; they update with
their own check (two minutes after the service starts, then every six hours), or by hand
once no backup or restore runs on the node:
/opt/restow-pve/bin/restow-pve update, thensystemctl restart restow-pve pvedaemon pvestatd pveproxy pvescheduler. From 0.4.3 on restow-pve restarts the daemons itself; a node that an older release updated does it once after the new release starts. - Restore checks that a locked repository failed before the update stay red until the next check of the guest ("Check now" on the guest or VMs & containers).
- Demo hosts: the demo images are now published with every release; to let the demo follow
them, set
RESTOW_DEMO_FOLLOWas deploy/demo/README.md describes. - Rollback: 0.4.2 starts on the same database. It shows a restore point that could not be checked as unchecked.
Known issues
- The new parts were tested against a simulated Proxmox VE API and S3 store only, never against a real Proxmox VE cluster or S3 provider.
- The redesigned VMs & containers page was tested with DOM tests, not visually in a browser with thousands of guests.
- The known issues of 0.4.2 still apply, except the file share repository healing, which comes later.
Verification
Before the tag, on the release branch (main plus the 0.4.3 workstreams): lint and typecheck of all workspaces, the unit and Postgres suites of every package on Postgres 16 (with the Postgres guard), the web DOM tests on happy-dom 21 including the layout-width and green-is-proof checks, the v1 OpenAPI test, gofmt, go vet and the Go tests of the agents, the Perl tests of the Proxmox VE plugin, the server installer and demo reset tests, and shellcheck. The release smoke runs in the release pipeline; no real Proxmox VE cluster or S3 provider was used before the tag.
Release pipeline, 2026-10-22:
| Check | Result | Duration |
|---|---|---|
| 1. Install: images, compose up, migrations (new install and upgrade) | PASS | 87s |
| 2. Health endpoints, worker and scheduler registered | PASS | 0s |
| 3. Passkey sign-in E2E with a virtual authenticator, tenant creation, i18n de/en | PASS | 2m 15s |
| 4. Microsoft 365 backup and restore against the dev tenant | SKIPPED | 0s |
| 5. IMAP backup and restore against Dovecot, hash comparison | PASS | 8s |
| 6. Journal receipt, chain verification, export | PASS | 3s |
| 7. Standalone restore with restow-restore from the storage of check 5 | PASS | 24s |
| 8. Storage targets: S3 (Garage), local path, mounted directory | PASS | 20s |
| 9. Image scan (Trivy) and dependency audit (pnpm audit) | PASS | 21s |
| 10. Endpoint backup: the agent enrolls, backs up a folder, restores it | PASS | 11s |
| 11. Mail import (folder and upload) and export (EML ZIP, MBOX), compared by hash | PASS | 8s |
| 12. File share backup against Samba: back up, browse, ZIP, restore with permissions, copy job | PASS | 2m 17s |
11 of 12 checks passed in full, 0 partly, 1 skipped, 0 failed. The full report, including the reason for every skipped or partial check, is attached to the release as smoke-report.md.
The Community images were smoke-tested on their own (smoke-report-community.md, smoke-report-community-arm64.md): amd64 PASS with gaps: 9 of 12 checks passed in full, 0 partly, 3 skipped (see the reasons below); arm64 PASS with gaps: 9 of 12 checks passed in full, 0 partly, 3 skipped (see the reasons below).
Release notes of 0.4.3 on GitHub ↗
0.4.2 beta · 21 October 2026
Restow 0.4.2 stops a running backup from the web app (Proxmox VE, servers and clients,
file shares) and lets Proxmox VE nodes and agents follow a server update before new runs
start. A container whose repository went missing after a storage change begins a new
backup chain instead of failing, and a storage "move" now copies the restic repositories
of containers, machines and file shares. Every new Proxmox VE restore point is checked
within minutes of its backup. The storage check tells a pack it could not read for the
moment (a transfer or permission error) from a damaged one. The restore explorer lists one
entry per person and shows a mailbox's calendar by month; the mail job editor asks first
whether a job backs up, archives or both. The installation has a time zone of its own, and
restow-agent update installs the offered agent release at once. Six database migrations.
Breaking changes
None. The public API's storageIntegrity.state has the new value unreadable (integration
API contract 1.5.0, additive); clients that handle its values exhaustively should accept
it. "Keep existing backups where they are" is refused while containers or file shares have
restic repositories (see Fixed and Upgrade Notes).
Added
- Proxmox VE: "Reset repository" for a container (guest page, danger zone): the guest's name
typed, a recent sign-in, audited (
pve.repository.reset). Its restore points become unavailable at once and the next backup begins a new chain (POST /api/v1/pve/guests/:id/repository/reset). - Where backups land:
GET /api/v1/storage/destinationnames the tenant's effective primary repository. The guest page and its new "Back up now" dialog, the PVE job and connect dialogs, a machine's and a file share's page and the job editors show it; the installation default on the server's local disk is highlighted. The Repositories section and the dialogs that add PVE nodes, machines and file shares warn "Backups land on the server's own disk" when the primary is a directory on a disk of the server itself (not a network share). - Proxmox VE: every new restore point is checked within minutes of its backup, a bounded
number per pass, besides the weekly check of every guest. Installation › Proxmox VE has the
setting "Check every new restore point right after the backup" (on by default;
GET/PUT /api/v1/settings/pve). One database migration. - Proxmox VE in Recovery readiness: "Check now" per guest and "Check all unverified"
(
POST /api/v1/pve/guests/:id/verify,POST /api/v1/pve/verify-unverified); the footer names the Microsoft 365 schedule as such and says how VMs and containers are checked. - Proxmox VE backup jobs take the cadences of a mail job: every few hours, daily, on chosen weekdays, monthly or a cron expression (not more often than hourly).
- The Restow server's own VM: found by its SMBIOS UUID (restow-pve 0.4.2 reports it with the inventory) or marked by an admin on the guest's page. It is marked in the lists and the job editor, no job backs it up, and "Back up now" and putting it into a job need a confirmation that names the self-backup risk (docs/PVE.md, "The Restow server's own VM").
- Stop a backup from the web app, without SSH on the node. Proxmox VE: Stop in the VMs & containers table, on the guest's page and in the run drawer withdraws a queued backup at once and stops a running one: restow-pve now keeps its heartbeat going while a backup runs, gets the cancel order with it and stops the vzdump task through the PVE API (a backup started in PVE: its provider). The run ends cancelled, not failed, and shows "Cancelled by ". Per node: Pause queue (no new backup starts until resumed) and Cancel all queued, also for the whole tenant. Servers and clients: Stop backup on the machine's page and in the run drawer; the agent stops restic cleanly with its next heartbeat (every minute while it backs up). Runs of file shares can now be cancelled from the run drawer as well (Microsoft 365 and IMAP runs already could). Every stop asks first and is audited.
- Nodes and agents follow a server update: after the server runs the new release, new runs stay paused while Proxmox VE nodes and agents install it (they are idle then), until every reachable one reports it or 15 minutes have passed. The Updates tab shows their progress and, for the ones not reached, the command for an update by hand. Update to on a node and on a machine asks for the update at any time.
- Updates can be planned for a chosen date and time (in the installation's time zone) besides the lead times from now; nothing pauses before, and everyone sees a notice from a day before.
restow-agent updateon a machine, likerestow-pve updateon a node: it installs the release the server offers right away, through the same signed download as the automatic self-update, restarts the service and says what it did; while a backup runs it refuses and the agent updates itself once idle. The Updates tab names it for agents that were not reached (and the service restart for agents before 0.4.2).- The installation has a time zone of its own: chosen in the setup wizard (the browser's zone
preselected) and changed by an owner under Installation › Server & operation › Server, with
a searchable list of zones. Planned updates are read in it and a new tenant starts with it.
Without a choice,
RESTOW_TIME_ZONE, thenTZ, then Europe/Berlin apply as before. - When an update is available, the version in the sidebar becomes a highlighted, gently pulsing Update to available that leads to Installation › Updates (for those who may install updates; no animation with reduced motion).
- Restore explorer: a mailbox's calendar has a month view, day by day across all its calendars
or one, and a series in the list opens onto its occurrences, derived from the stored pattern
with the changed occurrences in place (new
GET /api/v1/snapshots/:id/calendar, audited assnapshot.calendar.read). - Restore explorer: one entry per person. A person's mailbox, OneDrive and IMAP account are
grouped; the second column shows Mail, Calendar, Contacts and OneDrive, each with its own
restore points. SharePoint and Teams sites have a section of their own, an orphaned OneDrive
is listed under its owner's name as "Account deleted". The list comes a page at a time from
the new
GET /api/v1/snapshots/people(keyset paging, search over names, addresses and account ids, type filter, all in the database), so tenants with 10,000+ people never load everyone at once. The snapshot objects carryownerKeyandownerName. - Mail job editor: the first question is what the job should do, back up, archive or both (both is the default for a new job). While it archives, the editor names the capture (Exchange journaling) with the way to its setup and suggests backing up every 8 hours; backing up alone names the gap between two runs.
Changed
- A red restore check of a guest shows the error it found (for example restic's) in the guest list, Recovery readiness and on the guest page, instead of only "blocks did not match or could not be read".
- /virtualization: the State column keeps its width and height while a backup reports progress; long messages are cut, with the whole text in the tooltip.
- Storage integrity: a pack the storage check could not read (a download cut off
mid-transfer, a reset connection, a timeout, S3 asking to slow down, or denied access) is
no longer shown or rated as damaged. Recovery readiness › Storage integrity lists it as
"temporarily unreadable" with the error, how many consecutive checks could not read it,
and when first and last; every later check reads it first, and "Re-check these packs"
reads only the flagged packs again. Objects using it are rated "Attention"; they become
"Not restorable" only when a pack is proven damaged (missing, wrong size or checksum) or
could not be read in 3 consecutive checks over at least 24 hours. When the packs read
intact again, the storage finding ends and the object's restore checks rate it again. The
public API's
storageIntegrity.statehas a new value,unreadable. - A run that was stopped on request is
cancelled(machines: a new run and task status); it raises no failure alert and does not count as the last backup. - Database: index
protected_objects (tenant_id, user_id)for a person's objects. - Restore explorer: calendar events sort by when they start (newest first, or upcoming first) instead of by subject, with When, Location and Organizer columns.
- Exchange backup: an event's metadata records its location and the end of a series range.
Fixed
- Proxmox VE: after the primary repository changed with "keep existing backups where they
are", every container with an existing repository failed with
repository does not exist(exit 10): the server trustedrepository_ready_atand never checked the repository was on the new primary. Before a backup gets its restic credential the server now checks it (once per run). Missing everywhere, a new repository and a new backup chain begin, visible on the guest page ("New backup chain started on …: the previous repository was not found at …") and in its history, audited aspve.repository.reinitialized; the earlier restore points become unavailable (no restore, no check, not counted for readiness, skipped by retention). Still on a retired target, the backup is refused with a clear message instead. Restores read the repository from whichever target holds it. One database migration. - Storage: "Keep existing backups where they are" and promoting a copy are refused
(
storage-switch-blocked-by-repositories; the dialog offers "keep" disabled) while containers or file shares have restic repositories, which only "move" takes along. - Storage: a "move" now copies the restic repositories of containers, machines and file shares (from the target that holds each one), verified by size and by SHA-256 like packs, waits for running container backups and file share runs before it switches, and retention of containers and file shares waits for it. It also copies the manifests of VM and container restore points, which it treated as pruned before. A retired location is no longer removable while it alone holds a container's or file share's repository.
- Proxmox VE backups ended with "TASK WARNINGS: 1" and restow-pve reported them as failed although the restore point was committed: restow-pve asks vzdump for no notes any more, the storage plugin takes a notes update as a no-op, and a backup vzdump ends with warnings after its restore point was committed counts as "Succeeded with warnings".
- restow-pve's late task result was refused with "404 Task not found" once the server had closed the task (by the run's own end, the worker, or its expiry during an outage): the server takes it and settles the run (a committed restore point always counts as succeeded), a task whose run is still open no longer expires, and restow-pve keeps a result the server could not take and sends it again with the next heartbeat instead of losing it.
- The storage check read each pack once: a download that broke off mid-transfer (Node's "aborted", which the S3 client does not retry) made a ~65 MB pack "unreadable", the page said "2 packs are damaged … No intact copy exists in any repository", and the mailbox was rated "Not restorable" although a restore check read 20 of 20 items back byte-exact. The check now reads a pack again after a transfer error, whole, up to 3 times (2 s and 8 s apart), hashing as it streams into a buffer of the pack's recorded size. Reads from S3 (restores and restore checks included) resume a body that breaks off from the first missing byte, pinned to the object's ETag.
- The maintenance and update banner covered the top of pages that size themselves to the window: the layout now accounts for the banner's height, also when it wraps to several lines.
- The update progress said "Downloading {image}" (and "{file}", "{services}"): those steps now name the version instead of a placeholder the updater never fills.
- Switching the tenant on a job's page opens the jobs of that kind in the new tenant instead of saying that the job does not exist; any detail page gives way to its list.
Upgrade notes
New image and six database migrations, which run in seconds whatever the size of the installation:
0053_pve_repository_chains: a container's new backup chains (pve_guests.repository_chains) andpve_snapshots.unavailable_at.0054_pve_runs_042: the Restow server's own VM (pve_guests.restow_server_detected,restow_server_flagged,self_backup_confirmed_at),pve_snapshots.verify_requested_atand the installation's Proxmox VE settings (settings.pve_settings).0055_pack_read_failures: a new, empty table. Packs an earlier storage check reported unreadable count as temporarily unreadable after the update and are read again first by the next check; no full check is needed.0056_cancel_and_fleet_update: cancel requests andcancelledstates for tasks and runs, a node's paused queue, "update now" for nodes and machines, the phasefleetof the job-start pause,settings.planned_update.0057_explorer_people: an index on the protected objects of a person in a tenant, for the restore explorer's list of people.0058_installation_time_zone:settings.time_zone. An existing installation has no time zone of its own after the update:RESTOW_TIME_ZONE, elseTZ, else Europe/Berlin applies until an owner chooses one under Installation › Server & operation › Server (docs/UPDATING.md, "The installation's time zone").
No new required environment variables, and the docker-compose.yml of 0.4.0 stays.
- Proxmox VE nodes should run restow-pve 0.4.2: it reports the Restow server's own VM, keeps its heartbeat going while a backup runs (for Stop and the progress) and no longer reports backups with "TASK WARNINGS" as failed. The fleet update after the server update installs it on every reachable node; the Updates tab names the command for the others.
- Stopping a running backup needs restow-pve 0.4.2 on the node and agent 0.4.2 on the machine; an older node ends a cancelled backup at its next upload (the server refuses it), an older agent finishes it. Both update themselves with this release.
- "Keep existing backups where they are" (and promoting a copy) is refused while containers or file shares have restic repositories; change the primary repository with "move", which now takes them along.
- Storage check reports from before 0.4.2 are read again: packs they listed as unreadable count as temporarily unreadable, not damaged, and the next check reads them first. An object rated "Not restorable" only because of such a pack is rated "Attention" until that check.
- Integration API contract 1.5.0 (additive):
storageIntegrity.statecan beunreadable. - Rollback: the migrations only add a table, columns, indexes and enum values and widen a
check; 0.4.1 starts with them, but runs and tasks in the state
cancelledare unknown to it.
Known issues
- A file share whose repository went missing after a storage change is not yet healed the way a container's is (a new backup chain); this comes in 0.4.3.
- The fix for the maintenance and update banner covering the top of window-sized pages was not verified visually in a browser.
- The new parts were tested against a simulated Proxmox VE API, S3 store and Microsoft Graph API only, never against a real Proxmox VE cluster, S3 provider or Microsoft 365 tenant.
- The restore explorer's list of people groups the tenant's protected objects in the database for every page it serves; that is fine up to about 10,000 people per tenant and slower beyond.
- The known issues of 0.4.1 still apply.
Verification
Before the tag, on the release branch (main plus the 0.4.2 workstreams): lint and typecheck of all workspaces, the i18n tests, the server installer tests, the v1 OpenAPI test, gofmt, go vet and the Go tests of the agents, the Perl tests of the Proxmox VE plugin, and the unit and Postgres suites of every changed package on Postgres 16. The release smoke runs in the release pipeline; no real Proxmox VE cluster, S3 provider or Microsoft 365 tenant was used before the tag.
Release pipeline, 2026-10-21:
| Check | Result | Duration |
|---|---|---|
| 1. Install: images, compose up, migrations (new install and upgrade) | PASS | 92s |
| 2. Health endpoints, worker and scheduler registered | PASS | 0s |
| 3. Passkey sign-in E2E with a virtual authenticator, tenant creation, i18n de/en | PASS | 2m 07s |
| 4. Microsoft 365 backup and restore against the dev tenant | SKIPPED | 0s |
| 5. IMAP backup and restore against Dovecot, hash comparison | PASS | 8s |
| 6. Journal receipt, chain verification, export | PASS | 3s |
| 7. Standalone restore with restow-restore from the storage of check 5 | PASS | 25s |
| 8. Storage targets: S3 (Garage), local path, mounted directory | PASS | 20s |
| 9. Image scan (Trivy) and dependency audit (pnpm audit) | PASS | 20s |
| 10. Endpoint backup: the agent enrolls, backs up a folder, restores it | PASS | 12s |
| 11. Mail import (folder and upload) and export (EML ZIP, MBOX), compared by hash | PASS | 10s |
| 12. File share backup against Samba: back up, browse, ZIP, restore with permissions, copy job | PASS | 2m 15s |
11 of 12 checks passed in full, 0 partly, 1 skipped, 0 failed. The full report, including the reason for every skipped or partial check, is attached to the release as smoke-report.md.
The Community images were smoke-tested on their own (smoke-report-community.md, smoke-report-community-arm64.md): amd64 PASS with gaps: 9 of 12 checks passed in full, 0 partly, 3 skipped (see the reasons below); arm64 PASS with gaps: 9 of 12 checks passed in full, 0 partly, 3 skipped (see the reasons below).
Release notes of 0.4.2 on GitHub ↗
0.4.1 beta · 16 October 2026
Restow 0.4.1 counts the restic repositories of machines, file shares and Proxmox VE containers in the stored bytes, instead of the chunk store alone, and says when a running container backup shows no progress because its node runs an older restow-pve. One database migration.
Breaking changes
None. physicalBytes of GET /status and of usage in GET /storage now include the
restic repositories (see Fixed); the fields and their types stay.
Changed
- Repositories › Usage: the chart is called "Chunk store per day", and its note says that the restic repositories keep no history and are not part of the growth.
Fixed
- Repositories › Usage, the Stored tile of the dashboard, the statistics and
GET /statusandGET /storageof the API counted only the packs of the chunk store: a tenant with 10 GB of container backups saw "Stored 10.9 kB" and "0 B backed up". Stored now adds the restic repositories of machines, file shares and Proxmox VE containers, and the usage card shows how much of it is in the chunk store and how much in restic repositories. The deduplication saving, the growth and the chart stay figures of the chunk store, and say so; "Protected data" names the sources it does not include instead of "No completed backup yet". The dashboard tile lists the repositories on a line of their own instead of "0 B backed up". - The size of a container's restic repository was not recorded. The restic route now measures it once at the first upload after the update, then counts every upload and deletion, and the worker measures it again after a prune. A view never lists a repository (it may live on NFS).
- A running container backup on a node with restow-pve before 0.4.0 showed "No progress reported yet" for its whole run. It now says that progress needs restow-pve 0.4.0 on that node, which version the node runs, and to update the node.
- restow-pve asked for an update only six hours after it started, so a restarted node kept an old version for hours. It now asks two minutes after the start, then every six hours; the update still waits until no backup or restore runs.
Upgrade notes
New image and one database migration, 0052_pve_guest_repository_bytes
(pve_guests.repository_bytes and repository_measured_at), which runs in seconds. No new
required environment variables, and the docker-compose.yml of 0.4.0 stays.
- The repository of a container counts under Stored from its next backup on, of a machine or file share as before (from its next backup or retention run); until then it counts as zero.
- Nodes pick up restow-pve 0.4.1 by themselves, within two minutes of their next start or within six hours, while no backup runs.
- Rollback: the migration only adds two columns; 0.4.0 runs with them.
Known issues
- A repository's size has no history: the statistics count the current size of a restic repository at every moment of a period since its machine, share or container was added, so the growth of Stored there comes from the chunk store only.
- Uploads that finish while the first measurement of a repository lists it may be left out until the next prune or retention run measures it again.
- "Protected data" still counts the chunk store's sources only; the logical size of restic backups is not added up.
- The known issues of 0.4.0 still apply.
Verification
Before the tag, on the release branch (main plus the 0.4.1 fixes): lint and typecheck of all workspaces, the server installer tests (679), gofmt, go vet and the Go tests of the agents, and the full unit and Postgres suites on Postgres 16: packages/db 195, packages/i18n 273, packages/core 2311 (6 skipped), apps/scheduler 73, apps/api 3816 (5 skipped), apps/web 4124, apps/worker 529, ee/api 247, ee/licensing 59, ee/web 176, ee/worker 5, packages/cli 34, the demo seed 208; 141 Postgres test files, none skipped. A container backup with the real restic binary checked the size the restic route counted against the repository. The re-measurement after a prune ran in no test with a real repository. The release smoke runs in the release pipeline; no Microsoft 365 tenant was used before the tag.
Release pipeline, 2026-10-16:
| Check | Result | Duration |
|---|---|---|
| 1. Install: images, compose up, migrations (new install and upgrade) | PASS | 91s |
| 2. Health endpoints, worker and scheduler registered | PASS | 0s |
| 3. Passkey sign-in E2E with a virtual authenticator, tenant creation, i18n de/en | PASS | 2m 08s |
| 4. Microsoft 365 backup and restore against the dev tenant | SKIPPED | 0s |
| 5. IMAP backup and restore against Dovecot, hash comparison | PASS | 9s |
| 6. Journal receipt, chain verification, export | PASS | 3s |
| 7. Standalone restore with restow-restore from the storage of check 5 | PASS | 24s |
| 8. Storage targets: S3 (Garage), local path, mounted directory | PASS | 19s |
| 9. Image scan (Trivy) and dependency audit (pnpm audit) | PASS | 18s |
| 10. Endpoint backup: the agent enrolls, backs up a folder, restores it | PASS | 11s |
| 11. Mail import (folder and upload) and export (EML ZIP, MBOX), compared by hash | PASS | 8s |
| 12. File share backup against Samba: back up, browse, ZIP, restore with permissions, copy job | PASS | 2m 19s |
11 of 12 checks passed in full, 0 partly, 1 skipped, 0 failed. The full report, including the reason for every skipped or partial check, is attached to the release as smoke-report.md.
The Community images were smoke-tested on their own (smoke-report-community.md, smoke-report-community-arm64.md): amd64 PASS with gaps: 9 of 12 checks passed in full, 0 partly, 3 skipped (see the reasons below); arm64 PASS with gaps: 9 of 12 checks passed in full, 0 partly, 3 skipped (see the reasons below).
Release notes of 0.4.1 on GitHub ↗
0.4.0 beta · 16 October 2026
Restow 0.4.0 backs up SMB and NFS file shares from the server, without an agent on the
file server, and restores them with their permissions. It adds a notification matrix per
channel and event, shows the progress of running Proxmox VE backups, lets an update wait
for or cleanly stop the backups in progress, and moves the database to another disk from
the web interface. It adds immutable backups (S3 Object Lock per repository, a deletion
delay for directories), ransomware detection on every new restore point with a retention
hold while a finding is unresolved, boot tests and file restore (Preview) of Proxmox VE VM
restore points, backup and restore of SharePoint Online sites, agent policies, and file
restore for the restore role. S3 endpoints saved from now on must use HTTPS. Seventeen
database migrations run at start; file share backup and the database move need the 0.4.0
docker-compose.yml (run the 0.4.0 installer again after the update, see Upgrade Notes).
SharePoint needs an added permission and a new admin consent per tenant.
Breaking changes
Nothing stops working on update: existing compose files, webhooks, notification recipients, API clients and agents keep working as before. What behaves differently:
- Notifications follow the matrix. Every channel (the bell, each webhook, each
notification recipient) now keeps the events it gets, and an alert rule no longer sends
an event to a channel whose column has it off. Migration 0038 fills every column with
exactly what the channel received before, so nothing changes on update. Afterwards:
- The webhook dialog no longer has an event picker. Choose a webhook's events under Integrations › Notifications.
- A webhook created after the update (in the interface, or through the API without
events) starts with the defaults of its format: a Discord, Slack or Teams webhook gets errors and warnings, a signed JSON (restow) webhook everything. A rule that sends other events to it delivers them only once they are switched on in the matrix. - The webhook stream keeps its event names (
job.failed,job.completed,verify.completed) and payloads.backup.succeededis the matrix's name for what the stream callsjob.completed(an alias, not a rename); a webhook subscribed tojob.completedkeeps getting it.eventson create and update still means the subscribed stream events; the session API also returns the webhook's column asnotifyEvents. - The four flags of a tenant's notification recipients (
notify_job_failures,notify_weekly_report,notify_readiness_red,notify_license_updates) are replaced by an event list. The tenant API still accepts and returnscategories; a recipient now also carriesevents. "Licence and update notices" never sent anything and is no longer returned. Only scripts that read these columns from the database directly need a change.
- New installations start the mounter.
install.shnow starts the mounter (compose profilemounts, it holds the Docker socket) by default, because file share backup runs through it. Scripted installations that do not want it pass--no-mounter(or--without-mounter). Running the installer again on an existing installation keeps that installation's choice. - Installer guard. Once the database was moved to another disk,
install.shrefuses (exit code 8, nothing changed) to put in place or start adocker-compose.ymlthat does not readRESTOW_POSTGRES_DATAfrom.env, so an older release file cannot start the stack without its database. - Building from source needs Node.js 22.12 or newer (
enginesinpackage.json; commander 15 requires it). The Docker images run onnode:22-slimas before; nothing changes for Docker installations.
Added
Unless marked otherwise, everything below is part of every edition (the Community core).
File shares (SMB and NFS)
- File share backup, in the menu next to VMs & containers. Restow mounts an SMB share
(Windows Server, Samba, a NAS) or an NFS export read-only for the length of one run, in
a short-lived runner container started by the mounter, and backs it up with restic into
a repository of its own (
file-shares/<share id>/on the tenant's primary repository). No agent on the file server, and the api and the worker are never restarted. NTFS permissions (and NFSv4 or POSIX ACLs, best effort) are kept in each restore point. Open, unreadable and offline files are skipped and listed in the run, which then ends "with warnings". SMB 2.1 to 3.1.1; SMB1 and NTLMv1 are refused. Requirements (the mounter, thecifsmodule or the NFS client on the Docker host, the ports, the account) are in docs/FILESHARES.md. - Add file share tests the connection and whether permissions are readable; the password is sealed with the tenant key and never shown again. Backup jobs of the kind File shares: schedule (at most hourly), folders, file types to skip, retention (daily, weekly, monthly). Tenant admins manage their own shares; a share on a private or loopback address needs a provider admin's approval or the installation switch Tenants may use private networks.
- Restore from a restore point (browse, search, versions): download as ZIP, restore to a new folder, to the original location (overwrite, keep both, skip) or into another share, with permissions where the target allows it. Restores into a share need Allow restore to this share on it (off by default).
- Restore checks: every new restore point is checked by reading up to 20 sampled
files back and comparing them, and the repository gets a weekly
restic checkof 5 % of its data; Recovery readiness rates a share by its newest restore point. A restore check of a restore point without samples (every file changed in the hour before the run) rates yellow and says why. - Copy jobs: on a schedule, the newest restore point of share A that passed its restore
check is written into a folder of share B, in mode
overwriteormirror(mirror deletes only inside that folder; refused into a share root or the source share, and stopped when the restore point has fewer than half the files of the last copy). A copy job keeps no versions and is labelled as not being a backup. - Budgets per share, per tenant and as a default (off by default; provider admins): a warning at 80 %, no new backups at 100 %.
- File shares count in the status tab, the start page, Recovery readiness, warnings,
History, the statistics and the provider view, with the alerts
file_share_backup_failedandfile_shares_without_job. - Enable network shares (Installation › Default repository & network shares, and the notice on the File shares pages) starts the mounter of an existing installation through the opt-in updater, for the provider owner, with a recent sign-in and an audit entry. File share runners, on the same page, sets how many runs go at a time and the memory per run.
Immutable backups
- Immutability per repository, on each repository's card (Repositories › Change
immutability), off until an administrator turns it on. What is locked: packs,
committed manifests and wrapped keys of the chunk store (Microsoft 365, IMAP, Proxmox VE
disks) and the
data/,snapshots/,keys/andconfigobjects of every restic repository (machines, file shares, Proxmox VE containers). Not locked: checkpoints, staging, exports, downloads, resticlocks/andindex/files (restic repair indexrebuilds the index from the locked packs). See docs/STORAGE.md, "Immutable backups". - S3 Object Lock (
complianceorgovernance, 1 to 3650 days): turning it on checks the bucket's Object Lock configuration and refuses a bucket without it. Every locked object is written with a retain-until date of the period plus 7 days; the daily retention job renews the lock of every object still in use once the last complete renewal is 6 days old, so data that new restore points reuse stays locked for at least the period. A renewal that was interrupted continues where it stopped. Retention, garbage collection and restic prune treat a deletion the storage refuses as "not yet": the restore point stays visible and restorable, and space comes back once the lock has run out (up to the period plus 7 days later). Restow never sends the governance bypass header. - Deletion delay for directories (the Docker volume, an NFS share): Restow's own code refuses to delete or overwrite a locked file until the period has passed since it was written. It protects against misuse of Restow (a stolen administrator account, a retention policy shortened to delete restore points), not against someone with access to the server or the share, and the interface says that it is not immutability.
- Verification: the daily retention job and Verify now ask the storage for the
retention of a sample of recent objects; objects without a lock or in a weaker mode, a
renewal that is overdue or failed and deletions waiting for their lock are shown on the
repository card, the dashboard's storage tile and, as
immutability_warning, in the provider view. Restore points of the restore explorer and of a machine show "Immutable until ", green only for a COMPLIANCE retention the storage itself reported. - Who may change it: raising the protection is open to whoever may configure the
repository; lowering it (fewer days, a weaker mode, off) is for the provider owner with a
sign-in from the last ten minutes and applies only to data written from then on. Every
change and verification is audited (
storage.target.immutability_changed). - What it protects against: an attacker on the Restow server, someone holding Restow's
storage key, a stolen administrator account, a shortened retention policy. What not:
someone who controls Restow can stop new locks and the renewal (what is locked stays
locked until its date); a GOVERNANCE lock is lifted by any account with
s3:BypassGovernanceRetention; delete markers and newer versions can hide locked data (the locked versions stay and can be brought back); the master key is not in the bucket.
Ransomware detection
- Every new restore point is judged for signs of ransomware from data the backup already has, every two minutes, for runs of the last 72 hours: the amount of change (new and changed files, bytes added) against the object's own history, mass deletions, renamed extensions, known ransomware extensions and ransom notes (versioned lists, matched on the server), the entropy of sampled new files, and the compression and deduplication ratio of the run's new data. The signals apply per kind of object: machines, file shares, Proxmox VE containers and VMs, mailboxes, OneDrives, SharePoint sites and IMAP accounts (deletions, renames, names and entropy exist only for machines, file shares and OneDrives). A signal without data says "not available", never clean. It never reads the data a second time, never blocks a backup and is no virus scanner. See docs/RANSOMWARE.md.
- Suspicious badge (red) on the restore point and the object in every restore view; when the newest restore point is suspicious, the restore views offer the newest one without findings. A click shows each finding with its numbers and the state of every signal.
- False alarm: technicians and administrators mark a finding as a false alarm with a
note, or take it back (audited,
suspicion.false_alarm,suspicion.false_alarm_revoked); the restore point then counts as clean and joins the baseline. - Retention hold: while an object has a suspicious restore point nobody resolved,
retention,
restic forgetand prune delete no restore point of that object, so clean older restore points are not pruned while encrypted ones pile up. Backups go on and quotas still apply. Only an administrator ends a hold, never a later clean restore point: Mark as false alarm, or Release hold (administrators, recent sign-in, auditedsuspicion.hold_released). The object shows "Retention paused" until then. - Notifications:
backup.suspicious(error, once per attack episode, says that retention is paused) andbackup.suspicious_hold(warning, when a hold is still open after 14 days and every 14 days after), in the bell, the rules and the webhooks. - Installation › Ransomware detection: the thresholds of every signal, each signal's
switch, the retention hold's switch and its reminder days (
settings.ransomware.updated; the provider owner changes them, every provider admin reads them). - The activity report (deletions, renames, names, entropy) needs the endpoint agent 0.4.0 on machines; the compression signal needs the agent 0.4.0 on machines and restow-pve 0.4.0 for containers. File shares get it with the 0.4.0 runner, VMs and Microsoft 365 objects with the server.
Notifications
- Notification matrix under Integrations › Notifications (#76): the events as rows,
grouped and marked by severity; the bell, every webhook and every notification recipient
as columns, each with the quick choices "Errors only", "Errors and warnings" and "All".
Saving is written to the audit log (
report.matrix_updated). Installation events (an update is available, low disk space) are rows for provider admins only. - New events
backup.succeededandverify.completedfor mail and the bell (off in the bell by default), andinstallation.disk_low(see Updates and the database).
Proxmox VE
- Progress of a running backup (#51): VMs & containers, the inventory and the guest's page show a progress bar ("34% · 3.2 GB of 9.4 GB · 85 MB/s"). Needs restow-pve 0.4.0 on the node; a backup of an older helper shows "No progress reported yet".
- The helper's own lines in the run's log (#61): the run id, what restic added, what it could not read and the commit of the restore point now reach the log Restow keeps for the run. A backup that succeeded with gaps names the items restic could not read (the first 20).
- Proxmox VE jobs in Servers & clients › Jobs (#53), next to the machine jobs with a Type column: guests per job, schedule, last and next run, "2 of 5 guests done" while a round runs, and a backup started by hand as a row "Manual backup · " until it ends. New job is a menu with one entry per type; the Proxmox VE job form lists the VMs and containers to put into the job.
- Create recommended job (#52) next to every "no job" notice (jobs, inventory, sources, directory, VMs & containers) and "Protect everything without a job" on the dashboard's readiness card: a preview of what the click creates or adds to, then one job per kind (mail every 8 hours; machines per system and profile; one Proxmox VE job, daily at 22:00 in snapshot mode). A second click adds to the same job instead of making another. Admins only; recorded in the audit log.
- Files of a container in Servers & clients › File restore (#62): every container with a restore point is listed in the group "Proxmox VE containers"; browse a restore point and download files and folders as one ZIP, read by the server from the container's repository. The guest page links each container restore point there (Browse files). Reads and downloads are in the audit log.
- Files of a VM in Servers & clients › File restore (Preview): VMs with a restore
point are listed in their own group "Proxmox VE VMs", marked Preview, and the guest
page links them (Browse files). Each readable file system of the VM's disks is a
folder (
scsi0-p2,vg0-root): partitions of GPT and MBR disks and linear LVM logical volumes with ext2, ext3, ext4 (the journal replayed in memory), XFS, NTFS or FAT12/16/32. Browse and download files and folders as one ZIP; files that cannot be read are named in_not-restored.txtinside it. The server reads the disks from the tenant's chunk store with its own user-space readers in a short-lived, restricted child process; nothing is mounted, on the server or on the node. Not readable: btrfs, ZFS, ReFS, LVM thin, RAID, BitLocker and LUKS, NTFS-compressed and EFS files; the XFS log and NTFS's$LogFileare not replayed. Same routes, roles and audit actions as for containers (withkind: "vm"). The volume list asks to compare an important restored file with the original before relying on it. See docs/PVE.md, "Files of a VM". - Boot tests of restore points: a Proxmox VE job can boot the newest restore point of
each guest weekly or after every backup (off by default; a memory limit, default 8 GiB,
and a wait for the guest agent, default five minutes), and Boot test now on a restore
point asks for one by hand. The guest's node restores it as a new guest in the pool
restow-restore, isolates it before the start (every NIClink_down=1or an isolated bridge, passthrough devices, serial ports, bind mounts and the hook script removed), starts it, waits for the QEMU guest agent (a container: its init) and destroys it in every case; leftovers are removed when the service starts. One test per node at a time, never beside a backup, and only with enough memory and storage. Green when the guest agent answered, yellow when the VM only started, red when the restore, the start or the boot failed. Readiness rates the newest restore point by its boot test where there is one. New eventsboot_test.failedandboot_test.unproven. Needs restow-pve 0.4.0 on the node (restow-pve config --boot-tests,--boot-test-storage,--boot-test-bridge); the guest's page says when its node cannot. Requesting one is audited (pve.boot_test.requested). See docs/PVE.md, "Boot test". - A guest shows the average a follow-up backup adds (over the newest 10 restore points after the first backup) (#58).
Servers and clients
- Data folders a job misses (#59): the endpoint agent 0.4.0 reports which of a fixed
list of Linux data folders (
/opt,/usr/local,/srv,/var/www, Docker and Podman volumes, the PostgreSQL, MySQL and MongoDB folders under/var/lib) exist and hold files, names only. The job editor names the folders the job's machines really have and leaves out, and a machine's page offers Add this folder to the job. With older agents the notice of 0.3.5 stays. - A machine's page says what it runs as (container, virtual machine, physical, with or without a desktop) (#59).
- The file browser of a restore point says which folders it contains, with a link to the job's folders (#59). A machine's restore points say what they contain and what they added, like Proxmox VE guests (#58).
- Agent policies for servers and clients, on three levels: the installation
(Installation › Agent policy), the tenant (tenant page › Agents) and the backup job (the
job editor). Each level may override a setting unless the installation locks it; every
view says where a value comes from and whether it is locked. The settings: a backup
window (weekdays and a time range, also across midnight, in the zone of the job's
schedule) in which scheduled backups, catch-ups and retries may start (a running backup
continues, Back up now ignores it); catch up missed backups as soon as the
machine is on again or in the next backup window, optionally only up to a maximum age;
the upload limit of a job without its own (a job can also choose Unlimited,
which sets a policy's limit aside unless the installation locks it; a job whose limit is
empty keeps inheriting); only on AC power; and the number of automatic retries
(0 to 10). Without a policy agents behave exactly as before. The server writes the
effective policy into each machine's configuration (a new configuration version) and
audits every change (
settings.agent_policy.updated,tenant.agent_policy.updated,endpoint.config.changed). The page of a server or client shows the policy that applies to it, each setting with its source (default, installation, tenant, backup job or the machine itself) and whether it is locked, read-only (restore operators see it too), and why a due backup waits next to the next planned start: for the backup window, for an update to finish, for AC power, or to catch up in the next window. The agent 0.4.0 sends that reason with its heartbeat (waiting); for an older agent the page shows none. Window, catch-up, retries and the waiting reason need the agent 0.4.0; the upload limit and AC power reach older agents too. - Agents respect the update pause (#46): while an update pauses new runs, the agent
0.4.0 starts no scheduled backup, retry or catch-up and waits for the pause to end (the
configuration and the heartbeat say
startsPaused); the wait does not count as a missed or failed backup. A Back up now request stays queued on the server until the pause is over, also for older agents, andrestow-agent backup-nowon the machine waits with a message.restow-agent statusshows the policy and why a due backup waits. - File restore for the restore role: restore operators (membership role "restore",
tenant_restore) now open Servers & clients › File restore. They list the machines, read a machine's restore points and their immutability, browse them, download files as a ZIP and restore files onto the machine; for Proxmox VE they read the overview and a guest's restore points and browse and download files of container and VM restore points. Settings, tokens, jobs, backing up, verify and boot tests, restoring a whole guest, revoking or deleting and the repository password stay with the administrators (backing up answers 403), and the web app leaves out the links to pages they may not open. Every read, download and restore is audited with the operator as actor.
Jobs, live view and dashboard
- Running backups in the sidebar (#57): the Jobs entries and VMs & containers show a spinner with the number of running backups and the waiting ones beside it; the tooltip lists them, a click opens the run.
- Live run drawer (#54): the charts move with time between measurements, the numbers count up, the time left counts down, and a line says when the run last reported (or that Microsoft Graph throttles it). The browser tab's title shows the percent while the drawer is open.
- All tenants in Jobs (#63, Service Provider): under "All tenants", Jobs of Mail & SaaS and of Servers & clients list every tenant with its jobs per kind, what runs and waits, how the newest backups ended and the next run, with filters and sorting; a row opens the tenant's jobs read-only.
- Storage growth on the dashboard (#64): 1 week, 1 month, 3 months or 1 year, from 3 months on with the estimate as a band between the slowest and fastest growth, and when the repository runs full at this growth where its free space is known.
Microsoft 365
- The first backup of a large mailbox shows "First backup: X of Y items" over all attempts, with an estimated time left (#47). A OneDrive shows "X items so far" until it is fully listed.
- SharePoint Online sites: every directory sync finds the tenant's sites
(
/sites/getAllSites; sites of Microsoft 365 groups and Teams included, personal sites left to their owner's OneDrive), and each site is a protected object of its own, in the mail jobs like mailboxes and OneDrives. The protection rules have a sites mode: all sites (the default) or only the selected ones. A backup holds every document library (with the OneDrive engine: delta per library, optional versions, checkpoints), every list with its columns and each list item's field values as JSON, and the site's description; a library or list Graph refuses is reported and keeps its last copy. New sites get their first backup at once; a deleted site fails withgraph.site_unavailable. - Restore of SharePoint sites in the explorer (libraries, folders, files, lists, items): download as ZIP; files and folders into the original library or another library of a backed-up site of the same source ("Keep both", "Overwrite" with SharePoint keeping the old file as a version, "Skip existing"); list items into their original list as new items, writable fields only. Restore checks sample library files like a OneDrive. Sites appear in the status, count as no protected mailbox, and are seen by restore operators and administrators only. Not backed up: Teams chats, attachments of list items, site design, navigation and workflows; permissions are kept as information and never restored. See docs/MICROSOFT.md, "SharePoint Online".
Updates and the database
- Runs in progress during an update (#46): the install dialog counts what runs and waits in every tenant and asks: Stop them cleanly (the default: Microsoft 365 and mail runs save a checkpoint and stop, runs on agents and of file shares are waited for, 30 minutes at most), Wait until they finish (two hours at most) or Do not wait (as before). From the confirmation until the update is over no new run starts; schedules that fall due meanwhile run afterwards. Everyone signed in sees a banner; the Updates tab shows what still runs, Start now and Cancel.
- Installation › Database (#73): where the database keeps its data, its size, the free space under it and under the local default repository, and what the next update needs. With the opt-in updater, the provider owner moves the database to a directory on another disk: checked first (another local file system, ext4, XFS, Btrfs or ZFS, room for the data), then copied, compared file by file, test-started and switched, with automatic rollback on any failure. The old copy is kept and blocked from starting until it is removed. See docs/DATABASE.md.
- Low disk space warning (#73): every 15 minutes the api checks the disk under the database (through the updater) and under the local default repository; below a tenth free, and always below 5 GiB, the provider admins get a notice in the bell, and rules that list "Low disk space" send it, once per episode.
- Kept database dumps (#73): the Updates tab lists the updater's pre-update dumps; the provider owner can download one (one-time link, recent sign-in, audit log) or delete it.
API
- Integration API contract 1.4.0 (additive):
GET /api/v1/statushasfileShares(counts of file shares) and the readiness summarysharesWithoutJob, andeventsis optional when a webhook is created. Nothing was removed or renamed.
Changed
- Updates: the updater's preflight now adds up what an update writes (the dump, the
images, room for the migrations, plus
RESTOW_UPDATER_MIN_FREE_MBas a margin) and refuses an update that does not fit (blockerdisk_space, with the numbers), instead of requiring a fixed 1 GB (#73). - A Linux machine enrolled with a client token whose agent (0.4.0) reports a container or a virtual machine without a desktop gets the server profile, because it runs services; the machine's page says why (#59). Existing machines keep their profile.
- Adding a folder to a machine job narrows an exclusion of a parent folder: adding
Docker's volumes turns
/var/lib/dockerinto/var/lib/docker/*and!/var/lib/docker/volumes(#59). - Notification recipients: the categories in the tenant wizard and on the tenant page add or remove their events and leave everything else chosen in the matrix alone.
- The live channel stays open while the tab is in the background (closed after ten minutes, or when the browser freezes the tab), so a page no longer reloads when you come back (#54).
- Mailboxes and OneDrives are named by their person (display name, else address), not by their technical id, which moves to a tooltip (#55).
- The sidebar's Installation section folds away to its header; the choice is remembered in the browser (#56).
- A machine's danger zone reads "Stop backing up this machine" and says that nothing is deleted; revoked machines are hidden in the inventory until "Show revoked machines" and have their own group in File restore (#65).
- The machine job editor says that Proxmox VE VMs and containers are backed up by Proxmox VE jobs, with links (#53).
- Proxmox VE: sizes are named ("9 GB allocated"); "Waiting for the node" says since when, and turns amber while the node is disconnected.
- Container backups on Proxmox VE (restow-pve 0.4.0) let restic scan before it backs up
(no more
--no-scan), so there is a total to show progress against. - docs/ARCHIVE.md explains what journaling captures and misses compared with the mailbox backup, and when to use which (#48).
- Dependencies: major updates of the build and test tooling and of two libraries: vite 8, @vitejs/plugin-react 6, vitest 5, lucide-react 1.x, commander 15, archiver 8, @biomejs/biome 2.5.
- Performance on large installations: the "All tenants" view of Jobs looks up each object's newest backup through an index, reads four tenants at a time and keeps each tenant's figures for 30 seconds for every viewer (the page refreshes every 15 seconds while something runs, else every minute); the sidebar's Proxmox VE badge asks a slim endpoint instead of the whole VMs and containers overview; the ransomware judgement reads new restore points through partial indexes, judges batch after batch for up to a minute per pass and logs restore points that leave its 72-hour window unjudged; the reconciler of left-behind jobs runs in one worker at a time; a heartbeat no longer rewrites the machine's settings when its facts did not change.
- The file share search needs at least three characters of the name.
- Performance with the largest workloads; memory now stays bounded whatever the size:
- File share catalog: the first catalog of a share (or one after a gap) streams
restic lsinto the database in batches of 5,000 instead of holding every file in memory and writing it in one transaction. An interrupted pass stays pending and continues without duplicates. - SMB file share runner: the permission reuse streams the previous sidecar into a
compact index (about 50 bytes per file instead of about 190), so incremental runs of
shares with millions of files no longer run out of memory. Above a third of the
runner's memory limit the run reads all permissions instead
(
RESTOW_SHARE_REUSE_MAX_ENTRIESoverrides the limit). - SharePoint: a site run indexes the previous restore point once instead of scanning it for every list and library, so sites with thousands of lists and a million items no longer take quadratic time.
- Proxmox VE: the block maps of VM disks being browsed or restored are cached by size
(256 MiB by default,
PVE_BLOCK_MAP_CACHE_MB) instead of by count. - Object Lock renewal sends 32 requests at a time instead of 8
(
IMMUTABILITY_RENEWAL_CONCURRENCY) and continues an interrupted pass where it stopped instead of starting over.
- File share catalog: the first catalog of a share (or one after a gap) streams
Fixed
- A Proxmox VE backup running while Restow was updated failed: the node helper gave up after about 30 seconds, while an update restarts the api and runs migrations. The node (restow-pve 0.4.0) now keeps trying for five minutes (#46).
- A Proxmox VE backup run whose end never reached the server stayed "running" for 48 hours. It is now closed once the node (restow-pve 0.4.0) no longer lists it in its heartbeat (#46).
- restow-pve's own lines never reached the run's log in Restow: vzdump writes a backup provider's lines to the PVE task log only, not to the guest's log file (#61).
- Jobs of a worker that was killed (not stopped) stayed "running" until they expired, 23 hours for a backup, because nothing gave them back. The next worker now returns them to the queue two minutes after its start (#46).
- A job stopped by a restart or an update before its first checkpoint used up one of its attempts, and on its last attempt the explanation said it would continue although its checkpoint was discarded (#46).
- An update could fill the disk with its database dump and stop PostgreSQL midway, because the preflight only checked for 1 GB free (#73).
- Adding
/var/libto a machine job, as the 0.3.5 notice offered, still left Docker's named volumes out, because the default exclusion/var/lib/dockercovers them (#59). - The live run drawer's charts shrank to the last 80 seconds and grew back every ten seconds, because each live event replaced the run's measurements instead of adding to them (#54).
- Notification mail through the backup's app registration from a tenant that never consented to it reported "app not found" and asked to check IDs that were right. It now says that consent is missing in the sender's tenant and links the admin consent.
- The update dialog's confirm button was enabled while the runs in progress were still being read, so a quick click announced the update before the admin saw them. It now stays disabled until they were read (or reading them failed).
Security
No vulnerability was fixed. Security-relevant changes:
- Database dumps can be downloaded from the Updates tab. A dump holds the whole database, the encrypted secrets included: provider owner only, with a recent sign-in, through a one-time link, recorded in the audit log. Keep a downloaded dump as carefully as the master key.
- New installations start the mounter by default, which mounts the Docker socket (root on
the host); read docs/MOUNTS.md, or install with
--no-mounter. File share runners have no network but an internal one to the api, and write with a credential that is valid for one run and append-only. - Tenant admins can add file shares themselves; shares on private or loopback addresses need a provider admin's approval or the installation switch.
- Browsing and downloading files of a Proxmox VE container are recorded in the audit log
(
pve.snapshot.browsed,pve.snapshot.downloaded); a prepared download can be started once, within ten minutes, by the admin who prepared it. The same holds for files of a VM (kind: "vm"), which a reader in a restricted child process reads without mounting anything (threat model in docs/PVE.md). - S3 endpoints need HTTPS. A new or changed S3 endpoint over plain
http://is refused (endpoint_insecure), for provider admins and the installation default too, except tolocalhost,127.0.0.1and[::1]. Endpoints saved before andS3_ENDPOINTkeep working; their card, Installation › Default repository and every test say in red that they should move to HTTPS.S3_ALLOW_INSECURE=trueaccepts plain HTTP for test and development setups only. - Restore operators can now browse, download and restore files of machines and of Proxmox VE guests (see Servers and clients); every read, download and restore is audited.
- Lowering a repository's immutability and releasing a retention hold need a recent sign-in and are audited; lowering is for the provider owner only.
Upgrade notes
New image, seventeen database migrations (0035 to 0051), and a new docker-compose.yml for
file shares and the database move.
- Update as usual (the opt-in updater, or
docker compose pull && docker compose up -d). The api applies the migrations as it starts. With the updater, the install dialog now asks what to do with runs in progress; the default stops them cleanly. - Run the 0.4.0
install.shagain in the installation directory (README.md). It replacesdocker-compose.ymlwith the 0.4.0 file (checked against the signed SHA256SUMS), keeps the previous one asdocker-compose.yml.bak-<timestamp>and leaves.envas it is. Without it everything else works, but file share runs cannot start (the mounter reportsrunner_network_missing) and Installation › Database cannot move the database. A compose file kept by hand needs the changes of the release file: the database volume read from.env, the internalrunnersnetwork on the api, andRESTOW_MOUNTER_URL, the runner limits and therestow-mounter-sharedvolume on the worker. - For file shares, start the mounter if it is not running: Enable network shares in
the web interface (with the opt-in updater) or
docker compose --profile mounts up -d mounter. The Docker host needs thecifsmodule for SMB and the NFS client for NFS. - Proxmox VE nodes: restow-pve 0.4.0 updates itself (it checks every six hours and updates
while idle), or run
restow-pve updateon the node. Older helpers keep working without progress, without the five-minute retry window and without the list of active runs. - Servers and clients: the endpoint agent 0.4.0 updates itself unless the tenant paused agent updates. Older agents keep working without data folders, machine facts, the backup window, catch-up rules and retry count of an agent policy, and do not hold their own schedules during an update pause.
- Plain-HTTP S3 endpoints: a repository or default repository that uses
http://to a host other than the machine itself keeps working, but its card asks in red to move it to HTTPS, and changing its location needshttps://(docs/UPDATING.md). Test setups with an object store on the compose network setS3_ALLOW_INSECURE=truein.env(api and worker). - SharePoint Online: the backup's app registration needs the application permission
Sites.ReadWrite.All(Sites.Read.Allis enough to back up, not to restore). Add it to the app registration, then the customer's admin opens the source's consent link again (docs/ENTRA-SETUP.md). Until then mail and OneDrive keep working, and the directory sync warns that sites are neither found nor backed up (sites_unavailable). - Immutable backups with S3 Object Lock need a bucket created with Object Lock (and
versioning); it cannot be added to an existing bucket. The access key needs
s3:GetBucketObjectLockConfiguration,s3:PutObjectRetention,s3:GetObjectRetention,s3:ListBucketVersionsands3:DeleteObjectVersion, and should not haves3:BypassGovernanceRetention,s3:PutBucketObjectLockConfigurationors3:PutBucketVersioning. Nothing is locked until an administrator turns it on. - Ransomware detection is on after the update and judges backups of the last 72 hours; its retention hold is on as well, so a suspicious restore point pauses retention of its object until an administrator resolves it. Thresholds and both switches are under Installation › Ransomware detection.
- Database migrations, in this order:
0035_file_shares,0036_backup_job_kinds_share_copy,0037_backup_job_file_shares(file shares),0038_notification_matrix(it translates the recipient flags and webhook event lists of every tenant and drops the four flag columns),0039_pve_run_progress,0040_recommended_jobs,0041_pve_downloads(container downloads),0042_job_start_pause(the update pause),0043_disk_alerts(the disk warning state),0044_pve_boot_tests,0045_ransomware_detection,0046_sharepoint_sites,0047_immutable_backups,0048_suspicion_retention_hold(the release of a retention hold and its reminders, five columns onrestore_point_suspicions),0049_perf_indexes0050_agent_policies(the installation's and each tenant's agent policy,settings.agent_policyandtenants.agent_policy, both{}, which changes no machine's configuration) and0051_agent_waiting(endpoints.agent_waiting, what a due backup waits for as the agent reports it; empty until an agent 0.4.0 reports). They change the schema and backfill only the notification recipients and webhooks, a few rows per tenant, so they should take seconds on any installation, except0049_perf_indexesbelow. They ran on a 0.3.6 schema in the Postgres test suites; they were not timed on a production-size database. 0049_perf_indexesadds six partial indexes (jobs,endpoint_runs,file_share_runs,pve_runs,pve_tasks). Each build reads its table once while writes to it wait (the migrator cannot build them concurrently): seconds on most installations, about a minute per ten million rows on large ones (docs/UPDATING.md).- 0035 creates the
pg_trgmextension for the file share search when the database role may; an external PostgreSQL without it searches more slowly, with the same results. - New optional environment variables:
RESTOW_POSTGRES_DATA_TYPEandRESTOW_POSTGRES_DATA(where the database lives; empty means the volumepgdata; the updater writes them when it moves the database),RESTOW_MOUNTER_MAX_RUNNERS(hard cap of file share runners, default 8) andRESTOW_MOUNTER_RUNNER_MAX_MEMORY_MIB(default 16384). No new required variables. - Downtime: the containers restart and the migrations run, as for 0.3.6.
- Rollback: the migrations are not reversible; go back to 0.3.6 only with the database dump
taken before the update. After a database move, keep a compose file that reads
RESTOW_POSTGRES_DATA: the 0.3.6 file does not, and PostgreSQL then refuses to start on the old copy.
Known issues
- Not tested against real systems before the tag: a Proxmox VE node (progress, the helper's log lines, the five-minute retry window and the closing of dropped runs were tested against the simulated node of the test suites), the database move and the Enable network shares button through a real updater and its Docker helper container, and the new pages in a browser by hand (they are covered by component tests).
- File shares were tested against Samba in the release smoke tooling, not against Windows Server (NTFS permissions, Backup Operators, a file kept open in Excel), a Synology or TrueNAS share, real NFS exports (NFS is not in the smoke check) or a host with SELinux enforcing.
- File shares have no VSS over SMB (files open with an exclusive lock are skipped), no alternate data streams, DFS namespaces, Kerberos or SMB1; repositories live on the primary repository only; tenant users have no self-service restore of shares; scheduled reports have no file share block yet.
- While an update waits or runs, agents older than 0.4.0 do not hold their own schedules: a backup such a machine starts by itself during that time runs, and may fail if the api restarts under it.
- Files of Proxmox VE containers and VMs are downloaded as a ZIP; restoring single files into the running guest is not built. VM file restore is a Preview: btrfs, ZFS, ReFS, LVM thin, RAID, BitLocker and LUKS are not readable, and the XFS and NTFS logs are not replayed, so a crash-consistent restore point can lack the last writes; compare an important file with the original before relying on it.
- Immutable backups, SharePoint Online, boot tests, VM file restore and agent policies were tested against simulated services only (a fake S3 with Object Lock, a simulated Microsoft Graph, a simulated Proxmox VE node), not yet against a real S3 service with Object Lock, a real Microsoft 365 tenant or a real Proxmox VE cluster. The ransomware signals were tested with simulated attacks, not with live ransomware.
- Immutable backups: the installation default repository cannot be made immutable (give the tenant a repository of its own or an immutable copy); restic index files are not locked; a tenant whose retention schedule was deleted is not renewed; space of deleted data comes back only once its lock has run out; a file share whose repository is still locked cannot be purged before then.
- SharePoint: attachments of list items, site design, navigation, workflows and permissions are not restored; a deleted site or library is not recreated; list items go back only into their original list.
- In "This restore point contains", Windows paths are listed but do not open.
- Deleting a revoked machine together with its restore points is not possible yet.
- The known issues of 0.3.6 still apply.
Verification
Before the tag, on 2026-10-15, on the release branch (main plus the 0.4.0 branches): lint and typecheck of all workspaces, the build, every unit suite, the Postgres suites on Postgres 16 (129 test files, 1505 tests), the Go agent tests, the Perl plugin tests of the Proxmox VE integration, the server installer tests (679) and the CI and smoke tooling tests (241). Smoke check 12 (file shares: back up, browse, search, ZIP download, restore with permissions, copy job against Samba) passed in CI on the 040-fileshares branch (run 38668096804). The release smoke runs in the release pipeline; no Microsoft 365 tenant was used before the tag.
Release pipeline, 2026-10-16:
| Check | Result | Duration |
|---|---|---|
| 1. Install: images, compose up, migrations (new install and upgrade) | PASS | 94s |
| 2. Health endpoints, worker and scheduler registered | PASS | 0s |
| 3. Passkey sign-in E2E with a virtual authenticator, tenant creation, i18n de/en | PASS | 2m 08s |
| 4. Microsoft 365 backup and restore against the dev tenant | SKIPPED | 0s |
| 5. IMAP backup and restore against Dovecot, hash comparison | PASS | 8s |
| 6. Journal receipt, chain verification, export | PASS | 3s |
| 7. Standalone restore with restow-restore from the storage of check 5 | PASS | 24s |
| 8. Storage targets: S3 (Garage), local path, mounted directory | PASS | 22s |
| 9. Image scan (Trivy) and dependency audit (pnpm audit) | PASS | 19s |
| 10. Endpoint backup: the agent enrolls, backs up a folder, restores it | PASS | 12s |
| 11. Mail import (folder and upload) and export (EML ZIP, MBOX), compared by hash | PASS | 8s |
| 12. File share backup against Samba: back up, browse, ZIP, restore with permissions, copy job | PASS | 2m 15s |
11 of 12 checks passed in full, 0 partly, 1 skipped, 0 failed. The full report, including the reason for every skipped or partial check, is attached to the release as smoke-report.md.
The Community images were smoke-tested on their own (smoke-report-community.md, smoke-report-community-arm64.md): amd64 PASS with gaps: 9 of 12 checks passed in full, 0 partly, 3 skipped (see the reasons below); arm64 PASS with gaps: 9 of 12 checks passed in full, 0 partly, 3 skipped (see the reasons below).
Release notes of 0.4.0 on GitHub ↗
0.3.6 beta · 15 October 2026
Lets you protect Proxmox VE guests straight from the inventory, gives the disk space of the old backup index back without an API restart, shows a notice with a button when a service needs a restart, and updates the compose file of older installations when the installer runs again. One database migration.
Added
- Proxmox VE: protect guests from the inventory: Under Servers & clients, Inventory, every VM and container has its backup job as a choice (an existing job, "No backup job" or "New job…") and "Back up now". Guests can be ticked one by one, all guests of a node, or all guests without a job in one click, and then added to a job ("Add to job…") or backed up together; "New job…" opens the job dialog and puts the selection into the new job. The inventory's protection column is now called "Protection". Proxmox VE stays a preview. (Every edition)
- Restart required notice with Restart now: When a service needs a restart, Installation, Updates shows a "Restart required" notice with the reason, a "Restart now" button and the command to copy, in the installation's directory; provider admins see a notice above every page. "Restart now" goes through the updater, restarts the API, the worker or the scheduler only, never runs while an update is announced or running, and asks for a confirmation first. The owner of the provider team gets the button, and it needs the updater of 0.3.6; an older updater shows the command instead. Nothing restarts without a click. Nothing records a restart request yet; the notice and the button are in place for updates that need them.
- The installer updates an old docker-compose.yml: Running install.sh again on an existing installation updates docker-compose.yml to the release named in .env, checked against the release's signed SHA256SUMS, and keeps the previous file as docker-compose.yml.bak-<timestamp>. .env stays as it is. Changes of your own belong in docker-compose.override.yml.
Changed
- Backup index: the old table's space comes back without a restart: After the 0.3.5 conversion of the backup index has finished, the API gives the old table's disk space back by itself, within about 15 minutes. The API restart that 0.3.5 asked for is no longer needed.
- Installation, Updates: an available update stands next to the running version, in the same size and highlighted, instead of only in the status line.
- Installation, Network shares: when the mounter is not running, the command starts with the installation's directory; a compose file from before the mounter is named as such, with the installer command that updates it.
- Repositories, Add a server path: the dialog is wider and no longer scrolls sideways.
- docker-compose.yml and .env.example keep only the comments a reader must not miss; the reference for every variable is in the documentation (docs.restowbackup.com). Nothing in the two files changed otherwise.
- Dependencies: patch and minor updates, among them better-auth, drizzle-orm, hono, pg, nodemailer, radix-ui and vite, and the pinned GitHub Actions setup-node, upload-artifact and download-artifact.
Fixed
- Mounting an NFS export that does not exist on the server ended with a bare "No such file or directory". The message now names the export path ("The export path … does not exist on …") and how to find the right one: showmount -e, on a Synology NAS the path starts with the volume (/volume1/), and with NFSv4 some servers show their exports below a pseudo-root, in which case NFS version 3 helps.
Upgrade notes
- New image and one database migration (0034, a small table for restart notices), which runs in seconds. No new required environment variables.
- The "Restart now" button needs the updater of 0.3.6. An updater from before 0.3.6 shows the command instead until it runs the 0.3.6 image.
- Installations from before 0.3.0 whose compose file lacks the mounter service: run the 0.3.6 installer again to update it.
- If the conversion of the 0.3.5 backup index is still running: nothing to do; the space comes back by itself once the conversion has finished.
- Rollback: the migration is not reversible; go back to 0.3.5 only with the database dump taken before the update.
Known issues
- Nothing in Restow records a restart request yet; the notice and the button are in place for updates that need them.
- The Docker error text for a missing NFS export was taken from Docker's documentation, not from a real failed mount.
- The known issues of 0.3.5 still apply.
Verification
Before the tag, on the release branch (main plus the five 0.3.6 branches): lint and typecheck of all workspaces, the server installer tests (642), the CI and smoke tooling tests (208), and the full unit and Postgres suites on Postgres 16: packages/db 177, packages/i18n 259, packages/core 1981, apps/scheduler 65, apps/api 3279 (1 skipped), apps/web 3839, apps/worker 444, ee/api 243, ee/licensing 59, ee/web 176, ee/worker 5, packages/cli 34, the demo seed 208; 114 Postgres test files, none skipped. The release smoke runs in the release pipeline; no Microsoft 365 tenant was used before the tag.
Release notes of 0.3.6 on GitHub ↗
0.3.5 beta · 14 October 2026
Lets long first backups of large mailboxes continue after an interruption instead of running out of retries, says plainly how big a Proxmox VE guest's backups are and when a container backup is incomplete, warns when a machine job leaves out the folders where Linux services keep their data, and adds a restore operator role. It also stores the backup index (which items each backup contains) as item versions instead of a full copy per backup, which shrinks the database of large installations, and gives sign-in a database role of its own. One database migration, run automatically at start.
Added
- Restore operator role: A new role per tenant: the restore operator sees and restores every backup of the tenant without administering it (jobs, storage, users). Creating a user or an invitation now always needs a role to be chosen; no role is preselected any more.
- Proxmox VE: what a backup contains and what it added: VMs & containers and the inventory get a Backup column with what the newest restore point contains and what its run added ("9.4 GB in the backup, +2.5 MB new"), with the first backup on hover. The former Size column is now called Disk. On a guest's page, restore points show their type (first backup, incremental, full read), what they contain and what they added. Proxmox VE stays a preview. (Every edition)
- Proxmox VE: incomplete container backups are named: A container backup that restic could not read completely shows as "Succeeded with gaps" ("Last backup incomplete" on the guest's page) with the number of unreadable items, and counts as "Incomplete" in the inventory. (Every edition)
- Usual data folders in machine jobs and file restore: For Linux machine jobs and file restore, a notice names the usual data folders (/opt, /srv, /var/lib, /home and others) that the job's paths leave out, with a button to add them.
Changed
- Proxmox VE: Back up now and restore name the node's check-in: "Back up now" and restore say that the node starts the task at its next check-in; a second click says a backup is already waiting. The pages refresh every 5 seconds while a guest waits for its node or backs up. (Every edition)
- Long first backups continue after an interruption: A backup that is interrupted by its time limit (23 hours) or by a worker restart, after it saved a checkpoint, continues without using up one of its retries, for up to 21 days after it first started. The run view shows such an interruption as information, not as an error. Before, the first backup of a large mailbox that needed several days ended as failed after five interruptions, and the next run started over.
- Microsoft 365: a Graph request without an answer is retried: A Graph request that gets no answer within 5 minutes is retried, instead of hanging until the job's time limit. Requests and throttling waits end with the job.
- Backup index stored as item versions: A backup no longer copies every item of a mailbox or OneDrive into the database. Restow keeps one row per version of an item, so an unchanged item costs nothing on later backups. On an installation with 14 million index rows (25 GB), the index needs about 0.5 to 1.5 GB afterwards. The worker converts existing backups one at a time after the upgrade; that conversion was tested on generated histories, not yet on a production database of 25 GB.
- Installation: "Default repository" and "Network shares" are one page again. Old links to the network shares open it.
- The worker gets 60 seconds to save checkpoints when it stops (stop_grace_period in the Compose files, drain time 50 seconds, WORKER_SHUTDOWN_TIMEOUT_MS).
- File restore: the file list has a fixed height and scrolls inside, so the path bar and the actions stay in place.
Fixed
- A first backup of a large mailbox that needed several days ended as failed after five interruptions, and the next run started over.
- "The run was interrupted" said nothing was lost also after the last attempt, when the checkpoint is discarded.
- The database of installations with many item backups (Microsoft 365) grew by the full item count with every backup, until the disk ran full.
- Archive: header, storage-protection notice, search and results had no space between them.
- After an update, the maintenance page kept saying "Restow was updated, this page reloads now" whenever the application was down later (for example after a reboot with a full disk). After 10 minutes it now says the server is temporarily unavailable.
- The browser console warned on every page load that three preloaded fonts were not used within the first seconds. The fonts are no longer preloaded.
- Forms reported no error for a missing field inside a list (for example the role of a new user in the setup wizard).
Security
- Database roles (hardening, no known exploit): up to 0.3.4 the application's database role could also read and write the tables outside row-level security, among them the sign-in tables, the installation settings, the license and the job queue. A flaw in tenant-scoped query code could therefore have reached data beyond its tenant. 0.3.5 moves sign-in to a role of its own and leaves the application role only tenant data and adding jobs. Affected: up to 0.3.4. Nothing to do beyond updating; the migration changes the grants of an existing installation.
Upgrade notes
- New image and one database migration (0033), which only creates a table and drops two indexes and takes seconds on any database size. No new required environment variables; DATABASE_AUTH_URL is optional.
- After the start, the worker converts the existing backup index in the background, one backup at a time (an estimated 15 minutes for 14 million rows); backups, browsing and restores keep working meanwhile. When the worker log says "manifest index conversion finished", restart the api once (docker compose restart api) to give the old table's space back.
- On a nearly full disk, free space first: the conversion writes the new index before the old one is released, and the updater writes a database dump before it updates. A Compose file you keep yourself needs stop_grace_period: 60s on the worker service.
- Rollback: the migration is not reversible; go back to 0.3.4 only with the database dump taken before the update.
Known issues
- The background conversion of the backup index was tested on generated histories, not yet on a production database of 25 GB; how long it takes there is an estimate.
- The known issues of 0.3.4 still apply.
Verification
Before the tag, on the release branch: CI green (lint, typecheck, build, Docker images, Go agent, licenses, secret scan, and the unit and Postgres suites on Postgres 16 with password authentication). No Microsoft 365 tenant was used before the tag.
Release pipeline of 14 October 2026: 10 of 11 checks passed in full, 1 skipped, 0 failed. The skipped check is number 4, Microsoft 365 backup and restore against the dev tenant. The Community images were smoke-tested on their own: 8 of 11 checks passed in full and 3 were skipped, on amd64 and on arm64. The full report, with the reason for every skipped check, is attached to the release as smoke-report.md.
Release notes of 0.3.5 on GitHub ↗
0.3.4 beta · 11 October 2026
A hotfix: Proxmox VE container backups no longer fail on the lost+found folder of an unprivileged container, VMs & containers show when a backup waits for its node or runs, and a short hiccup while the browser re-checks the sign-in no longer replaces the whole interface with "Restow could not be loaded". No database migration.
- Proxmox VE: containers that are not privileged: Backups of unprivileged containers on ext4 no longer fail on the lost+found folder. A container backup that restic could not read completely (exit code 3) keeps its restore point and names the unreadable items. VMs & containers show when a backup waits for its node or runs. After the server update, run restow-pve update on every node. (Every edition)
Upgrade notes
- New image and a new restow-pve helper for the Proxmox VE nodes; no database migration. After the server update, run restow-pve update on every node so the container fix applies there; the server part works with older helpers too.
Known issues
- The inventory's node rows do not yet show that a backup waits for its node or runs; VMs & containers does.
- The known issues of 0.3.3 still apply.
Verification
Release pipeline of 11 October 2026: 10 of 11 checks passed in full, 1 skipped, 0 failed. The skipped check is number 4, Microsoft 365 backup and restore against the dev tenant. The Community images were smoke-tested on their own: 8 of 11 checks passed in full and 3 were skipped, on amd64 and on arm64. The full report, with the reason for every skipped check, is attached to the release as smoke-report.md.
Release notes of 0.3.4 on GitHub ↗
0.3.3 beta · 10 October 2026
Shows the Proxmox VE nodes with their VMs and containers in the inventory, mounts an NFS share straight from the path field of a repository, and calls the place where backups are stored "repository" everywhere. No database migration.
- Repositories: an NFS address in the path field: The path field of a directory repository accepts an NFS address (192.168.1.10:/export/backup, nas.local:/volume1/restow or nfs://). The provider owner can test and mount it right there, and Restow fills in the path; when jobs are running, the mount waits until none runs and can be cancelled. A menu entry Repositories sits under your organisation, and the interface says repository everywhere instead of storage location. Tested against the mounter's stand-ins, not yet against a real NFS server; while a mount waits, new jobs still start.
- Proxmox VE nodes in the inventory: Under Servers & clients, Inventory, every Proxmox VE node is a row you can expand, with its online state and counts (protected, failed, no job, not backed up yet); expanded, each VM and container shows its status, backup job and backup state, and why its last backup failed, linked to its page. For administrators, once a cluster is connected. (Every edition)
Security
- The Dependabot alerts are cleared (uuid, esbuild and vitest, the last two for development only). None of the vulnerable code paths was reachable in Restow.
Upgrade notes
- New image; no database migration. Coming from 0.3.0 or older, read the notes of 0.3.2 and 0.3.1.
Known issues
- Mounting an NFS share from the path field was tested against the mounter's stand-ins, not yet against a real NFS server. While a mount waits for running jobs, new jobs still start, so on a server that is never idle it keeps waiting.
- The known issues of 0.3.2 still apply.
Verification
Release pipeline of 10 October 2026: 10 of 11 checks passed in full, 1 skipped, 0 failed. The skipped check is number 4, Microsoft 365 backup and restore against the dev tenant. The Community images were smoke-tested on their own: 8 of 11 checks passed in full and 3 were skipped, on amd64 and on arm64. The full report, with the reason for every skipped check, is attached to the release as smoke-report.md.
Release notes of 0.3.3 on GitHub ↗
0.3.2 beta · 10 October 2026
Connects a Proxmox VE node with one command and no input, and fixes Microsoft 365 mailbox backups that stopped in the folders step with "Microsoft refused the request" (HTTP 400). One database migration runs on update.
- Connect a Proxmox VE node with one command: One command per node, with the enrollment token in it. The node installer sets up the user restow@pve, the roles and the restore pool itself, creates its own API token, checks its privileges before it installs anything and picks the thin storage for fleecing by itself. An existing Proxmox VE API token can be used instead and is handed to the node once. The storage plugin reports the storage API of the running Proxmox VE (8.4 up to 9.1). Tested against stand-ins for pveum and pvesm; Proxmox VE is a preview and not yet recommended as the only backup. (Every edition)
- Microsoft 365 mailbox backups no longer stop in the folders step: A mailbox backup could fail in the folders step with "Microsoft refused the request" (HTTP 400, an id that has to be unique in a batch) when Microsoft listed a folder twice while the folder list was read. Every batch now uses ids of its own, and each folder is read once.
Upgrade notes
- New image; one migration, 0032_pve_enrollment_pve_token (adds one column; instant). Coming from 0.2.x, read the notes of 0.3.1 first.
- Proxmox VE nodes connected with 0.3.1 keep working. To get the new storage plugin (no more warning on Proxmox VE 9), run restow-pve update on each node.
Known issues
- The new Proxmox VE onboarding was tested against stand-ins for pveum and pvesm, not yet on a Proxmox VE host.
- The known issues of 0.3.1 still apply.
Verification
Release pipeline of 10 October 2026: 10 of 11 checks passed in full, 1 skipped, 0 failed. The skipped check is number 4, Microsoft 365 backup and restore against the dev tenant. The Community images were smoke-tested on their own: 8 of 11 checks passed in full and 3 were skipped, on amd64 and on arm64. The full report, with the reason for every skipped check, is attached to the release as smoke-report.md.
The release notes on GitHub are dated 9 October 2026; the release was published on 10 October. Release notes of 0.3.2 on GitHub ↗
0.3.1 beta · 8 October 2026
The first release of the 0.3 line: 0.3.0 was tagged but never published, because its release checks found the first two fixes. Backs up Proxmox VE virtual machines and containers (preview), mounts NFS shares from the web interface, lets Community run several administrators and sends notifications to Discord, Slack and Teams and through Microsoft 365 or Google Workspace. The overview, the backup jobs and the archive now say plainly what is and is not protected. Six database migrations run on update.
- Proxmox VE (preview): Backup of VMs and containers on Proxmox VE 8.4 and newer through the Backup Provider API: VM disks over NBD with dirty bitmaps, so only changed 4 MiB blocks are uploaded; containers through restic; restore as a new VMID into the pool restow-restore. Guests count in readiness, warnings and overdue alerts. The node side is a storage plugin shim (a separate work under AGPL-3.0-or-later) and the helper restow-pve (Apache-2.0). A preview: tested against qemu-nbd, qemu-img and restic; not yet recommended as the only backup. (Every edition)
- Several administrators in every edition: Members with roles, Community included; limiting a member to chosen tenants stays part of Service Provider. Owners can reset a member's access. Before a change of the public URL or the operating mode makes passkeys unusable, the settings name the affected accounts and refuse a self-lockout. The login page offers "Lost access?": a reset link by mail when notification mail is set up, and always the way back through an owner or restow admin recover. Your account: change your password, see and sign out your sessions.
- NFS network shares from the web interface: Under Installation, Network shares, through the opt-in mounter container (docker compose --profile mounts up -d mounter, or the installer option --with-mounter): the mounter checks a share before it writes docker-compose.override.yml, restarts api and worker and rolls back on failure, and the updater moves it along with every signed update. Tested against fakes only, not against a real NFS server.
- Alerts in Discord, Slack and Microsoft Teams: A webhook gets a format, chosen from its URL: Discord, Slack or Microsoft Teams, beside the signed Restow envelope. 4xx answers from chat services end a delivery at once instead of retrying. Tested against fakes only.
- Overdue backups and warnings: A new event, backup.overdue, for mailboxes, OneDrives, IMAP accounts, machines and Proxmox guests, aware of the job schedules; an alert rule can set its own deadline (24 to 720 hours). Warnings list why single items were not backed up (folder, subject, date, cause, what to do) and can be acknowledged with a note. The bell leads to the cause, and a Notifications page keeps the history.
- Notification mail through Microsoft 365 or Google Workspace: Notification mail can be sent through Microsoft 365 (an app of your own with Mail.Send, or the backup app) or through Google Workspace (a service account with gmail.send), with guides and plain error messages. Tested against fakes only.
- Audit log export: Export of the audit log as CSV and as JSON with hashes. The delivery logs export as CSV. (Business, Service Provider)
- Archive per mail job, reading and search: Archive per mail job; journal reports are assigned to the mailboxes they name. The chain check verifies every link, the daily anchors (now written every night) and optionally a sample of message contents. Archived messages can be read and downloaded as .eml; search pages through its results and filters by sender, date and mailbox; a notice says how the storage protects archived mail.
- Legal holds with an audited release: A legal hold is released with a required, audited reason; holds can cover a single mailbox. (Business, Service Provider)
- What is protected, said plainly: Machines in no backup job no longer count as protected anywhere (overview, provider view, readiness, reports). The directory, the backup jobs and the machines say which job backs up an object and name objects in paused or unscheduled jobs. Overview, Statistics shows the active tenant only; the statistics of every tenant have a page of their own for Service Provider.
Breaking changes
- Machines that are in no backup job no longer count as protected anywhere (overview, provider view, readiness, reports). Installations that relied on the old count will see more yellow: add the machines to a backup job.
- Overview, Statistics shows the active tenant only. The statistics of every tenant moved to a page of their own (Service Provider); old links with scope=provider lead there.
- A provider member limited to some tenants can no longer read the statistics totals of all tenants (403).
- Integration API contract 1.3.0 (additive): webhooks carry format.
Security
- A provider member limited to some tenants could read the statistics totals and the tenant table of all tenants (Service Provider with tenant-limited members, up to 0.2.2). Fixed; nothing to do beyond updating.
Upgrade notes
- Six database migrations (0026 to 0031) run on start. The setup notice changed: every installation asks its provider admin once to accept it again.
- docker-compose.yml gains the mounter service in the profile mounts; replace your compose file with the one of this release (the updater does this for you).
- Rollback: restore the database dump taken before the update and start the 0.2.2 images; migrations are not reversible.
Known issues
- Proxmox VE was tested against real qemu-nbd, qemu-img and restic, but not yet on a Proxmox VE host; treat it as a preview.
- NFS mounts, Microsoft 365 and Google notification mail and the chat webhooks were tested against fakes only.
- Archive immutability on local and NFS storage is enforced by the application only.
Verification
Release pipeline of 8 October 2026: 10 of 11 checks passed in full, 1 skipped, 0 failed. The skipped check is number 4, Microsoft 365 backup and restore against the dev tenant. The Community images were smoke-tested on their own: 8 of 11 checks passed in full and 3 were skipped, on amd64 and on arm64. The full report, with the reason for every skipped check, is attached to the release as smoke-report.md.
Release notes of 0.3.1 on GitHub ↗
0.2.2 beta · 6 October 2026
Lets every Microsoft 365 source connect either through the consent invitation or through a Graph app of its own, connects the tenant the backup app lives in without a consent link, fixes the Hetzner Object Storage preset and the Object Lock detection, and adds an alpha channel for test builds to the updater tab. No migration and no manual step.
- A Graph app of your own for a source: Besides the consent invitation of the shared backup app, a source can be connected through an app the customer created by hand in their own tenant: tenant ID, application ID and a client secret or certificate. The credential is sealed in the organisation's secret store and never shown again. Tested with unit tests and the CI suites only, not against a real Microsoft 365 tenant.
- The tenant of the backup app connects without a consent link: For the tenant the backup app lives in, the source page offers a direct connection (provider owners only): a working app token for that tenant is the proof, so the consent round trip, which Entra refuses for an app that is not installed there, is skipped. It needs the directory (tenant) ID of the app under Settings, Microsoft 365.
- Hetzner Object Storage preset with a location choice: The preset has a location selector (Falkenstein, Nuremberg, Helsinki) that sets endpoint and region together. Buckets with a dot in their name are reached by path, and the Object Lock detection accepts the answers of S3-compatible services such as Hetzner. Not tested against a real Hetzner bucket.
- Alpha channel for test builds: Under Installation, Updates, "Use the alpha channel" selects a source of unsigned test builds in one click. The tab warns while it is selected and while the updater does not verify signatures. Only for test installations, never for one with production data.
Known issues
- Connecting through a Graph app of your own and connecting the home tenant directly were tested with unit tests and the CI suites only, not against a real Microsoft 365 tenant. The Hetzner preset and the Object Lock detection were not tested against a real Hetzner bucket.
- A source connected by consent cannot be switched to a Graph app of its own in the interface.
Verification
Release pipeline of 6 October 2026: 10 of 11 checks passed in full, 1 skipped, 0 failed. The skipped check is number 4, Microsoft 365 backup and restore against the dev tenant. The Community images were smoke-tested on their own: 8 of 11 checks passed in full and 3 were skipped, on amd64 and on arm64. The full report, with the reason for every skipped check, is attached to the release as smoke-report.md.
Release notes of 0.2.2 on GitHub ↗
0.2.1 beta · 3 October 2026
Finishes what 0.2.0 started: machines are backed up through jobs, tables get context menus, and file restore puts machines and mailboxes on one timeline.
- A machine backs up once it is in a job: Every machine without a backup job is marked "Without backup", counted in a banner and offered "Create a job" or "Add to job". A newly enrolled machine waits for a job instead of backing up on its own. Servers back up their application data (/opt, /usr/local, /var/lib) by default.
- Context menus on table rows: Right-click or the ⋯ button on a table row opens the actions of that row: a new job from the selection, edit a job, run it now, open its history, restore files from a machine.
- Machine table with "assigned to": A table of all machines with host name, the person each machine is assigned to, last backup and recovery readiness. A machine can be assigned to a person from the directory.
- File restore from the machine table, one timeline: File restore starts from the machine table. Restore points of machines and mailboxes sit on the same timeline, with day separators, times and a jump to a date.
- Public demo with simulated running jobs: The public demo shows backups that run, with progress, throughput charts and the run detail panel, simulated on synthetic data.
- Default storage in the web interface: The installation owner sets the default storage (a local path or an S3-compatible bucket) under Installation > Default storage, checked by a probe before it is saved. Tenants on it stay separated by their own prefix and key.
- Updater with one command, updating itself: The opt-in updater starts with one command and, after a signed update, moves itself to the verified release. A Community installation switches to the full build from Installation > Edition, with a database dump, health check and rollback.
Breaking changes
- A machine in no backup job is not backed up. Newly enrolled machines and machines that leave their job wait for one; machines in no job at the time of the update keep their own configuration.
- API: backups of a machine need a job. backup_now and a schedule other than none answer 409 endpoint-no-job for a machine in no job.
Upgrade notes
- One additive database migration (0025_endpoint_assigned_user); nothing is deleted.
- If you enabled the updater on 0.2.0, replace docker-compose.yml with the one of the 0.2.1 release before or with the update.
- Agents older than 0.2.1 do not know the schedule none and report failed no_paths runs until they update themselves; nothing is read or written.
- SMB/CIFS is no longer named as a supported storage mount; network storage for local targets is NFS. Existing mounted paths keep working.
- Rollback: the 0.2.0 images plus the database dump taken before the update, because a migration ran.
Release notes of 0.2.1 on GitHub ↗
0.2.0 beta · 3 October 2026
Reorganises the web interface around three levels (the installation, a tenant, all tenants), adds backup jobs that replace per-object schedules, and shows running backups live. Updating from 0.1.0 runs three additive migrations and moves schedules and machines into jobs without deleting anything.
- Installation, own organisation and tenant settings: Every setting has one place and one scope: the installation (server, notification mail, updates, license) and one settings page per tenant (connections, protection, jobs, retention, repositories, archive, agents, notifications, integrations, members, modules), in place of the Setup tab bar of 0.1.0. For service providers, setup creates their own organisation as an internal tenant. Switching a module off pauses its work and never deletes data.
- Tenant switcher with search in the menu: Switch between tenants at the top of the menu and find one by name or customer number. (Service Provider)
- Colour scheme Restow or Neutral beside light and dark: Choose between the Restow colours and a neutral palette, in addition to light and dark mode.
- Full-width tables with pinned columns: Tables use the full width of the page, and pinned columns stay in view while you scroll sideways.
- Guided installer with a reverse-proxy mode, encrypted to Restow by default: An installer that guides you through the installation, with a mode for running behind a reverse proxy. The connection to Restow is encrypted by default.
- Setup wizard starting with the language choice: The setup wizard now begins with the choice of language.
- Get started as a checklist in the menu: A rocket entry at the bottom left of the menu opens the setup checklist: a working repository, a connected source, protected objects, active schedules, the first backup and the first passed restore check, plus notification mail, which can be marked as not needed. The entry disappears once setup is complete.
- Recovery readiness linked to filtered tables: Each state of recovery readiness (passed, due, not finished and retried, failed, backup too old, never checked) filters the table below it.
- Overview across all tenants or one tenant: The Overview shows every tenant at once or a single tenant, as you choose. (Service Provider)
- Jobs as templates: A job says what is backed up, when, into which repository, for how long and how often a restore check proves it, for many mailboxes, OneDrives, IMAP accounts or machines at once, with overrides per object or machine. There are jobs for mail and for servers and clients; for servers and clients you pick the folders to back up and exclude file types. On the update, today's schedules and machine settings become jobs; nothing is deleted.
- Live updates without reload, run detail panel with throughput charts: Running jobs and tables update on their own, without reloading the page. A run opens a detail panel with charts of its throughput.
- Bandwidth limit with a time window: Limit how much bandwidth a backup may use, within a time window that you set.
- History including agent runs: Every run, including the runs of agents on servers and clients, with filters by kind (backup, restore, restore check, export, import, maintenance) and by job. The retries of a restore check show as one line.
Breaking changes
- POST /api/v1/setup requires providerName, the name of your own organisation.
- Backup and restore-check schedules are created through jobs: POST /api/v1/schedules with kind backup or verify answers 422 kind_replaced_by_jobs. Maintenance schedules are unchanged.
- A machine in a job is configured from the job: PATCH /api/v1/endpoints/:id answers 409 when it would change folders, exclusions, hooks, bandwidth or the schedule.
- Old web addresses redirect to their new place; bookmarks and links in mails keep working.
Release notes of 0.2.0 on GitHub ↗
0.1.0 beta · 2 October 2026
First public release.
- Microsoft 365 backup: Exchange Online mail, calendar and contacts, and OneDrive, incremental via Microsoft Graph delta queries.
- IMAP backup: Any IMAP mailbox, password-authenticated: one login for the whole source, a password per mailbox, or a master user. OAuth2 sign-in for IMAP is not implemented yet.
- Non-destructive restore: A single item, a folder, a file version or a whole mailbox or OneDrive, into the original account or another one. The restore explorer has a slim timeline of restore points at the bottom.
- Weekly restore check: A sample from every backup is read back and compared byte for byte, shown as recovery readiness.
- Archive: An append-only archive with a SHA-256 hash chain, chain verification, search and export (EML in a ZIP, MBOX), in every edition. Search covers subject, addresses and the body text of imported mail, not attachments. It is filled by mail import with archiving; journal receipt is part of the GoBD layer below.
- GoBD layer for the archive: Exchange Online journal receipt over SMTP (it needs a TLS certificate of your own), retention policies with an enforced deletion run, and legal hold per tenant or per mailbox. Designed for GoBD-compliant use. Immutability is enforced by the application on local and NFS repositories; on S3 with Object Lock the archive item records are locked, and since 0.4.0 an administrator can lock the packs with the message content too (see Immutable backups). (Business, Service Provider)
- Multiple tenants: Many client tenants in one installation, with a cross-tenant API key. Managing several tenants is part of Service Provider. (Service Provider)
- Delegated tenant administrators and provider overview: Each tenant has its own administrators who see and manage only that tenant. The provider view of the Overview lists every tenant with its status and the alerts that follow from it. (Service Provider)
- REST API and operations log: OpenAPI-documented REST API with per-tenant keys and scopes; job history and errors in every edition.
- Team with roles: Several administrators, each an owner, administrator, technician or read only, with every tenant or chosen ones. Owners invite and change members; every change is audited. Business and Service Provider had this from 0.1.0; every edition has it since 0.3.1 (see 0.3.1 to 0.3.6).
- Audit log viewer: Search, filters, entry details and hash-chain verification of the audit log. Recording of every read and restore runs in every edition; the export came with 0.3.1. (Business, Service Provider)
- Scheduled reports: A daily, weekly or monthly summary by e-mail: success rate, failed items, share proven restorable, failure causes, storage and restores. (Business, Service Provider)
- Alerts: Rules that send an e-mail, a bell entry or a signed webhook when a backup fails, a restore check does not pass or storage is damaged, with a delivery log.
- Server and client backup: A Go agent for Linux and macOS on restic 0.19.1, set up with one command and a one-time token, installed in a folder only root can change. Signed agent releases, outbound HTTPS only, append-only, one repository per machine, restores into a new folder, restore tests that feed recovery readiness. Hooks run only where root on the machine allows them. Windows is planned.
- Mail file import and export: Import EML, MSG, MBOX and MailStore exports as a legacy mailbox you can browse, restore into Microsoft 365 or IMAP, download and optionally archive. Export mailboxes from backups, imports and the archive as EML in a ZIP or as MBOX.
- Updates: An update check that is off until you turn it on, with your own release source if you like, and an optional updater that announces maintenance, backs up the database first and rolls back if the new version does not become healthy. The rollback applies only while the database migrations have not run; after a migration the updater stops, keeps the dump and tells you how to restore it.
- Failure explanations: Every failed job, item, sync and verification says what happened, why (88 stable cause codes) and what to do, with links and technical details for support.
- Signed, verified releases: Every release comes as two builds in four images: the full build (restow, restow-web) and the Community build (restow-community, restow-web-community). Each image is built for amd64 and arm64, signed with cosign, ships an SBOM and passes a release smoke run (fresh install, restores with hash comparison, endpoint backup, mail import, file share backup against Samba) before it is published. Every agent release is signed by the maintainer with an Ed25519 key; the installer and the agent refuse a release without a valid signature. The automated Microsoft 365 check of the release pipeline against a real tenant (check 4, a dev tenant) is not active yet and was skipped in every release so far; a simulated Graph API stands in. In the release checks, the agent installer runs in a Linux container only.
- Setup token, operator notice, two builds: The setup wizard first asks for a one-time setup token that only someone with access to the server can read in its log, then for the acceptance of an operator notice. The core (Apache License 2.0) and the Business and Service Provider modules (ee/, under the Restow license terms) live in one repository. The full images carry both, the modules locked until a license key is installed; the Community images carry the core only.
Known limits
- Teams is not part of version 1; SharePoint Online sites were added in 0.4.0.
- OAuth2 for IMAP sources is not implemented yet; planned. IMAP sources sign in with a password: per source a shared login, a password per mailbox, or a master account.
- The first backup of a large tenant can take days because Microsoft throttles the Graph API; Restow shows that wait.
- Microsoft 365 backup and restore have not yet been run against a real Microsoft 365 tenant in the release checks; they are covered by tests against a simulated Graph API.
- Sign-in with Microsoft and single sign-on (SSO) are planned: the Microsoft sign-in code exists, but account linking is missing, so nobody can sign in with Microsoft in 0.1.0.
- Microsoft began switching off Exchange Web Services (EWS) for Exchange Online on 1 October 2026 and retires it for good on 1 April 2027. Restow never used EWS and talks to Microsoft only through Microsoft Graph.
- The archive is filled by mail import with archiving and, from Business, by Exchange Online journal receipt. Archive sync from Microsoft 365 (Graph) and from IMAP mailboxes is planned and not part of 0.1.0. Archive search covers subject, addresses and the body text of imported mail; attachment content is not searched.
- Immutability is enforced by the application on local, NFS and SMB targets; on S3 with Object Lock, 0.1.0 locks only the archive item records, not yet the message content or backup data.
- The updater's automatic rollback applies only while the database migrations of the new version have not run; after a migration it stops and keeps the database dump.
- The endpoint agent backs up files on Linux and macOS: no Windows yet, no mTLS, no filesystem snapshots, no disk images. The release checks ran the agent's install script only in a Linux container; the macOS agent ships, but its installer was not part of the release checks. The first installation trusts the install script the instance serves: compare the release key fingerprint it prints with the published one, SHA256:2LB7RPpIS0wqbcZ2NA8MjuQN7ZfFPqBX3CwrGFSu76o.
- PST and OST import and PST and MSG export are planned. An imported mailbox cannot be deleted in this release, and calendar and contacts from MSG are not imported.
- Beta: do not use it as your only backup.
Verification
Before a version is published, the release smoke run checks, for the full and the Community build on amd64 and arm64, a fresh install, health, passkey sign-in, IMAP backup and restore with hash comparison, journal receipt and chain check, standalone restore, storage targets, an image scan, endpoint backup and restore, and mail import and export (the Community build skips the journal and storage-target checks, which need the Business modules or a second tenant). The release notes on GitHub carry the result and say what was not run. The final smoke run of 1 October 2026 passed, for the full build, 9 of 11 checks in full, 1 partly (the upgrade path, because there is no earlier release) and skipped 1: the Microsoft 365 check against a test tenant, for lack of credentials. For the Community build it passed 7 in full and 1 partly (the same upgrade path) and skipped 3: the Microsoft 365 check and, by design, the journal and storage target checks, which run in the full build.