Skip to main content
A box is a Linux machine in the cloud that holds a copy of your worktree at ~/workspace. Tesser creates boxes, syncs your worktree to them, runs commands on them, and puts them to sleep when they are idle. You do not deal with instance ids, SSH keys, or IP addresses; tesser status will show them if you want to see them. Each box has 2 vCPU and 8 GB of memory by default — --size picks small (1 vCPU, 4 GB) through xlarge (8 vCPU, 32 GB), with large and xlarge on Pro and Team — with a 40 GB disk, running Ubuntu 24.04 with Node (22 and 24, via fnm), Bun, rsync, and Docker (compose included) installed. The ubuntu user has passwordless sudo and is in the docker group, so sudo apt-get install and docker compose up both just work. You can check what else is available with tesser exec -- which pnpm and install what your project needs with tesser exec.

Two kinds of box

A workbench holds your worktree and runs nothing on its own. It is the box tesser exec uses when you do not name one, and it is where tests, typechecks, builds, and one-off scripts run. Each worktree gets a workbench the first time it needs one. An instance box runs one service’s dev server. tesser dev <service> creates one for each combination of worktree and service, so a repository with a frontend and a backend ends up with two instance boxes and one workbench. Keeping these separate means a test run never competes with the dev server for CPU, and an install on the workbench never replaces the dev server’s node_modules while it is running.

Creating a box

Most of the time you do not create boxes directly, because exec creates the workbench and dev creates the instance box. tesser make exists for when you want a box before running anything on it: tesser make creates a workbench and tesser make <service> creates an instance box. Both print the new box id. When a warm box of the requested size is available in the pool, creating a box takes a second or two. Otherwise a new machine is launched, which takes about 90 seconds. On Team, tesser pool target, run inside a repo, keeps a couple of warm boxes for that repo ready, and lets them expire an hour after the last box made there. Only a box made from that repo claims one. Every org has a box cap from its plan: 3 boxes on Hobby, 10 on Pro, none on Team. When make, exec, or dev fails with box_limit, remove an idle box with tesser rm or upgrade. tesser sleep does not help: the cap counts boxes, not awake ones. Pro and Team bill usage past what the plan includes. An admin can cap that with tesser spend-limit 50 or on the dashboard’s Billing tab: when the org reaches it, its awake boxes sleep, and launches and wakes fail with credit_exhausted until the limit is raised or the month resets. A brand-new box also warms itself from its siblings automatically: right after it boots, it copies the pnpm, npm, yarn (classic and berry), bun, cargo, go, and uv stores from up to three awake boxes in the org, over the private network. When one of those boxes was made from the same repo — any service or workbench — every node_modules in its workspace comes along too, so the first npm install is the incremental one — seconds, not the cold install. With bun or pnpm, whose node_modules are hard links into the store, the store is what comes across and the first install relinks it. A pool box kept for a repo (tesser pool target run inside it) gets that repo’s node_modules at launch, before anyone claims it, so dev there starts on an install that is already in place; a blank pool box (--any-repo) gets them when it is claimed. A box keeps packed copies of what it hands out, so a sibling that has been idle for hours serves as fast as one that just installed. The first dev or exec on that box waits for the copy to finish (up to four minutes) and prints how it ended, so an install never starts mid-copy. This is best-effort — if no sibling is awake or the copy fails, the install simply runs cold — and it never touches your code, env files, or anything outside the package stores and node_modules. The node_modules come from whatever branch that sibling has installed — same repo, not necessarily the same lockfile — which is exactly what the incremental install reconciles; and unlike the stores, whose contents the package manager verifies, they are trusted as-is from any box in the org.

Syncing

Every exec and dev copies your worktree to the box before running. Only changed files are transferred, and .gitignore is respected, so node_modules, .env.local, and build output stay on your laptop. The box gets its own node_modules when the setup command runs. If a sync would delete most of what is on the box, Tesser stops and reports it, because that usually means the box was created from a different worktree. In that case, create a new box rather than passing --force. The copy on the box is a real git repository with a single commit, your current HEAD. git status and git diff work there, and git log shows one entry. Anything you commit on the box is lost at the next sync, so always commit on your laptop.

Sleep, wake, and removal

Idle boxes go to sleep on their own. A sleeping box keeps its disk and its id and costs a few cents a day. Any command that targets a sleeping box wakes it first, which takes about 40 seconds, and if the box had a dev server running it is started again. Opening a sleeping box on localhost shows a Wake button that does the same. tesser sleep-after 30m sets your own idle time for the instance boxes you own in the org, anywhere from 5 minutes to 2 hours, or up to 24 hours on Pro and Team; --default goes back to 2 hours. tesser dev --sleep-after 30m, or tesser sleep-after 30m --box <box_id>, sets one box’s idle time instead, over its owner’s. A box counts as idle when nobody is using it. Using it means one of these: a browser tab showing it that is visible, a request from something other than a browser (curl, a script, a test run), a CLI command, or an SSH session where someone is typing or something is running or printing. A background tab, or a shell left open with nothing running in it, does not keep a box awake. Traffic between boxes does not count on its own, but a box you are using keeps the boxes it depends on awake, so a server and its database idle together. You can also act immediately: tesser sleep <box_id> powers a box down, and tesser rm <box_id> removes the box permanently. Neither asks for confirmation. A box that must never go down on its own, such as a database the rest of the org depends on, is kept (on Pro and Team): tesser keep <box_id> takes it off the idle clock entirely, so it neither sleeps nor expires, and bills for every second it is awake. tesser keep <box_id> --off puts it back. Explicit sleep and rm still work on a kept box.

Finding a box

tesser ls lists every box in the org with its kind, service, power state, whether it is currently online, when it was made, and the local worktree it was made from. In a terminal it opens a live table instead: press a column’s number or click its header to sort by it, press / to fuzzy search, and click a box twice to use it on localhost. tesser status <box_id> shows the details of one box, including its IP addresses and when it was last active. Both accept --json.

Looking inside a box

tesser ssh <box_id> opens a shell in ~/workspace, and tesser ssh <box_id> -- <cmd> runs a single command there without syncing first. Use these for inspection rather than editing, since an edit made on the box is overwritten by the next sync. To read the dev server’s output, use tesser logs <box_id> for the last 200 lines or tesser logs <box_id> -f to follow it.