Install Mend
Install the CLI on each device. Set up the server on the machine that will keep your projects.
Choose where Mend runs
Section titled “Choose where Mend runs”| Deployment | Shape | Use it when |
|---|---|---|
| Your own machine | Local Docker server | Server and client share a machine |
| VPS or home server | The same Docker setup on a remote machine | Work should continue when your laptop sleeps |
| Kubernetes | Operator-managed Helm deployment | You already operate a cluster |
mend server setup currently provisions Docker, not Kubernetes.
Requirements
Section titled “Requirements”- Node.js 22.13 or newer for the CLI. The TUI, the dashboard
mendopens in a terminal, requires Node.js 26 or newer. - For the server, a local Docker daemon with client/server API 1.45 or newer and Docker Compose v2.
- Disk space for repositories, session captures, images, databases, and backups. The two mirrors setup runs beside Mend take up to 30 GiB more by default (npm 10g, Docker Hub 20g), and each leaves 5 GiB free on its disk.
- For access from another device, a network path you choose: a private network you control admission to, or a TLS edge in front of Mend. Read Exposure.
Docker Desktop and OrbStack must pass the same capability checks as Docker Engine. On a machine with
more than one Docker engine (Docker Desktop and OrbStack on one Mac), --context <name> (or
DOCKER_CONTEXT) picks the engine; without either, a fresh install on a terminal asks which one,
and an existing install stays on the context its data is on. For a Mac mini serving laptops, read
Mac mini and VS Code. Physical macOS and installed VS Code acceptance
are recorded separately in the
validation checklist. Linux
checks are not evidence that MacBook-to-Mac-Mini operation has been verified.
Install only the CLI
Section titled “Install only the CLI”npm install --global @sealant/mendThis installs only the CLI. It does not install Docker, create a server, or start a service. To try the next release before it ships, read Try a preview.
Set up the server
Section titled “Set up the server”On the server machine, in a terminal:
mend server setupSetup asks a few questions, one at a time, and says in a line what each choice means. Enter takes the answer in brackets.
- Which Docker should Mend run in? Asked first, and only when more than one Docker engine on
this machine answers: Docker Desktop beside OrbStack, colima, Rancher Desktop, a rootless Docker.
Each choice says what it is, its socket, and whether a Mend server or Mend’s volumes are already
there; the current Docker context is the answer in brackets. With one engine, setup says which one
it uses and asks nothing. A context on another machine (SSH or TCP) is listed, not offered: Mend
runs in a Docker engine on this machine.
--context <name>orDOCKER_CONTEXTanswers it for you. - Allow user namespaces? Asked next, only on a Docker host whose kernel refuses unprivileged
user namespaces (Ubuntu 23.10 and later, 24.04 LTS included), where no session can start:
Allow them now? [Y/n]. A yes writes/etc/sysctl.d/60-mend-rootless-docker.conf, marked somend uninstall --allremoves it again;--allow-usernsanswers for a script. See Every session fails to launch on Ubuntu. - How will people reach this Mend? Just this machine, your private network or Tailscale, or the public internet with HTTPS.
- For Tailscale, setup reads
tailscale statusand offers this machine’s MagicDNS name and tailnet address. When Tailscale Serve already forwards an https name to Mend’s port, it offers that name as a browser origin too. A name with Funnel on is public: setup says so, offers it with No as the answer, and not at all on a fresh install. Setup never changes Tailscale’s own settings. - For a private network, setup offers only this machine’s private addresses: the tailnet, a LAN or VPN address, carrier-grade NAT space. A public address is never one of them. When the machine has no private address, setup says so and installs on this machine; join it to a tailnet, or choose public HTTPS.
- For public HTTPS, it asks for your domain, then says where the domain resolves from this
machine and whether something already listens on ports 80 and 443. When the domain does not
resolve from this machine, Enter at
Apply?changes nothing, and setup says why. - For a private network or public HTTPS, it asks whether VS Code Remote-SSH should reach sessions from other machines, and where workspace SSH is published. On a public install it also asks whether you checked who can reach that port.
- Whether to turn on the T3 Code gateway, whether to keep the mirrors as they are, and whether the server holds one organization or several.
Setup never asks which address to bind: that follows from your answers. Each line that starts with
Observed: is something setup looked at on this machine, not a judgment about who can reach it.
Before anything is pulled, setup checks that Mend’s web and SSH ports are free where they are to be
published. When one is taken it says what holds it (another Mend, 0.27.4, answers there, or
something else) and offers the next free port. Setup ends with what it will do and the same command
with flags, and changes nothing until you say yes:
Setup will install: reached your network, at http://mend-box.tailc79e49.ts.net:3105 (listening on 100.94.101.28) exposure declared private workspace SSH published on 100.94.101.28:2222, beyond this machine T3 Code gateway off organizations one organization (single) mirrors npm on, 10g · Docker Hub on, 20g extra origins https://mend-box.tailc79e49.ts.net:8443Same as: mend server setup --bind 100.94.101.28 --url http://mend-box.tailc79e49.ts.net:3105 --origin https://mend-box.tailc79e49.ts.net:8443 --exposure privateApply? [Y/n]On a Mac with both Docker Desktop and OrbStack, the engine question looks like this:
Mend's server, its data and every session's workspace live in the Docker engine you choose, and stay there: another engine later means a new install.
Which Docker should Mend run in? 1. desktop-linux (the current context) · Docker Desktop · socket /Users/you/.docker/run/docker.sock · nothing of Mend's here yet 2. orbstack · OrbStack · socket /Users/you/.orbstack/run/docker.sock · nothing of Mend's here yet 1 or 2 [1]: 2Pick the engine you keep running on that Mac: Mend’s own Mac runs used OrbStack, and Docker Desktop
works when it starts at sign-in (Mac mini guide). Your choice goes into
the command setup prints (--context orbstack), and mend doctor and mend uninstall use the same
context from then on.
Run it again to change something. Setup shows what is saved, then lets you keep it, change one
thing, or go through every question. It keeps the Docker engine it was installed in and says so:
moving to another engine is a new install, mend uninstall --server and then mend server setup
there. Turning on the T3 Code gateway on a public install looks like this:
Currently: public HTTPS at alpha.mend.run, VS Code SSH from other machines on, T3 gateway off. …What would you like to do? 1. keep it as it is · setup checks this install and starts it again 2. change something · pick it, answer, done 3. go through every question 1-3 [1]: 2
What should change? 1. how people reach it · the public internet, over HTTPS at alpha.mend.run (the edge, Caddy, on 80 and 443) 2. VS Code Remote-SSH from other machines · published on 0.0.0.0:2222, beyond this machine 3. the T3 Code gateway · off … 1-7 [1]: 3
The T3 Code gateway lets T3 Code's desktop, mobile and web apps pair with this Mend: in T3 Code, add a Remote link with its address and a code from mend pair. T3 Code's SSH mode is not for Mend: it starts T3 Code's own server on this machine.Behind the edge, its port stays on 127.0.0.1:3120, as Mend's own does; the edge can serve it on a host of its own.Turn on the T3 Code gateway? [y/N] y
T3 Code always uses a server's root path, so the edge cannot serve the gateway under alpha.mend.run. Serve it on a host of its own? 1. yes, on a host of its own · such as t3.alpha.mend.run, whose DNS points here as alpha.mend.run's does; Caddy gets its certificate 2. no, not through the edge · it stays on 127.0.0.1:3120: this machine, or a tailnet through Tailscale Serve 1 or 2 [2]: 1The gateway's host [t3.alpha.mend.run]:Observed: t3.alpha.mend.run resolves to 203.0.113.7, as alpha.mend.run does.
Change something else? [y/N]
What changes: T3 Code gateway: off → on, at https://t3.alpha.mend.run through the edge (its port on 127.0.0.1:3120)Same as: mend server setup --t3-gateway-origin t3.alpha.mend.runApply? [Y/n]On a private install the same question says where T3 Code reaches the gateway: on the address Mend’s
web port is published on, such as http://mend-box.tail1234.ts.net:3120 over Tailscale, with no
tunnel.
At idle, five product containers run:
- The complete Mend application, including its pinned Sealant API/worker/SSH runtime. Sealant’s job queue runs in Postgres; workspace images are built and launched in the host Docker Engine through the mounted daemon socket.
- Official Postgres, with separate Mend and Sealant databases and users.
- Garage, an S3-compatible object store that holds every session’s captures: the worktree files and harness state that workspaces upload as they work.
- The npm mirror and the Docker mirror, caches
that sessions download packages and Docker Hub images through. Neither publishes a host port;
--no-npm-mirrorand--no-docker-mirrorturn them off.
With --edge, a sixth runs: Caddy, the TLS edge.
You manage the Mend version. There is no separate Sealant installation or version choice for this Docker setup. Session workspaces may create additional containers. Repositories, session captures, database data, and SSH identity persist in Docker-managed volumes.
With flags
Section titled “With flags”Scripts and CI pass flags instead. The Same as: line is the flag command for what you answered,
and mend help server setup lists every flag. On an existing install, each flag changes only what
it names and setup keeps the rest, so mend server setup --t3-gateway turns the gateway on and
leaves everything else as it was. Setup prints This run changes: and one line per change before it
applies. --declare <item> adds a statement to the saved ones, --undeclare <item> takes one back,
and --declare none clears them all. A rerun with --port moves a saved plain-http --url that
names the old port to the new one; an https URL, or one whose port is implicit, stays as it was. A
web or SSH port that something else holds is refused with flags, naming --port or --ssh-port.
After starting, setup waits for health from the server it started, by version and instance id, so
another Mend answering on the same port is said as one.
With no terminal and no flags, setup refuses a fresh install instead of guessing how the server is
reached, and names the flags that say it. mend server setup --yes takes the defaults: this machine
only, at http://localhost:3105.
Network boundary
Section titled “Network boundary”Web and SSH bind to localhost by default. Postgres has no published host port, and no image registry is published. Private access must be configured explicitly: answer “my private network or Tailscale” to setup’s first question, or pass the flags yourself:
mend server setup --bind 0.0.0.0 --url http://mend-host:3105 \ --origin http://localhost:3105Use your server’s reachable private hostname. The primary URL and additional exact origins govern authentication, CORS, WebSockets, pairing, and advertised URLs. Incoming forwarding headers and interface discovery cannot add trust.
Binding
0.0.0.0exposes web and SSH on every IPv4 interface. Registration closes after the first account, so create that account before anyone else can reach Mend. The application has administrative Docker socket access. Configure your firewall or private network yourself; setup does not do it for you.
Plain HTTP does not protect credentials on an untrusted network. Use an encrypted private network or a TLS edge. Setup runs the edge for you once the first account exists: answer “the public internet, with HTTPS”, or pass the flag:
mend server setup --edge mend.example.comCaddy then listens on ports 80 and 443 of every interface, obtains and renews a certificate for
mend.example.com and proxies to Mend, whose own port stays on loopback. The browser origin becomes
https://mend.example.com. For a certificate to be issued, the name’s DNS must point at this
machine and both ports must reach it from the Internet. mend server status says whether Caddy
holds one. A fresh install refuses --edge: until the first account exists, registration is open to
whoever reaches the origin first, so set up on localhost, create the account, then add the edge. The
questions say the same on a fresh install and set up on this machine first.
Setup also takes the posture the server declares, --exposure loopback|private|public and
--tenancy single|multi, and keeps the edge and the posture across reruns and upgrades. Without the
flags a server it installs runs as private and single. public needs the edge and an existing
first account. Read Exposure for the modes, the public exposure gate, and what
Mend observes beside what you declare.
Create your Mend account
Section titled “Create your Mend account”Open http://localhost:3105 and create an account. The first account on an instance becomes the
owner of its organization and the instance’s operator. Registration then closes. The next page, Git
access, asks how Mend reaches your repositories: with a key of yours held on the server, or through
the ssh-agent on your machine. Read Configure Git access.
Then sign in from the terminal:
mend login --url http://localhost:3105For a remote server, use its configured URL instead. mend login opens the authorization page.
Compare its code with your terminal before approving. The CLI receives its own revocable device
token; it does not ask for your password in the terminal.
Everyone else joins by invitation. An owner runs mend invite (or uses Settings on the web) and
shares the printed one-time /join/<token> link; whoever opens it creates an account in the
organization. Read Organizations.
Connect providers
Section titled “Connect providers”Run these where your credentials live, usually your laptop:
mend connect claudemend connect codexmend connect githubmend accountsmend doctorUse only the providers you need. Signing in to Mend and connecting a provider are separate actions.
See provider accounts and Git access.
mend doctor --bundle writes one redacted diagnostic archive to attach to a bug report; read
Troubleshooting.
Operate and upgrade
Section titled “Operate and upgrade”mend server statusmend server logs --tail 100mend server stopmend server startmend server restartStop stops the installation’s containers (Mend, Postgres, Garage, the mirrors, and the edge when there is one) without deleting volumes. Restart restarts Mend and keeps the others running. These operations interrupt connections; workspace containers remain.
Setup reruns preserve the server pin, secrets, and data. Updating the CLI does not upgrade the server. To change the server, explicitly choose a published version:
mend server upgrade --version VERSIONUpgrade validates the target, stops application writers, and saves a private database backup before
target activation. A failure after target startup retains the target pin and recovery files. There
is no automatic database restore or downgrade. Back up Docker volumes and private configuration too;
the SQL dump is not a backup of repositories, session captures (the mend-garage volume), or SSH
identity.
Each backup is a full dump under ~/.config/mend/backups/upgrade-UUID/ and can run to gigabytes.
After the new version answers health, the upgrade keeps the newest two completed backups, its own
included, removes older completed ones, and prints each removal with the space it freed:
Removed upgrade backup /home/me/.config/mend/backups/upgrade-3f2c… · 2.4 GiBUpgrade backups · removed 1 · 2.4 GiB freed · kept 2 (--keep-backups 2)Choose the count with --keep-backups N; --keep-backups 0 keeps every backup. A failed upgrade
removes nothing. A backup that 0.36 or later recorded as pending (its upgrade never saw a healthy
target), or whose dump is incomplete, is always kept and listed; remove it yourself once its
recovery no longer needs it. Directories Mend did not write are left alone.
Backups written by releases before 0.36 carry no outcome. The first upgrade on 0.36 or later treats
each one whose dump is whole as completed and keeps only the newest N, including the backup of an
old upgrade that failed after its target started. Copy any you want to keep out of
~/.config/mend/backups/ first. Their removals end in · from before 0.36, no recorded outcome.
The self-hosting guide covers offline assets, port selection, ownership conflicts, locks, and upgrade recovery. The retired host installer is not an automatic migration path into this volume-backed deployment.
Uninstall
Section titled “Uninstall”mend uninstallChoose everything, the server only, or this machine’s files only (--all, --server, --home).
While another server command holds the server lock, uninstall refuses with the lock’s words. The
plan is printed before anything is removed; taking the server down deletes its volumes
(repositories, the mend-garage session captures, the database) and asks for the word delete.
Data volumes go only when they carry this installation’s identity label. Live sessions are listed in
the plan; uninstall stops their workspaces and removes them with their volumes and networks. If
Docker is stopped, nothing is removed.
In ~/.ssh/config, uninstall removes only the blocks mend ssh setup wrote for the server it
removes and the one this machine is signed in to. Other servers’ blocks stay. So does the unscoped
Host mend-ws block an older release wrote, because nothing in it says which server it is for:
uninstall names it and how to delete it by hand.
If Docker refuses to remove something, uninstall names it, writes it to uninstall-left.json, and
keeps the mend-store volume and the identity, so running mend uninstall again finishes the job,
and mend server setup can reinstall over it. Everything also offers Mend’s images (the metadata
guard’s busybox among them), the user-namespace sysctl file setup wrote, and, in its own question,
Docker’s build cache. When an uninstall stops before those, it says each one was “not reached”, and
the next run removes them. The CLI stays until npm uninstall -g @sealant/mend.