{"slug": "show-hn-boxhaven-run-claude-code-and-codex-on-persistent-cloud-vms", "title": "Show HN: BoxHaven – Run Claude Code and Codex on Persistent Cloud VMs", "summary": "BoxHaven launched as a hosted and self-hostable service that runs AI coding agents such as Claude Code, Codex, and Gemini on persistent remote Linux boxes with managed tmux sessions, project sync, and direct SSH access. The CLI requires `bh` 0.2.0 or later, and a public BoxHaven skill installs via Vercel's Skills CLI with `npx skills add finbarr/boxhaven --skill boxhaven -g -a codex claude-code` on Node.js 22.20 or later. The tool targets teams assembling remote dev boxes by hand, letting agents keep working after a laptop disconnects and allowing parallel agent runs on separate boxes.", "body_md": "BoxHaven gives development teams a standard way to run AI coding agents and\ndeveloper shells on named remote Linux boxes. Try the [hosted version](https://app.boxhaven.dev/)\nor self-host it with your own cloud credentials. Each box keeps running after a\nlaptop disconnects, has a managed tmux session for long-running Codex, Claude,\nGemini, or shell work, and can sync the current project to and from the remote\nmachine.\n\nBoxHaven is built for the workflow many teams are assembling by hand today: remote dev boxes for individual developers, persistent agent sessions, direct SSH access, project sync, GitHub pushes from the box, and a self-hostable control plane.\n\nThe web console, API, and documentation are separate surfaces. The backend\nserves the console and API, while [docs](https://github.com/finbarr/boxhaven/blob/master/docs) builds as a static site. Set\n`BOXHAVEN_DOCS_URL` when building the console if its footer should link to a\ndifferent documentation site. Backend distributions can add in-process\nmodules through the published `@boxhaven/backend` contract.\n\nThe self-hosted console checks the public BoxHaven GitHub release through its backend and shows a compact release banner when the installation is behind. Release checks are cached and disappear silently when GitHub is unreachable.\n\nThe CLI is intentionally small, and the workflow is agent-first: copy your project to a box once, start Claude or Codex inside the box's tmux session — resuming your local conversation if you like — then disconnect and let it work:\n\n```\nbh login\nbh ssh-config install # one-time setup for normal ssh/scp aliases\nbh create work        # provisions a box and syncs this project once\nbh run work claude    # claude starts working in the box's tmux session\n# close the laptop — the agent keeps going. Reattach any time:\nbh connect work\n```\n\nOn first login, enter your backend API URL, or press Enter to choose hosted\nBoxHaven. Later logins reuse the saved URL. For scripts or a noninteractive\nterminal, pass `--backend-url` or set `BOXHAVEN_BACKEND_URL`.\n\nMid-conversation with Claude locally? `bh run work claude --continue` resumes\nthat exact session on the box.\n\nSign up with email and password, then follow the one-hour verification link.\nThe link verifies your email and signs you in automatically, replacing any\nother account open in that browser.\nThe \"Continue with GitHub\" button appears only when the operator configures a GitHub OAuth app\n(`GITHUB_CLIENT_ID`/` GITHUB_CLIENT_SECRET`). GitHub's provider-verified email\ndoes not need a second verification step; `bh login` then works the same either\nway.\n\nRun agents in parallel, each on its own box. Redirecting input and output makes these launches detached even when you run this example in a terminal:\n\n```\nlaunch_logs=$(mktemp -d)\n(bh create work-2 && bh run work-2 codex 'Review the project and report improvements.') </dev/null >\"$launch_logs/work-2.log\" 2>&1 &\ncodex_pid=$!\n(bh create work-3 && bh run work-3 claude 'Run the project checks and report failures.') </dev/null >\"$launch_logs/work-3.log\" 2>&1 &\nclaude_pid=$!\nwait \"$codex_pid\" || cat \"$launch_logs/work-2.log\"\nwait \"$claude_pid\" || cat \"$launch_logs/work-3.log\"\nbh connect work-2\n```\n\nTeach your local Codex or Claude agent to operate BoxHaven with the public\n[BoxHaven skill](https://github.com/finbarr/boxhaven/blob/master/skills/boxhaven/SKILL.md). Install it using\n[Vercel's Skills CLI](https://github.com/vercel-labs/skills), the installer behind\n[skills.sh](https://skills.sh/) (Node.js 22.20 or later):\n\n```\nnpx skills add finbarr/boxhaven --skill boxhaven -g -a codex claude-code\n```\n\nInvoke `$boxhaven` in Codex or `/boxhaven` in Claude. The skill teaches box\ncreation, persistent sessions, parallel launches, progress and preview checks,\n`.boxhavenignore`, and retrieving results. It bundles a parallel launcher and\nsync reference, and links to current docs for details. It requires `bh` 0.2.0\nor later and your existing login.\n\nUpdate it with `npx skills update boxhaven -g`.\n[Installation, project scope, and version pins](https://docs.boxhaven.dev/agent-skill)\nare documented on the site. Inside a remote box, a separate image-installed\n`boxhaven-web-preview` skill describes its web preview setup.\n\n`bh create` asks the backend for a machine, waits for it to be reachable, and\nsyncs the current project into `/opt/boxhaven/project`. After that the box\nowns its copy: `bh run` does not mirror local files, so nothing an agent does\non the box is ever overwritten by a routine command — `bh sync up` pushes\nlocal changes explicitly (mirroring deletions) and `bh sync down` retrieves\nthe box's work. Project sync excludes common dependency/cache directories such\nas `node_modules/`, `.next/`, and `.venv/` by default. Add a `.boxhavenignore`\nfile at the project root for additional rsync-style exclude patterns. Sync\ncompletion reports elapsed time, network bytes, changed bytes, and file counts.\n\nWhen you start `claude` or `codex` with `bh run`, bh forwards your newest\nlocal sessions for the project, so `claude --continue` on the box picks up\nthe conversation exactly where your laptop left it. Agent commands use the\nmachine's managed tmux session: they attach with a terminal and start detached\nwithout one. Other commands run over direct SSH. Inspect session output before\ncalling an agent working; a successful detached launch is not task completion.\n\nRun the one-time setup after logging in to add managed `bh-<name>` aliases to\nyour normal OpenSSH configuration:\n\n```\nbh ssh-config install\nssh bh-work\nscp ./notes.txt bh-work:/opt/boxhaven/project/\nrsync -az ./fixtures/ bh-work:/opt/boxhaven/project/fixtures/\n```\n\nThe aliases also work in tools that use OpenSSH configuration, including VS\nCode Remote SSH. Box aliases update after create, list, status, rename, and\ndestroy operations; `bh ssh-config refresh` forces an update. Each connection\ntransparently replaces the local short-lived certificate while reusing a\ndevice key stored under `~/.boxhaven/ssh`. Remove the managed include with\n`bh ssh-config uninstall`.\n\n- Named remote boxes: create, list, inspect, connect, sync, run, rename, and destroy.\n- Project sync to `/opt/boxhaven/project` with explicit sync up/down commands.\n- A managed tmux session per box for long-running AI agent sessions.\n- Mouse-wheel scrolling through tmux history in interactive sessions.\n- Direct SSH using backend-signed short-lived user certificates.\n- GitHub HTTPS credential forwarding from local `GH_TOKEN` or`GITHUB_TOKEN` .\n- Git safe-directory configuration for the synced project path.\n- Optional preview hostnames for HTTP services running on the box.\n- Multiple cloud providers per backend: DigitalOcean and Hetzner Cloud.\n- Provider plan discovery with team-owned size shortcuts.\n- Team-owned boxes with roles, shareable invite links, and per-team visibility.\n- Admin-managed golden images that become the default for new boxes.\n- An open-source Fastify/Better Auth backend.\n\nAgents can discover the documentation through\n[`llms.txt`](https://docs.boxhaven.dev/llms.txt). Each page also has a Markdown\nURL, such as [`commands.md`](https://docs.boxhaven.dev/commands.md), generated\nfrom the same source on every docs build.\n\nBoxHaven is open source under the GNU Affero General Public License v3.0 only\n(`AGPL-3.0-only`). See [NOTICE](https://github.com/finbarr/boxhaven/blob/master/NOTICE) and [LICENSE](https://github.com/finbarr/boxhaven/blob/master/LICENSE) for the\ncopyright notice and full license text.\n\nRun the complete open-source backend with your own provider credentials and\nno built-in limits. No BoxHaven subscription or company account is required.\nSee [backend/README.md](https://github.com/finbarr/boxhaven/blob/master/backend/README.md) for running the backend and\n[deploy](https://github.com/finbarr/boxhaven/blob/master/deploy) for the deployment bundle. Your cloud provider charges for\nthe machines you create.\n\nOne-liner (macOS and Linux, installs the latest release):\n\n```\ncurl -fsSL https://raw.githubusercontent.com/finbarr/boxhaven/master/install.sh | sh\n```\n\nHomebrew (via the `finbarr/tap` tap):\n\n```\nbrew install finbarr/tap/boxhaven\n```\n\nOr build from source:\n\n```\ngo build -o bh ./cmd/bh\n./bh version\n```\n\nNormal CLI commands check the public BoxHaven GitHub releases in the\nbackground. Results are cached for 24 hours under the BoxHaven config\ndirectory, so update notices never wait on the network and offline checks stay\nsilent. When a newer release is known, `bh` prints one concise link after\nreading the cached result.\n\nThen jump straight into the quickstart:\n\n```\nbh login\nbh ssh-config install # one-time setup for normal ssh/scp aliases\nbh create work       # provisions the box and syncs this project once\nbh run work claude   # start Claude in the box's tmux session\nbh connect work      # reattach after disconnecting\n```\n\nBoxHaven reads global config from `~/.config/boxhaven/config.toml` and project\nconfig from `.boxhaven.toml`.\n\n```\n[remote]\nbackend_url = \"https://api.example.com\"\ntoken = \"browser-granted-session-token\"\nssh_user = \"boxhaven\"\nprovider = \"hetzner\"\nsetup = [\n  \"docker compose up -d db\"\n]\n```\n\nEnvironment overrides:\n\n- `BOXHAVEN_BACKEND_URL`\n- `BOXHAVEN_TOKEN`\n- `GH_TOKEN` or`GITHUB_TOKEN` for GitHub repository access inside remote boxes\n\nA single backend can serve multiple cloud providers. `GET /v1/providers` lists\nwhat a backend has configured, and `bh create` picks the backend default unless\na provider is requested explicitly:\n\n```\nbh create work --provider hetzner\nbh create work --provider digitalocean --region sfo3\nbh create work --provider hetzner --region fsn1 --image 12345678\n```\n\n`--region` is passed through to the provider. `--image` accepts a name or ID from the target team’s image list. Set a\nproject-wide default with the `provider` key under `[remote]` in\n`.boxhaven.toml` or the global config.\n\nThe backend enables a provider when its credentials are present and selects the\ndefault with `BOXHAVEN_BACKEND_PROVIDER`. When unset, the first configured\nprovider is the default (DigitalOcean when both are configured).\n\nDigitalOcean:\n\n- `DIGITALOCEAN_ACCESS_TOKEN` : API token, enables the provider.\n- `DIGITALOCEAN_REGION` : default`nyc3` .\n- `DIGITALOCEAN_SIZE` : default`s-2vcpu-4gb` .\n- `DIGITALOCEAN_IMAGE` : base image fallback, default`ubuntu-24-04-x64` .\n- `BOXHAVEN_REMOTE_IMAGE_DIGITALOCEAN` or`BOXHAVEN_REMOTE_IMAGE` :\ngolden snapshot id for new boxes.\n\nHetzner Cloud:\n\n- `HCLOUD_TOKEN` : API token, enables the provider.\n- `HETZNER_LOCATION` : default`nbg1` (also`fsn1` ,`hel1` ,`sin` ).\n- `HETZNER_SERVER_TYPE` : provider plan behind the built-in`small` size, default`cpx22` .\n- `HETZNER_IMAGE` : base image fallback, default`ubuntu-24.04` .\n- `BOXHAVEN_REMOTE_IMAGE_HETZNER` : golden snapshot id for new boxes.\n\nEvery provider supplies the built-in `small`, `medium`, and `large` sizes.\nUse `bh size list` to see their exact CPU, memory, disk, provider plan, and\neffective hourly price with day and month estimates. `bh size plans` shows the\nsame price context before an owner or admin gives a provider plan a team-owned\nshortcut:\n\n```\nbh size plans --provider digitalocean\nbh size create gpu --provider digitalocean --plan gpu-4000adax1-20gb\nbh create model-work --size gpu\nbh size rm gpu\n```\n\nShortcuts store a provider and provider-plan slug. Creating a box resolves the shortcut once and records the resolved plan on the box; changing or deleting a shortcut never changes an existing machine.\n\nEvery box belongs to a team. Each account automatically gets a default team\nnamed `<name>'s team`, so boxes work with no setup. Create another team in the\nconsole or from the CLI:\n\n```\nbh team create acme\n```\n\nInvite teammates by shareable link. `bh team invite <email>` (or the console\nTeams view) creates an invitation and prints an invite URL such as\n`https://app.example.com/invite?id=<invitation-id>`; send that link to the\nteammate, who accepts it after signing in with the invited email address.\nBoxHaven does not send invitation emails.\n\nNew boxes land in the session's active team: `bh login` pins it, and accepting\nan invitation, creating a team, or selecting a team in the console's Team view\nswitches it for that session. Control placement explicitly:\n\n```\nbh create work --team acme   # create a box directly in a team\nbh team switch acme          # change the CLI default team for new boxes\nbh move work acme            # move one of your boxes to another of your teams\n```\n\nMembers have one of three roles: `owner`, `admin`, or `member`. Team members\nsee exactly the boxes in that team and who owns each one; boxes in your other\nteams stay invisible to them. Owners and admins can destroy team boxes;\nmembers can only destroy their own.\n\nWhen you leave a team (or are removed), your boxes in it move back to your active team the next time you list them; until that next listing, the old team can still see and destroy them.\n\nOnly an owner can delete a team. Deletion is refused until every box in the\nteam has been destroyed and every in-progress create has finished. Failed\nboxes still require explicit destruction because BoxHaven does not assume\nprovider cleanup succeeded. If the backend restarts during creation, its stale\nreservation is cleared and the durable recovery box remains for that explicit\ncleanup. Provider discovery updates the record for cleanup but never makes it\nready; `bh list` reports `recovery required`, and the box must be destroyed and\ncreated again. A deployment-specific module can add another fail-closed check\nfor external account state.\n\nMoving or sharing never copies a box. To hand a teammate a box like yours, snapshot it into a team image and create a new box from the resulting image:\n\n```\nbh image create work --name dev-tools\nbh create work-clone --image dev-tools\n```\n\nGolden images carry the BoxHaven VM runtime so new boxes boot ready to use. Image names are unique within the active team, with no added prefix. Different teams can independently use the same name; images remain private to their owning team. Names must start with a letter or number after normalization. A team member can snapshot one of the team's boxes, then select that image when creating another box in the same team. If no image is selected, BoxHaven uses the backend's configured default image for that provider.\n\n```\nbh image ls\nbh image create work --name dev-tools\nbh create work-clone --image dev-tools\nbh image rm dev-tools --force\n```\n\nWhen the current project's `origin`, or the `origin` of an immediate child\nrepository in a multi-repository workspace, points at GitHub, `bh create`,\n`bh run`, `bh connect`, and `bh sync up` forward GitHub auth to the remote box in\n`/run/boxhaven/session.env`. The file lives in tmpfs, is readable only by the\nremote SSH user and root, and is replaced or removed by the CLI on the next\ncommand. The remote image includes a Git credential helper that uses those\nvariables, so agents and shells inside the box can push to HTTPS GitHub remotes.\n\n`GH_TOKEN` or `GITHUB_TOKEN` are used when set. Otherwise, if the GitHub CLI is\ninstalled and authenticated locally, `bh` uses `gh auth token` and forwards that\ntoken for the remote session. For the smoothest agent workflow, use an HTTPS\nGitHub origin and either export a token with the repository scopes your team\nallows or run `gh auth login` before starting the remote session.\n\n`bh create`, `bh run`, `bh connect`, and `bh sync up` also forward selected\nlocal agent login files for Claude, Codex, Gemini, GitHub Copilot, and\nopencode (for example `~/.codex/auth.json`, `~/.claude.json`,\n`~/.claude/.credentials.json` on Linux, and `~/.claude/settings.json`).\nRecent claude/codex sessions for the current project are forwarded when you\nstart those agents so they can resume; broader histories, caches, and\ndatabases are never copied.\n\nThose commands also forward the effective local Git author identity for the\ncurrent project by setting `user.name` and `user.email` in the remote SSH user's\nglobal Git config. BoxHaven does not copy the full local Git config.\n\nOpen a box's **Public preview** link in the console, or use **Open preview** in\nits details drawer. Previews open in a new tab. Each box has a small character\ninspired by the logo; its appearance stays the same across renames and team moves.\n\nEach box receives a public preview URL when the backend is configured\nwith a preview base domain. The backend warms the preview URL during machine\ncreate so Caddy has already completed on-demand certificate issuance before the\nURL is shown. Public HTTPS and WebSocket traffic terminate at the BoxHaven\ncontrol plane, then the backend proxies plain HTTP/WebSocket traffic to the\nmachine's `BOXHAVEN_PREVIEW_TARGET_PORT`, default `80`.\n\nInside the box, commands receive:\n\n- `BOXHAVEN_PREVIEW_URL` : the browser URL to share.\n- `BOXHAVEN_PREVIEW_HOSTNAME` : the public hostname.\n- `BOXHAVEN_PREVIEW_TARGET_PORT` /`BOXHAVEN_WEB_PORT` : the machine port to\nserve, normally`80` .\n- `BOXHAVEN_WEB_BIND` : the bind address to use, normally`0.0.0.0` .\n- `/run/boxhaven/context.json` : structured runtime context with the same\npreview details under`.preview` .\n\nApps should bind HTTP to `0.0.0.0:$BOXHAVEN_WEB_PORT` or run a reverse proxy on\nthat port to the app's internal dev-server port. Framework dev-server\nWebSockets, including Vite HMR, use the same preview URL. The default\n`boxhaven` user has sudo access if binding to port 80 is required.\n\nThe open-source backend in [backend](https://github.com/finbarr/boxhaven/blob/master/backend) provides:\n\n- Better Auth browser/device login\n- team-centric box ownership with automatic default teams\n- DigitalOcean and Hetzner Cloud provisioning\n- multi-member teams via Better Auth organizations with roles and invite links\n- admin-managed golden images per provider\n- backend-signed short-lived SSH certificates\n- VM agent RPC for setup commands and tmux session lifecycle\n- generated preview hostnames and a browser console/auth app\n\nRun it locally:\n\n```\ncd backend\nnpm ci\nBETTER_AUTH_SECRET=\"$(openssl rand -hex 32)\" \\\nDIGITALOCEAN_ACCESS_TOKEN=dop_v1_example \\\nnpm run dev\n```\n\nThen point the CLI at it:\n\n```\nbh login --backend-url http://127.0.0.1:8787\nbh create work\n```\n\nProduction deployment and golden-image tooling live in\n[deploy/digitalocean](https://github.com/finbarr/boxhaven/blob/master/deploy/digitalocean). Deploy the public self-hosted stack\nfrom the repository root with:\n\n```\nnpm run deploy:app -- --target root@app.example.com\n```\n\nThe SSH target is required: use `-- --target user@host` or set\n`BOXHAVEN_DEPLOY_TARGET`. The command fast-forwards `/opt/boxhaven/app` on\n`master`, builds the docs site, and runs the Compose deploy on that machine.\nHealth checks use `BOXHAVEN_API_URL`, `BOXHAVEN_APP_URL`, and\n`BOXHAVEN_DOCS_URL` from its Compose environment. The remote VM snapshot is\nrebuilt separately. On the Droplet itself, use\n`npm run deploy:production:local`.\n\nThe deploy script supports distribution-specific build and service wiring through\n`BOXHAVEN_PRODUCTION_COMPOSE_OVERLAY_FILE` and\n`BOXHAVEN_PRODUCTION_COMPOSE_OVERLAY_ENV_FILE` (or the matching\n`--compose-overlay` flags). It refuses to deploy or verify an existing\ndistribution without an overlay when the backend's Compose metadata records one.\n\nAfter changing the VM runtime or image-builder code, explicitly rebuild and publish the remote VM image:\n\n```\nnpm run deploy:runtime -- --target root@app.example.com\n```\n\nThe runtime deploy creates and snapshots a temporary DigitalOcean builder\nDroplet, updates `BOXHAVEN_REMOTE_IMAGE`, then restarts and verifies the backend\nso new boxes use the image. When an active `BOXHAVEN_REMOTE_IMAGE` exists, the\nbuilder starts from that snapshot by default instead of reinstalling the full\nOS/toolchain from Ubuntu. Use `npm run deploy:runtime -- --target root@app.example.com --full-base-image` only\nfor base OS or runtime dependency rebuilds.\n\nBoth deploy commands forward your SSH agent for Git access. Set\n`BOXHAVEN_DEPLOY_DIR` if the server checkout is not `/opt/boxhaven/app`.\n\nRun the reusable remote lifecycle smoke against your test backend after remote VM, SSH, sync, snapshot, or agent changes:\n\n```\nBOXHAVEN_SMOKE_BACKEND_URL=https://api.example.com \\\nBOXHAVEN_TOKEN=... \\\nGH_TOKEN=... \\\nBOXHAVEN_SMOKE_GIT_REMOTE=https://github.com/<org>/<smoke-repo>.git \\\nmake smoke-remote\n```\n\nThe default smoke is intentionally fast: it creates one box from the active\nsnapshot, syncs a temporary Git project, verifies runtime tools, fetches the\npreview URL, optionally pushes and deletes a temporary GitHub smoke branch, and\ndestroys the box unless `BOXHAVEN_SMOKE_KEEP=1` is set.\n\nUse `make smoke-remote-full` with `BOXHAVEN_SMOKE_RESTART_BACKEND_CMD` when the\nagent reconnect path needs coverage. Use `make smoke-remote-two-box` only for\nconcurrency, provider import, or multiple-machine behavior.", "url": "https://wpnews.pro/news/show-hn-boxhaven-run-claude-code-and-codex-on-persistent-cloud-vms", "canonical_source": "https://github.com/finbarr/boxhaven", "published_at": "2026-09-24 18:16:45+00:00", "updated_at": "2026-09-24 18:30:17.834771+00:00", "lang": "en", "topics": ["ai-agents", "ai-tools", "developer-tools", "ai-products", "ai-infrastructure"], "entities": ["BoxHaven", "Claude Code", "Codex", "Gemini", "Vercel", "GitHub", "Node.js"], "also_reported_by": [], "alternates": {"html": "https://wpnews.pro/news/show-hn-boxhaven-run-claude-code-and-codex-on-persistent-cloud-vms", "markdown": "https://wpnews.pro/news/show-hn-boxhaven-run-claude-code-and-codex-on-persistent-cloud-vms.md", "text": "https://wpnews.pro/news/show-hn-boxhaven-run-claude-code-and-codex-on-persistent-cloud-vms.txt", "jsonld": "https://wpnews.pro/news/show-hn-boxhaven-run-claude-code-and-codex-on-persistent-cloud-vms.jsonld"}}