Skip to content

Updating

On every platform. Settings → About → Updates is the path. rexenv checks a signed release descriptor, offers the new version, verifies its signature and checksum, replaces itself in one step — and then asks before it closes. Nothing restarts under your hands.

What “replaces itself” means differs, because the three operating systems allow different things:

macOS rexenv.app is swapped for the new bundle, then the app asks and — on OK — closes and opens again. The menu-bar menu carries an Update to … item when there is an offer, and it only opens that card.
Windows The install directory is renamed aside and the new one moved in — measured: a running .exe cannot be deleted or overwritten, but it can be renamed away while it keeps running. Nothing is deleted on any path, the previous directory is kept until the new build reports itself healthy, and the Apps & Features entry’s version is rewritten to match.
Linux From the .deb: the new package is downloaded and checked — it must be rexenv, the version the signed release names, this machine’s architecture, and carry both rexenv and rex — then installed by dpkg behind one password dialog. From an AppImage: the file is swapped for the new one where it sits, with no prompt, and the previous copy is set aside rather than deleted.

Each platform reads its own signed descriptor — and on Linux each package kind and architecture has one of its own — so a release never offers a machine another platform’s build. That no longer rests on the publisher alone: a release marked for another operating system is refused by the copy reading it, before any other rule is applied, and a component update marked for another system is never offered. Each also keeps its own replay record: when one record served every descriptor, a copy that had once read another platform’s document refused its own as a replay, and said only that it “couldn’t reach the update server”.

The sentence above the button says what the click does, in the order it happens:

Downloads rexenv <version> (<size>), checks its signature and checksum, replaces rexenv.app in one step, then ASKS before it closes and reopens on <version>. Your sites, databases and DNS keep running throughout — services outlive the app. Open terminals and running jobs close with it, exactly as they do when you quit.

On Windows the same sentence names what is replaced there instead: rexenv’s program files (your data folder is not touched); on Linux, rexenv’s program files.

The size is counted in 1024s — 29.6 MB for 31,000,000 bytes — the same rule as the card’s header. Through 0.8.11 the sentence divided by 1,000,000 while the header used 1024, so one download showed as two sizes on the same card.

Installed with Homebrew, it adds that brew upgrade --cask rexenv also works and that brew sees the new version afterwards. And on macOS it adds the one consequence worth knowing before the click: rexenv has no Apple developer signature yet, so each build is a new identity to macOS — it may ask again for permissions it had already granted this copy.

Until 0.8.4 it did not. The install ended in the app quitting itself: the swap landed, the window went away mid-sentence, and nothing had been said about it. Since 0.8.4 the install swaps the build and stops there, and a one-button dialog headed rexenv <version> is installed says what a restart costs:

rexenv will now close and open again on <version>. Your sites, databases and DNS keep running throughout — services outlive the app. Open terminals and running jobs close with it. If you dismiss this, the new version starts the next time you open rexenv.

OK is the only button, and dismissing it — Esc, or the backdrop — is allowed rather than trapped, which is why the sentence names what dismissing means. Either way the new build is already on disk: nothing is downloaded a second time.

If the app does not come back after OK, open it by hand — and then look at relaunch.log in rexenv’s logs folder. The small helper that waits for the old app to exit and opens the new one writes three lines there per update: what it was told, how the wait ended, and how the launch ended. Before this file existed, a reopen that did not happen left no trace at all: the helper outlives the app, detached, with nowhere to write an error. Builds older than the release that added it do not write it.

Skip this version sets one version aside and is undoable; a later release still offers itself, because what is stored is a version rather than a flag.

The check rides launch, plus a six-hour loop for a Mac left running — rexenv is a menu-bar app and outlives its window. The request carries rexenv/<version> and nothing else: no architecture, no identifier.

The automatic checks switch is in the same card and in rex config set app_update_auto_check false. It is read before any network I/O, so off means the request is never made rather than the answer being ignored — and Check now still works.

The footer reads checked N ago from a timestamp written only after a successful check, so a failed check keeps the old one instead of ageing into a lie. Nothing on the card ever says “up to date”.

A failed check reads as a sentence, not a stack trace — the URL and the raw error go to the log instead:

What happened What the card says
No connection, or it timed out rexenv couldn’t reach the update server. Check your internet connection and try again.
Nothing published yet The update server has nothing published yet.
Any other HTTP status The update server answered code, so rexenv could not check just now.
A reply rexenv cannot read The update server sent something rexenv could not read.
A reply rexenv refused The update server answered, but rexenv refused what it published: the reason. Nothing here changes until a descriptor this copy accepts is published.
A copy with no descriptor On Linux, a copy that is neither the .deb install nor an AppImage: This rexenv is not a .deb or an AppImage install, so there is no update descriptor for it — nothing here says whether a newer rexenv exists.

The last row is new since 29 September 2026: until then a copy that could not check at all — nothing fetched, no network involved — was reported as the first row, couldn’t reach the update server.

The check enforces its own deadline rather than trusting the HTTP client’s: 6 seconds when you press Check now and are watching a spinner, 12 seconds for the launch check and the background loop.

Deciding it can update and being able to are different questions, so the card answers them separately. Every refusal names the consequence, carries the fix, and never turns into an admin prompt.

On Windows the same rule produces a different list — the one refusal worth naming is an install outside %LOCALAPPDATA%: a copy under Program Files is a per-machine install, which rexenv does not ship and will not silently elevate to replace.

On Linux rexenv replaces exactly two shapes, the .deb install and an AppImage, and says so to anything else — a build from source reads no descriptor at all:

Install rexenv from its .deb (sudo apt install ./rexenv_<version>_<arch>.deb) or run it as the AppImage from a folder you own, then update from there.

An AppImage in a read-only or root-owned folder is refused with the fix named, since the swap writes beside the file.

The .deb update’s password dialog has its own sentence — “rexenv needs administrator permission to install the update it downloaded — the new rexenv package.” — rather than the one first-run setup shows. It comes from a file the package installs, so the first update from 0.8.10 or earlier still shows the setup sentence, and the update after it shows this one.

With rexenv’s apt repository configured, sudo apt upgrade updates rexenv too. It installs the same package the button does, so neither route can leave the other confused about what is on the machine.

The macOS refusals:

Where rexenv is running from What to do
A mounted disk image Drag rexenv.app into Applications, open it from there, then update
The folder it was downloaded to Same — macOS is running a temporary read-only copy of it
Anywhere that is not Applications Move it into Applications and open it from there
A symlinked path Open the real copy; the running one may not be the one that gets replaced
An Applications folder you can’t write sudo chown -R "$USER" /Applications, or ask an admin to update rexenv
A bundle owned by another account Have that account update, or take ownership of the bundle first
A disk without room It needs about three times the download free — the archive, the extracted copy and the previous bundle. Nothing is downloaded until that check passes

The cask declares auto_updates, so a plain brew upgrade leaves rexenv alone and the app updates itself. That is what stops brew and the app from installing over each other.

Terminal window
brew upgrade --cask rexenv

Naming the cask overrides auto_updates — so do --greedy and reinstall. All of them install whatever your local tap checkout names, which means any of them can move you backwards over a version the app installed for itself. brew info --cask rexenv reads the version out of the installed app rather than brew’s own receipt, so it stays honest after an in-app update either way.

Quit rexenv — Quit rexenv in the menu-bar menu; closing the window only hides it — then drag the new copy over the old one in Applications.

Quitting the app leaves one rexenv process running on purpose: the .rex resolver, which macOS restarts the instant it exits — including halfway through your copy. A program started from a half-copied bundle is killed by macOS, which reported it as “rexenv quit unexpectedly”. The resolver now waits until the new copy verifies before it starts from it, and starts as soon as it does — so the swap is quiet, and .rex stops answering only for as long as the copy takes.

The Gatekeeper right-click → Open step is only needed the first time you ever install — an update over an app you have already approved opens normally.

Everything that matters, on every platform:

  • Your sites, their files and their databases
  • Downloaded components — nothing re-downloads
  • Settings, blueprints, certificates and CA trust
  • The DNS mechanism — resolver files and the edge daemon on macOS, the NRPT rule and the logon task on Windows, the rexenv0 route, the edge unit and the DNS agent on Linux

They live in rexenv’s data folder and in the system locations listed under Where things live, never inside the app itself.

Component versions do not change on their own

Section titled “Component versions do not change on their own”

Updating rexenv can bring new pinned versions of PHP, nginx or a database engine. Those are part of the release, listed in its notes and in the changelog, and never a background download that changes your stack while you are working — see Pinned versions.

A new PHP minor becoming available does not switch any existing site to it.

Two components can also move between rexenv releases, and both are a button you press rather than something that happens to you:

  • A PHP patch — 8.3.32 → 8.3.33 — from Settings → PHP versions, one minor at a time, restarting that minor’s pool and reverting if it does not come back. If the offered build cannot reach PostgreSQL, the row says so before the click. See Patch updates.
  • Adminer, from the card under the engine table on the Databases screen. See Updating Adminer.

Both resolve only versions a signed manifest describes, both are floored by the version your build pins, and neither ever runs at launch.

Terminal window
rex version

Prints the app and CLI versions. They should match; a mismatch usually means an old rex is earlier on your PATH than the one the current install linked.

rex --version answers without the app running, and names the commit the binary was built from — so a rex run off a mounted disk image reports what is on that image rather than what happens to be running on the machine.

In the app, Settings → About — and, on macOS, rexenv → About rexenv and the same item in the menu-bar menu, for when no window is open — carries a Build card: version, commit, build date, platform and Tauri version. The menu item opens rexenv’s own About rather than the bare macOS panel, which can show a name and a version and nothing else — and “which build is this?” is the question a stale install once turned into a whole misdiagnosis.