Repository navigation
Restow 0.2.1
Restow 0.2.1. Released 2026-10-03, channel beta.
Beta release. Run it alongside your existing backups, not as your only one, until
you have verified restores against your own data.
Summary
Restow 0.2.1 backs up a machine only once it is in a backup job and shows every
machine without one, lets you assign a machine to a person of the directory, adds
context menus to the tables, puts machines and mailboxes on one restore-point
timeline, makes the installation's default storage configurable in the web
interface and lets the opt-in updater start with one command and update itself.
Updating runs one additive migration; read the Upgrade Notes if you enabled the
updater on 0.2.0 or run agents older than 0.2.1.
Breaking Changes
- A machine in no backup job is not backed up. A newly enrolled machine gets
the schedulenoneand starts backing up once it is a member of a job. A machine
that leaves its job (removed as a member, left out of a replaced scope, or its job
deleted) goes back tononewith a new configuration version, and a backup
request that has not started yet is dropped. Machines that are in no job at the
time of the update keep running their own configuration; only newly enrolled
machines and machines leaving a job wait. To keep a machine backed up, put it
into a job (Servers & endpoints > Jobs, or "Create a job" / "Add to job..." in
the machine table). - API: backups of a machine need a job.
POST /api/v1/endpoints/:id/tasks
withbackup_now, andPATCH /api/v1/endpoints/:idwith a schedule other than
none, answer 409urn:restow:problem:endpoint-no-jobfor a machine in no job.
The machine list marks such a machine with the attention reasonno_job.
Scripts that trigger machine backups must add the machine to a job first.
Added
Machines and backup jobs
- Machines without backup are visible (all editions, Servers & endpoints >
Machines). The inventory has a "Backup job" column with the job as a link or a
"Without backup" warning badge, a filter "Without backup / In a job", and a
banner counting the machines without backup. Job administrators get "Create a
job" (the job editor with the machine preselected) and "Add to job..." (moves the
machine after a confirmation when it is in another job) on each row and on the
machine page. "Back up now" is disabled for a machine in no job, and the enroll
dialog says that a new machine is backed up only in a job. - The 0.2.1 agent waits for a job. Under the schedule
noneit starts no
scheduled backup, ignores a backup request, andrestow-agent statussays it is
waiting for a backup job. - Assign a machine to a person (all editions). A machine can be assigned to a
person of the tenant's protection directory (a user of the directory, not a login
account): an "Assigned to" column in the machine table (sortable, filterable by
person and by nobody, found by the search), an "Assign to..." dialog that
searches the directory and removes an assignment, and the same on the machine's
overview and General settings. The assignment is audited asendpoint.assigned
and cleared when the person leaves the directory. API:PATCH /api/v1/endpoints/:idtakesassignedUserId(422
endpoint-assignee-unknownfor anyone not in the tenant's directory), list and
detail carryassignedTo, andGET /api/v1/directory/people?search=feeds the
picker. - "Run now" on a machine job shows what happens. The agent starts a requested
backup at its next check-in (about every 5 minutes); until then the job and its
members show "Queued" with the expected check-in, and the toasts say that the
backup was requested or is already waiting.
Tables and restore
- Context menus on table rows (all editions). Right click, the context menu
key, Shift+F10 or a long press on a touch screen open the row's actions in the
machine, jobs, job members, protected objects (mailboxes) and the other tables
with row actions; the "..." menu offers the same entries. Links, fields, selected
text and Shift with a right click keep the browser's menu. Selectable tables get
checkboxes, a selection bar and "New job from selection" on the context menu of a
selected row. The jobs table gains Open and Open history; protected objects offer
their restore points, a restore in the explorer, a new job with the object and
the protection decisions. - File restore with machines and mailboxes on one timeline (all editions).
File restore lists the tenant's machines and mailboxes in one searchable list.
Restore points are shown newest first, grouped by day under sticky separators
(Today, Yesterday, weekday and date), with a jump to a date that lands on that
day or the nearest earlier one; the Snapshots tab of a machine uses the same
timeline. A mailbox's chosen restore point opens in the restore explorer. The
machine table links to file restore for a machine.
Installation
- Configurable installation default storage (Installation > Default storage).
The installation owner can save a local path or an S3-compatible bucket as the
default storage in the web interface, with a recent sign-in. It is stored sealed
with the master key and, once saved, wins over theSTORAGE_*/S3_*
environment; removing it brings the environment back. The page shows where the
default comes from and the probe the server ran before saving. A change that moves
the location is refused (409, tenants named) while any tenant keeps data on the
current default, has it attached as a retired target, is migrating off it or has a
storage job queued or running. A tenant's storage page always shows the
installation default as a choice, with "Use installation default" while the
tenant's own primary holds no data. API:PUTandDELETE /api/v1/settings/default-storage, audited. - The opt-in updater starts with one command.
docker compose --profile updater up -dis enough:RESTOW_PROJECT_DIRandRESTOW_UPDATER_IMAGEare optional.
The project directory is mounted at/project, and on its first start the
updater pins its own image by digest intoRESTOW_UPDATER_IMAGE, so later
rewrites ofRESTOW_IMAGEdo not reach it. The install script no longer writes
RESTOW_UPDATER_IMAGE. See docs/UPDATING.md. - The updater updates itself. After an image-mode update whose images passed
the signature check, the updater pinsRESTOW_UPDATER_IMAGEto the verified
application image by digest and recreates itself through a short-lived helper
container. Never after a source-mode update or with the signature check off;
RESTOW_UPDATER_SELF_UPDATE=falseswitches it off. A failed self-update leaves
the application update successful, and the Updates tab shows the command that
finishes it. - Switch a Community installation to the full build (Community build,
Installation > Edition). The section names the build, lists what Business and
Service Provider add and offers a one-way switch to the full build of the same
version: with the updater (owner role, recent sign-in, audited as
update.build_switch.scheduled; the full images are verified against the
release signature of the tag, with database dump, health check and rollback), or
by hand with the two.envlines it shows. A license key can be stored on the
Community build; it is kept sealed until the full build verifies and installs it,
and a rejected key is dropped and audited. There is no switch back to Community. - "Hide Start" in the popover of the sidebar's setup guide removes the Start
entry in every tenant for that user (remembered in the browser, with an undo
toast); the setup steps stay on each tenant's page under "Show setup steps".
Public demo
- Simulated live runs. The demo's agent sidecar plays backups of the two
simulated machines through the real agent API with live progress, about one in
ten finishing partial. Configurable withRESTOW_DEMO_SIM_RUNS,
RESTOW_DEMO_SIM_INTERVAL_SECONDS,RESTOW_DEMO_SIM_MIN_DURATION_SECONDS,
RESTOW_DEMO_SIM_MAX_DURATION_SECONDSandRESTOW_DEMO_SIM_PROGRESS_SECONDS
(deploy/demo/README.md, "Simulated live runs"). The seed puts the demo machines
into a daily machine job, since a machine in no job is not backed up.
Changed
- Linux servers back up application data by default. The server profile's
default paths add/opt,/usr/local,/var/liband/var/backupsto/etc,
/home,/root,/srvand/var/www, and exclude/var/lib/docker,
/var/lib/containerdand/var/lib/apt/lists. These are the defaults a newly
enrolled server starts with; the client profile is unchanged. Databases under
/var/libare backed up as files: for a consistent copy, add a dump command as a
hook before the backup. - IMAP sources lead to the mailbox passwords. In "one password per mailbox"
mode, editing a source offers "Manage mailboxes and passwords" (the directory
filtered to that source), and a new source says where the passwords go after
saving. Enteringoutlook.office365.comor another Microsoft IMAP host shows that
Microsoft has disabled password sign-in there and points to the Microsoft 365
connection. - Adding mailboxes shows the source's login mode with a link to change it, a
password (and optional username) field per mailbox on a source with one password
per mailbox, and afterwards a list of each mailbox as new or already listed and
whether a password is set. Pasted CSV keeps working. - Removing a mailbox that has backups explains that backups are never deleted
as a side effect, stay restorable and expire with retention, and offers "Exclude
from protection"; under a legal hold it says so, also when a delete is refused. - Machine settings use the full width in two columns from the xl breakpoint,
with a sticky column of jump links from the lg breakpoint. - File restore without the machine dropdown: the machines are a searchable list
beside their restore points; with a single machine it is chosen right away. - SMB/CIFS is no longer named as a supported storage mount. Network storage for
local targets is NFS only in the interface, the API description and the docs.
Existing mounted paths keep working. - The Updates tab explains the updater: choosing an update source does not
start it, the one command that does, that the updater is the application image,
and the self-update state. - A switch from Community to the full build is worded as a switch ("switch to
the full build (0.2.1)") in the run card, maintenance banner, modal and toasts,
not as an update. - Dependencies (Dependabot #14, patch and minor): hono 4.13.11, mailparser
3.9.31 (linear quoted-printable decoding), nodemailer 10.0.12, smtp-server
3.19.15, react-resizable-panels 4.14.1, recharts 3.10.1 and @aws-sdk/client-s3
3.1143.0. No configuration, API or database change; THIRD_PARTY_NOTICES.md is
regenerated.
Fixed
- A new Linux server backed up almost nothing (about 2 MB): the default paths
left out/opt,/usr/localand/var/lib, where servers and LXC containers
keep their application data (see Changed). - "Run now" on a machine job looked as if nothing happened. The request stayed
invisible until the agent's next check-in and the toast said nothing started; the
open request is now read and shown as queued. - The IMAP password column was a dead end in "one password per mailbox" mode; it
now leads to where the passwords are set. - Long webhook URLs pushed the delete dialog wider than the screen (a Discord
webhook URL is one unbreakable word); the description wraps now. - A long press on a touch screen opened an empty row menu, because no context
menu event fires there; the entries are read when the menu opens. - Listing the tenants that keep data on the default storage scanned every pack;
it uses an indexed probe per tenant now.
Security
No Restow advisory or CVE is fixed in this release. Two points concern the parser
update and the updater's trust model.
- mailparser 3.9.31 fixes quadratic quoted-printable decoding (a crafted
message made decoding slow). Restow up to 0.2.0 already contained it by parsing
mail in isolated processes with time limits, so it could not stall the service;
the update is defence in depth and needs no action. - The updater now changes its own image. After a successful image-mode update,
the release signature, verified by cosign against the release workflow identity
of the tag and Sigstore, is what lets the updater (which holds the Docker socket)
replace itself with the new application image, pinned by digest. Before, the
updater's image changed only by hand. SetRESTOW_UPDATER_SELF_UPDATE=falseto
keep it that way. pnpm audit --prod: no high or critical findings; 4 moderate ones in
transitive dependencies that are not reachable at runtime: esbuild 0.18 (through
drizzle-kit, its development server only), vitest and @vitest/mocker 3.2 (a peer
of better-auth, not loaded in production) and uuid 8.3 (through @azure/msal-node
v3, v5 and v6; the affected buffer API is not used).
Upgrade Notes
Kind of update: new images, plus manual steps if you enabled the updater on
0.2.0. Order: back up (docs/UPDATING.md), replace docker-compose.yml if you use
the updater, pull the 0.2.1 images, start. Migrations run automatically at start.
- Database: one additive migration,
0025_endpoint_assigned_user. It adds the
nullable columnendpoints.assigned_user_idwith an index, a unique index over
users (tenant_id, id)and a composite foreign key so that a machine's person
always belongs to the machine's tenant. Nothing is deleted. Its duration was not
measured on an installation with real metadata. - Machines without a job. Machines that are in no job at the update keep
running their own configuration. Newly enrolled machines and machines that leave
a job wait for a job (see Breaking Changes). - Agents older than 0.2.1 do not know the schedule
none. Until they update
themselves (every 6 hours, or when a machine is next online), a machine with the
schedulenonegets no paths and no hooks from the server and reports one failed
no_pathsrun per slot of its profile's default schedule, with abackup.failed
alert each time; nothing is read or written. The agent's self-update ends it. If
you paused agent updates (Tenant page > Agents), resume them or put the machine
into a job. - If you enabled the updater on 0.2.0: before or with the update, replace
/opt/restow/docker-compose.ymlwith the one of the 0.2.1 release assets (the
updaterservice now falls back toRESTOW_IMAGEwhenRESTOW_UPDATER_IMAGEis
empty and mounts the project directory at/project). Install 0.2.1 from
Installation > Updates, then delete theRESTOW_UPDATER_IMAGEline from.env
(or set it to the 0.2.1 application image) and run
docker compose --profile updater up -d updateronce. From then on the updater
follows signed updates itself. - Environment variables: new
RESTOW_UPDATER_SELF_UPDATE(optional, default
true: the updater moves itself to a verified release after an image-mode
update); newRESTOW_DEMO_SIM_*(public demo only).RESTOW_UPDATER_IMAGEand
RESTOW_PROJECT_DIRbecome optional. - Default storage: once a default storage is saved under Installation > Default
storage, it overridesSTORAGE_*/S3_*from the environment; removing it there
brings the environment back. Nothing changes until you save one. - Downtime: the containers restart and the migration runs at start.
- Rollback: the previous tag (the 0.2.0 images) plus
docker compose up -d, and
restore the database dump taken before the update, because a migration ran and
migrations are not reversible.
Known Issues
- No migration back to the installation default: a tenant whose own primary
storage already holds data cannot move back to the installation default. - Staged uploads and exports are not counted as data on the default storage
when the server decides whether the default may move. - A default-storage change made by another API process is picked up within 30
seconds, not at once. - The switch to the full build and the updater's self-update were not exercised
end to end with a real Docker daemon; they are covered by unit and PostgreSQL
tests only. - The integration API does not expose
assignedToyet. - Demo only: simulated runs report made-up sizes while reusing a snapshot of
about 1 MB. - Not part of this release: the Windows agent and Proxmox.
- Microsoft 365 backup and restore have still never run against a real Microsoft 365
tenant; they are covered by tests against a simulated Graph API.
Verification
Local runs on 2026-10-03 in a Linux container, on the release candidate before
the version commit; the release pipeline repeats CI and runs the smoke on the
tag. No Microsoft 365 development tenant was used.
- Lint and typecheck over every workspace passed.
- Unit suites passed: packages/core 1919, apps/api 2161, apps/worker 248, ee
174, apps/web 3461, packages/i18n 244, demo seed 204, packages/db 143 (including
its PostgreSQL tests). - PostgreSQL suites against a local PostgreSQL 16 with restic 0.19.1 passed:
apps/api 762 (including the endpoint suites), apps/worker 96, ee 66. - Endpoint agent:
go vetandgo testpassed. - Installer:
deploy/install/test.sh, 601 checks passed. - Not run here, and so not claimed: the Docker image build, the release smoke
(scripts/smoke), the updater end-to-end test, and runs against real Microsoft
365, IMAP or S3 services. They run in CI and the release pipeline; this section
is completed with the smoke reports of the pipeline run.
Release pipeline, 2026-10-03:
| Check | Result | Duration |
|---|---|---|
| 1. Install: images, compose up, migrations (new install and upgrade) | PASS | 74s |
| 2. Health endpoints, worker and scheduler registered | PASS | 0s |
| 3. Passkey sign-in E2E with a virtual authenticator, tenant creation, i18n de/en | PASS | 114s |
| 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 | 20s |
| 9. Image scan (Trivy) and dependency audit (pnpm audit) | PASS | 17s |
| 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 |
10 of 11 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: 8 of 11 checks passed in full, 0 partly, 3 skipped (see the reasons below); arm64 PASS with gaps: 8 of 11 checks passed in full, 0 partly, 3 skipped (see the reasons below).
Images
Two builds of the same release. The full build is the Apache-2.0 core plus the Business and Service Provider modules (Restow Enterprise License), which stay locked until a license key is installed. The Community build is the Apache-2.0 core alone.
ghcr.io/restow-backup/restow:0.2.1
ghcr.io/restow-backup/restow-web:0.2.1
ghcr.io/restow-backup/restow-community:0.2.1
ghcr.io/restow-backup/restow-web-community:0.2.1
The digests of the multi-arch manifests (the opt-in updater verifies the pulled images against the two lines of its build):
restow: sha256:088948e19a919db498b3edaba733cc6c5672801dec4f7573b99f72b53e0153c4
restow-web: sha256:a0552e195c95ac71747e5b266941d4f6528b7e44bc60caa193004d78016ecdc6
restow-community: sha256:44bc107637b6138a457d5ef13586c706efc7c3784cbc870535eb6236474395cc
restow-web-community: sha256:7d47a7fd637b32efdcc8b843f4881633eca44af7803bf3da53f21889084de724
All four images are multi-arch (linux/amd64, linux/arm64), signed with cosign (keyless) and carry an SPDX SBOM as attestation. Verify any of them with:
cosign verify ghcr.io/restow-backup/restow:0.2.1 \
--certificate-identity-regexp '^https://github.com/restow-backup/restow/\.github/workflows/release\.yml@refs/tags/v' \
--certificate-oidc-issuer https://token.actions.githubusercontent.com
cosign verify ghcr.io/restow-backup/restow-web:0.2.1 \
--certificate-identity-regexp '^https://github.com/restow-backup/restow/\.github/workflows/release\.yml@refs/tags/v' \
--certificate-oidc-issuer https://token.actions.githubusercontent.com
cosign verify ghcr.io/restow-backup/restow-community:0.2.1 \
--certificate-identity-regexp '^https://github.com/restow-backup/restow/\.github/workflows/release\.yml@refs/tags/v' \
--certificate-oidc-issuer https://token.actions.githubusercontent.com
cosign verify ghcr.io/restow-backup/restow-web-community:0.2.1 \
--certificate-identity-regexp '^https://github.com/restow-backup/restow/\.github/workflows/release\.yml@refs/tags/v' \
--certificate-oidc-issuer https://token.actions.githubusercontent.com
docker-compose.yml and env.example are the release stack (deploy/release/); install.sh installs it on a dedicated Linux VM (README.md, "Install with the script"; install.sh.sha256 for sha256sum -c after the download); restow-agent-THIRD_PARTY_NOTICES.txt holds the licenses of the agent binaries and of the restic they run; SHA256SUMS lists every file of this release and is signed (SHA256SUMS.sigstore.json).