Your dev environment on a cheap VPS — reachable from every device you own, including your phone. Code, long-running builds, and AI coding agents that keep working after you close the laptop.
This isn't a theoretical tutorial: it's the exact setup we run daily, extracted into a repo. Browser IDE + tmux on a VPS, one named session per project, one-tap attach from an iPhone via Termius, secrets from 1Password/Bitwarden, drag-and-drop file transfer, an orchestrator pattern ("Hermes") where one agent supervises every session, and a read-only status door for a hosted bot that helps run the work while you keep the final say.
┌────────────────────────── YOUR VPS (OVH / Hetzner / any) ─┐
Desktop browser ──────► HTTPS (Coolify/Traefik + optional Cloudflare Access) │
Desktop terminal ─────► ssh host → docker exec ─┐ │
Laptop / VS Code ─────► ssh :2222 (private tunnel by default) ─────────────────┐ │
iPhone (Termius) ─────► ssh :22 forced-command ─┼──►│ devbox container │ │
one-tap picker "devbox-attach" menu │ │ code-server (browser IDE)│ │
Any device w/ key ────► direct :2222 only after explicit exposure/firewall ───┘ │
│ │ tmux ── main │ │
│ /data/devbox/* bind ──────►│ ├─ api (agent) │ │
│ mounts (survive rebuilds) │ ├─ blog (agent) │ │
│ │ └─ hermes (orchestrator)
└──────────────────────────────┴──────────────────────────┴─┘
- Code lives on the server, not your laptop. Close the lid mid-build; reattach from your phone; nothing died.
- One session per project — run several AI agents / builds / servers in parallel and
hop between them from any device with the same 3 commands (
devbox,devbox ls,devbox kill). - Phone access done right — Termius + a forced-command SSH key = one tap opens a numbered menu of your live sessions. Lost phone = delete one line to revoke.
- Rebuild-proof persistence — host bind mounts + a relink script mean image rebuilds keep your code, tools, and every CLI login (GitHub, Claude, etc.).
- Secrets that follow you — 1Password service account (or Bitwarden) read by the
box; repos commit only
op://references, never keys. - Files in, from anywhere — a Dock drop-folder, a screenshot command, SFTP from the
phone; everything lands in
~/project/_inbox/. - Hermes — a tiny tmux wrapper that lets one agent list, read, and type into every other session. Orchestration with zero infrastructure.
- Several agents per project, at a glance — each agent gets its own worktree, branch,
and tmux window;
devbox-session status <project>shows what every one of them has done, which one may currently write to each worktree (holds the turn), and which are safe to clean up. - A bot as your project manager, on a short leash — run a second, isolated devbox for
the bot (
install-devbox --instance NAME --web-port N --ssh-port N) with dev-only credentials and give the bot a read-only status key. Anything it changes goes through a small bridge you write yourself, and you keep merges, releases, production, and secrets (docs/14).
Give Claude, Codex, Hermes, or another terminal-capable agent this repository URL and ask
it to follow AGENTS.md and the
agent-guided installation. The supported installer
shows a dry-run, keeps services loopback-only by default, asks before privileged/public
changes, builds the Compose stack, installs helpers, and verifies the result. It does not
use a blind curl | sudo bash path or print generated credentials.
- Get a VPS — Ubuntu LTS, ~$5+/mo, 10 min of hardening.
- Install Coolify — one command; gives you HTTPS + deploy UI. (Plain-Docker alternative included.)
- Deploy the devbox — the container from
stack/: code-server + tmux + sshd, with bulletproof bind-mount persistence. - Learn the session vocabulary — one session per project, multiple agents in parallel.
- Connect every device — browser, terminal, VS Code/Claude desktop over SSH, and the Termius one-tap phone setup.
- Wire up secrets — 1Password or Bitwarden CLI.
- Send files in — DevboxDrop,
2dev,devshot. - Run the Hermes pattern — one agent to drive them all.
- Backups & rebuilds — nightly cron, hands-off rebuilds, hardening checklist.
- Manage projects from Telegram topics — one private forum group, one isolated topic and tmux session per project.
- Switch coding agents mid-session — safely hand a live worktree between Claude Code, Codex, or another CLI.
- Browsers & authenticating to services — headless testing browser, token-first auth (incl. Cloudflare), SSH-forwarded OAuth callbacks, and the opt-in server GUI browser.
- Multi-channel sessions with multiple agents — one project = one tmux session reachable from any channel; multiple agents in parallel, each with its own worktree + branch and a per-worktree one-writer turn-lock.
- Let a bot manage your devbox — a separate instance with dev-only credentials, a read-only status gate, your own bridge for writes, and human gates on everything that matters.
Each release below lists what changed; full notes, including upgrade steps, are on the releases page.
Why. Hosted AI bots (a chat bot, a scheduled automation, an agent running somewhere else) can now plan and supervise coding work: check on your agents, hand out the next task, report when a branch is ready. That keeps projects moving while you're away. But giving a bot a shell on your everyday devbox gives it everything that devbox can reach: your logins, your secrets, and anything you can deploy from there. And a bot can be steered by text it reads (prompt injection) or taken over through its vendor account. v1.8.0 gives you the pieces to delegate while keeping the damage small if something goes wrong. (Writes, such as handing out tasks, go through a small bridge you build; see docs/14.)
- A separate instance so the bot's agents work in their own box with dev-only credentials. A mistake or a takeover then reaches that box's repositories, dev secrets, and model login, not your everyday logins and secrets. It is a container on the same server, not a separate machine; docs/14 lists the limits.
- A read-only door so the bot can watch progress without a shell. A stolen bot key can only read status, as long as nothing inside the instance has been compromised.
- A more accurate status board, because a bot (or you) uses it to decide what to clean
up. In v1.7.0, an agent whose tmux window was missing could show the main shell's state
(for example
idle) instead ofunknown; it now readsunknown. Still, treat status as a report, not proof.
What's in it:
- Run a second, isolated devbox —
install-devbox --instance NAME --web-port N --ssh-port N(plus--browser-port Nwith--with-browser) creates containerdevbox-NAMEin Compose projectdevbox-NAME, with its own data root (/data/devbox-NAME), logins, keys, and sessions. Ports are loopback-only by default, validated, and refused if already in use; the installer never adopts a container it did not create. The harness takes the same flags:plan --instance NAME --web-port N --ssh-port N, andpreflight/verify/diagnose --instance NAME. Without--instancenothing changes. See docs/03. - A read-only door for bots —
devbox-status-gateis installed with the other helpers (and checked byverify) but does nothing until a key uses it. Onerestrict,command=...line inauthorized_keyslets that key ask only forversion,list, orstatus <project> [--json]; everything else is denied (exit 126) and logged inside the instance. Revoke = delete the line. It limits what the bot's key can do to the instance; it does not protect the bot or you from a compromised instance. See docs/14. - Let a bot manage coding work while you keep control — docs/14: a separate instance with dev-only credentials, your own bridge for writes, human gates, independent verification, a staged autonomy ladder, and a threat model.
- Upgrading: to add an instance to an existing box, first move
/opt/devbox-anywhereto the approved v1.8.0 commit and re-run the default installer with the same flags you first installed with, such as--expose-sshor--with-browser(docs/14 step 1). - Fixed: the status board now proves an agent's tmux window exists before reading it.
An agent with no window reads
unknowninstead of borrowing the session's main shell state; a window it cannot prove (a duplicated window name, or tmux output it cannot parse, such as a newline inside a window name) also readsunknownand is never(reapable). - Fixed: a split window reads
workingwhen any of its panes runs the agent, not only the selected one. A turn the board cannot read exactly readsunknown, neverfree.statusno longer takes git's index lock (polling could make an agent'sgit addfail), a worktree git cannot read showsERRORinstead of hiding the whole board,--jsonescapes every control byte, and the table andlistprint printable ASCII only.
- Every agent on a project, at a glance —
devbox-session status <project>shows each agent's git state (new | new+dirty | clean | dirty | merged | merged+dirty),+ahead/-behindagainst the base branch, who holds thedevbox-turn, and a labeled activity heuristic (working | idle | blocked | unknown). Add--jsonfor a schema-versioned document that scripts and agents can read. - Safe cleanup — an agent is marked
(reapable)only when it is merged, clean, the turn is free, and its window is notworking. A just-created agent readsnew, nevermerged. Activity is never guessed: it readsunknownwhen it cannot be read. An agent with no tmux session or no window of its own readsunknownand can still be reapable, because nothing can be running there. A missing worktree, an unknown fork point, or a window that may exist but cannot be proven (since v1.8.0) is never reapable. - No migration — existing registries keep working. See docs/13.
v1.6.0 shipped the features below; v1.6.1 fixed a verify false alarm under repeated runs.
- Your customizations survive upgrades — add your own persistent links as
~/.local/share/devbox-relink.d/*.confdrop-ins instead of editing the shipped helper. If an upgrade would overwrite a helper you changed, the installer backs it up first and prints aPRESERVEDnotice listing what to move into a drop-in. - Long-running processes come back on their own — declare them in
~/.local/share/devbox-daemons.d/<name>.confanddevbox-daemonrestarts them at every boot, with no human action after a rebuild. See docs/10. verifychecks what actually breaks after a rebuild — broken managed links and declared daemons that aren't running are reported as warnings (they never block an install that was fine before).- Fixed:
gh auth logincan now persist its login on a fresh install, and repeatedverifyruns no longer trip OpenSSH's connection penalties.
- Dependencies on new worktrees, handled — a freshly created agent worktree has no
installed dependencies.
DEVBOX_WORKTREE_POSTADD='npm ci' devbox-worktree add <project> <agent>runs your install step inside the new worktree right after it's created; a hook failure surfaces as a non-zero exit but keeps the worktree so you can fix and re-run. See docs/13 "Dependency policy" — install per worktree, never sharenode_modules/venv across worktrees.
Multi-channel/multi-agent shipped in v1.4.0; v1.4.1 was a sanitization patch. The feature below is current.
- One project, one session, any channel — a project is one durable tmux session on the box. A terminal over SSH, a Telegram topic, and a Slack channel all drive the same live session, so the shell, processes, branch, and files are identical the moment you switch. Channels themselves are configured in Hermes, not here; the box provides the session layer and docs/13 shows the wiring.
- Multiple agents per project, in parallel —
devbox-worktree add <project> <agent>gives each agent its own tmux window, git worktree, andagent/<agent>branch, so agents never collide (one writer per checkout is preserved).devbox-sessionresolvesproject:agentto its session/window/worktree/branch and fails closed on bad input. - Per-worktree one-writer turn-lock —
devbox-turn take|release|statuscoordinates who writes a checkout when an agent and a human (or two agents) share it. Atomic acquisition, holder-only release, stale-lock reclaim. It is advisory coordination, not a sandbox — every channel carries the devbox account's full terminal authority.
- Headless browser, always on — every image bakes in a pinned Playwright + Chromium
(offline-ready) for tests, screenshots, scraping, and link checks. Smoke-test with
devbox-browser-check. - Auth playbook — docs/12 covers token-first auth (including Cloudflare API tokens, Access service tokens, and tunnel tokens), SSH-forwarded OAuth callbacks, and when to reach for a real browser.
- Opt-in interactive browser —
install-devbox --with-browseradds a loopback-only noVNC Chromium (127.0.0.1:8081) for click-through logins. Off by default, generates its own owner-only password, persists its profile across rebuilds, and is never exposed publicly. It is a convenience, not a sandbox.
Devbox Anywhere now includes two complementary agent-integration layers:
- A deterministic repository harness at
scripts/devbox-anywhere. It gives terminal- capable agents schema-versioned JSON and stable check IDs instead of asking them to infer readiness from prose or command output. - A distributable Hermes umbrella skill at
skills/devbox-anywhere/. It guides release selection, approval prompts, installation, verification, diagnosis, Telegram project topics, coding-agent handoffs, backups, recovery, and rebuilds.
The harness supports four commands:
./scripts/devbox-anywhere preflight --json
./scripts/devbox-anywhere plan --json --approved-commit EXACT_SHA
sudo /opt/devbox-anywhere/scripts/devbox-anywhere verify --json
sudo /opt/devbox-anywhere/scripts/devbox-anywhere diagnose --jsonpreflightchecks Linux, required tools, Compose v2, and local Docker availability.planvalidates an exact installer-bearing commit and reports paths, listeners, required approvals, and the authoritative installer command without changing the server.verifyvalidates trusted installer state, exact Docker ports and writable mounts, helper installation, and real HTTP/SSH readiness.diagnosereports the same stable check IDs and safe recovery information without trying to repair or mutate the installation.
The skill and harness do not replace scripts/install-devbox, bypass sudo, or turn
Hermes into a sandbox. The Bash installer remains the only privileged installation engine;
the user must still approve privileged operations and any public network exposure. Docker
operations are pinned to the local default context with isolated configuration, and
services remain loopback-only unless public SSH is explicitly requested.
Starting with v1.2.0, Hermes users can install the umbrella skill from the same verified stable checkout used for the workflow, including all eight operational references:
install -d -m 0700 "$HOME/.hermes/skills/devbox-anywhere"
cp -R ./skills/devbox-anywhere/. "$HOME/.hermes/skills/devbox-anywhere/"Do not use a mutable default-branch skill identifier for this workflow. Resolve the stable tag and exact commit first, copy the skill from that checkout, and start a new Hermes session so its skill index refreshes. Installation and deployment always require a stable installer-bearing release.
| Path | What |
|---|---|
docs/00–14 |
Agent-guided install plus the full guide in build order |
stack/ |
Dockerfile, entrypoint, tmux/sshd/VS Code configs, compose alternative, devbox-browser-check |
scripts/ |
Installer, agent harness, devbox, devbox-attach, devbox-relink, devbox-session, devbox-status-gate, devbox-turn, devbox-worktree, tmux wrapper, backup cron |
skills/devbox-anywhere/ |
Hermes umbrella skill plus eight operational and security references |
clients/ |
Your Mac: devbox() + 2dev + devshot zsh functions, DevboxDrop watcher |
templates/ |
main.env and per-project .env.op examples |
tests/ |
Installer, named-instance, harness, skill, source-trust, lifecycle, session/status, status-gate, and mutation checks |
Run the repository checks with:
./tests/run.shWe run this on OVH; Hetzner is equally good (often cheaper). Sizing table in docs/01.
Disclosure: some provider links in this repo may be or become affiliate links — signing up through them supports this guide at no extra cost to you. The setup works identically wherever you rent your server.
Why not just VS Code Remote-SSH / Codespaces / a cloud IDE? Those give you an editor; this gives you a place. Sessions that outlive every client, reachable from a phone, hosting your own deployed apps on the same box, no per-seat pricing, and your agents keep working while you're gone.
Does it need Coolify? No — stack/docker-compose.yml
runs it with plain Docker + any reverse proxy. Coolify just makes HTTPS and redeploys
one-click, and you'll want it for everything else you self-host.
Can a hosted AI bot drive it? Yes, with limits you set. Give the bot its own instance with dev-only credentials and a key that can only read status (docs/14). Anything that changes state (starting an agent, giving it a task) goes through a small bridge you write and run yourself; it is not included in this repo. Agent branches reach your Git host only through a push step you control, which opens pull requests, and the guide keeps merges, releases, production, and secrets with you. No particular bot is required.
Android instead of iPhone? Termius works the same; any SSH client with key auth does (the picker is server-side).
Windows/Linux desktop instead of Mac? Everything server-side is OS-agnostic. The
clients/ helpers are macOS/zsh; ports to PowerShell/systemd-user timers are
straightforward PRs — contributions welcome.
MIT. Built from a real production setup; sanitized, but the gotchas are all real. Issues and PRs welcome.

