Install the node
One command as root turns a clean Ubuntu server into your node. Give it the token from your detachbox contact and the empty disk for the pool:
curl -fsSL https://api.detachbox.com/api/v1/node-host/install \
| sudo bash -s -- <token> --disk /dev/sdb
It takes 30 to 60 minutes, most of them spent building OpenZFS and the first base image. It ends with "The node … is live", and your account's terminals open at https://my.detachbox.com.
On a server other people log in to, leave the token out: every local user can see a command line while the install runs. Without it, the command asks for the token on your terminal as detachbox enrollment token: and does not show what you type.
curl -fsSL https://api.detachbox.com/api/v1/node-host/install \
| sudo bash -s -- --disk /dev/sdb
What the command takes
| Argument | What it does |
|---|---|
<token> | The enrollment token: one server, one install. Left out, the command asks for it |
--disk /dev/sdb | The whole, empty disk for the node's pool; a /dev/disk/by-id/… name works too |
--private-network 10.20.0.0/16 | A private network the terminals may reach; repeat it for more, none for none. What terminals reach explains it |
--verbose | Every line of the commands it runs on the screen too, not only in the log |
--check | The checks alone: nothing on the server changes, and the token is not spent |
A rerun without --private-network keeps the networks the earlier run was given. Once the node has enrolled, change them with private-networks, not with the install command.
The checks come first
The command opens with a table of checks: the system, root, systemd, the root filesystem, other containers or tunnels on the server, CPU and RAM, the disk, the private networks, the clock, Secure Boot and the kernel headers when ZFS has to be built, DNS, every outbound destination, TLS and the token. Each line reads ✓ or ✗, and under each ✗ is the fix. Nothing on the server changes until every line is ✓.
To see the table before the day you install, put --check after the token:
curl -fsSL https://api.detachbox.com/api/v1/node-host/install \
| sudo bash -s -- <token> --check --disk /dev/sdb
It runs the checks alone and ends with Nothing on this machine changed and the token is not spent: the same command without --check installs the node. The node-host it downloaded and verified stays on the server for that next run, as after every run.
The eight steps
- Packages: the newest Incus from Zabbly's stable repository (7.6 or later) and the newest cloudflared from Cloudflare, from their own apt repositories, with their signing keys built into the installer. The install holds both (
apt-mark hold): a restart of either cuts the open connections to the node for a few seconds, so they move only at a moment you pick, or on a day no terminal runs. - OpenZFS 2.4.4 and the ZFS cache limit. The installer builds OpenZFS from the official release, its SHA-256 pinned and its signature checked with the OpenZFS release key, into packages with a DKMS module, so every kernel with headers gets it. It swaps Ubuntu's module for the new one without a reboot.
- The ZFS pool
boxeson your disk. - Incus: its storage, two bridges (one for the terminals, one for the node's edge), their firewall rules, the projects, and Incus's own API on the edge's bridge only.
- The first base image: Ubuntu with Docker, Claude Code, Codex and OpenCode, 10 to 20 minutes. The node rebuilds it every 6 hours.
- Enrollment with api.detachbox.com: the node gets its name, its keys, its Cloudflare Tunnel and its own domain under detachbox.dev; node-host gets its config and units.
- cloudflared and node-host start.
- The server's bring-up: detachbox reaches node-host through the tunnel, starts the edge (the terminal gateway and the preview proxy), runs a smoke test and sets up your account's Compute. The command prints each stage as the server reaches it.
Each step prints the part it is at, such as the configure, compile and packaging of the OpenZFS build. While a long part runs, a line comes every 30 seconds.
A rerun picks up where it stopped
If anything stops the install, such as a blocked host, a dropped connection or a reboot, run the same command again. It continues from the step that stopped, and work that finished is not done twice. The node-host it downloaded and verified stays on the server, root's only, until the install puts it in place. A token downloads node-host three times, then once every 30 minutes; the command says how long to wait.
- When the remote shell you started it from drops, the install stops where it is. Started without a terminal, from a script or a tool with a time limit, it keeps running. On a shaky connection, run it inside
tmux. Ctrl-C, orsudo kill <pid>from another session, stops a run within seconds, together with everything its step started.- One run at a time. Start the command while a run is at work and it changes nothing: it says since when that run has been working, its process, and how to follow it.
- A rerun goes on the same disk. To start over on another disk, uninstall first.
When a step fails
The command prints what failed, why, and the command or change that fixes it, with the last 30 lines of the failing command. The failure goes to detachbox with your token, so your detachbox contact sees it too. Fix it and run the same command again. Node troubleshooting covers the usual ones.
The log
/var/lib/detachbox-node/install/install.log holds every run whole: the line that names its start and release, every line of every command it ran, and how it ended. If you lost the session, the log says how the run ended. To follow a run from another session:
sudo tail -f /var/lib/detachbox-node/install/install.log
What runs on the server
| What · runs as | What it does |
|---|---|
detachbox-node-host · root | The only way detachbox reaches the node: it carries out the server's signed requests on the local Incus, holds the account's CPU and RAM budget, rebuilds the base image and updates itself from signed releases |
cloudflared-detachbox · cloudflared, sandboxed | The node's Cloudflare Tunnel, outbound only |
| Incus · root | The terminals, each a container with its own Docker |
| The edge container · inside Incus | The terminal gateway and the preview proxy |
What lands where
| Path | What |
|---|---|
/usr/local/bin/detachbox-node-host, /usr/local/lib/detachbox-node/ | The program |
/etc/detachbox-node/ | Its config (node-host.json) and keys |
/var/lib/detachbox-node/ | Its state; the install's record and log in install/ |
/etc/systemd/system/detachbox-node-host.service, /etc/systemd/system/cloudflared-detachbox.service | The two services |
/etc/cloudflared/detachbox.token | The tunnel's token |
/etc/apt/sources.list.d/, /etc/apt/keyrings/ | The Zabbly and Cloudflare repositories and their keys |
/etc/sysctl.d/90-detachbox.conf, /etc/modprobe.d/zfs.conf | The node's sysctl settings and the ZFS cache limit |
/usr/src/openzfs-build | The OpenZFS build |
The pool boxes on your disk | Your account disk, the terminals' system disks and the base images |