Three things version independently: the per-host agent, the server, and cloud-hypervisor. None of them touch running VMs.
The fleet overview shows each host's agent version. When the server knows a
newer release (it polls https://eitri.sh/dl/latest/manifest.json daily), an
↑ button appears next to hosts that are behind. Clicking it tells that one
agent to upgrade itself:
eitri-agent out of it.eitri-agent.prev—and re-execs. The process keeps its PID; running VMs
are untouched and stay under the agent's care throughout.If a download fails, nothing is swapped; click again to retry. To roll back by
hand, stop the agent, move eitri-agent.prev back over the binary, and start
it again.
The release pipeline makes this same offer to every connected host after it
rolls a plane, and waits for them to report the new version, so a hosted fleet
arrives at a release without anyone clicking through it host by host
(deploy/server/README.md, stage 8).
Requirements. The button lights up only when the running agent's version
orders before the published release. Release tags (vX.Y.Z), pre-releases
(vX.Y.Z-pre.N, which order below the release they lead to), and git-describe
builds (vX.Y.Z-N-g<hex>, N commits past the tag) all order, so a
hand-deployed describe build behind the latest release is offered the upgrade,
and one ahead of it is never offered a downgrade. Anything else—dev, a
-dirty tree—orders before nothing and is never offered anything. The server
needs release_manifest_url reachable; set it to "" in the server config to
disable upgrade checks entirely.
An agent whose version never orders—a dev or -dirty build—never gets the
button. The manual path is the same swap, done by you.
On Linux, take the current release from /dl/latest/ and read its version out
of SHA256SUMS; on an arm64 host, _arm64 replaces _amd64 throughout:
curl -fsSLO https://eitri.sh/dl/latest/SHA256SUMS
V=$(sed -n 's/.*eitri-server_\(v[^_]*\)_linux_amd64\.tar\.gz$/\1/p' SHA256SUMS)
curl -fsSLO "https://eitri.sh/dl/latest/eitri-server_${V}_linux_amd64.tar.gz"
grep " eitri-server_${V}_linux_amd64.tar.gz$" SHA256SUMS | sha256sum -c -
tar xzf "eitri-server_${V}_linux_amd64.tar.gz"
sudo cp /usr/local/bin/eitri-agent /usr/local/bin/eitri-agent.prev
sudo install -m 0755 "eitri-server_${V}_linux_amd64/eitri-agent" /usr/local/bin/eitri-agent
sudo systemctl restart eitri-agent
The host bundle is the artifact—there is one set of bytes per platform per
release, and it is the same one the console's button fetches. Each release also
has an immutable home at /dl/<version>/, but only the current one is served,
so /dl/latest/ is the address that always answers.
Running VMs survive the restart—the unit's KillMode=process (see Operations
below) leaves the guests alone. The .prev copy mirrors what the button path
keeps, so rollback is the same either way.
On a Mac the binary lives in the running account's own space (e.g.
~/.local/bin/eitri-agent). Overwrite it with eitri-agent from the darwin
bundle (eitri-agent_<version>_darwin_arm64.tar.gz), then restart the
LaunchAgent:
launchctl kickstart -k gui/$(id -u)/sh.eitri.agent
No sudo anywhere, same as the install. If guests turn unreachable through
the gate after the swap ("cannot reach VM"), check the agent's Local Network
permission—the quickstart's "Join a Mac" section covers it.
If the swapped-in agent reports it is not enrolled, it is running as a
different account than the one that joined: enrollment state is per-account
(~/.eitri/agent on macOS, /var/lib/eitri-agent on Linux), so under a new
account the binary is a new host. Join it fresh from the console, then
decommission the old host row it leaves behind. If that old agent is gone for
good, the delete needs force—the dead-hardware escape hatch on host delete.
The console banner links here when the published release differs from the running server's version. Server upgrades are manual and downtime is fine—agents keep reconciling and VMs keep running while it's away:
eitri-server.A newer server with older agents is safe: new fields in the sync protocol are ignored by agents that predate them.
One exception, from v0.0.4 on. A guest's SSH host key is generated by the
host that runs it, and an agent from before v0.0.4 does not know how to do
that. The server will not place a VM on such a host: the create is refused with
the host's name, the version it reports, and the endpoint that upgrades it —
nothing could verify a guest created there. A connected host that has reported
no version at all is read the same way, since nothing says otherwise. Only a
host that is connected is judged: one that is offline has told this server
nothing about the agent that will eventually run the VM, so the create is
accepted as desired state and the guest is verified — or refused — when it
boots. Placement chosen for you skips those hosts entirely. Existing VMs are unaffected — they
keep the host key and certificate they were built with; one created against an
older release with an uncertified key is refused by eitri ssh at host
verification, and re-creating it after the upgrade is the fix.
Swap /usr/local/bin/cloud-hypervisor on the host. New and restarted VMs use
the new binary; running VMs keep their old process until they stop. There is
no live handover for running guests.
The agent runs under systemd (eitri-agent.service). The unit sets
KillMode=process—that line is load-bearing: the default would kill every
cloud-hypervisor guest in the unit's cgroup whenever the agent stops. Logs:
journalctl -u eitri-agent.