BoxHaven gives development teams a standard way to run AI coding agents and developer shells on named remote Linux boxes. Try the hosted version or self-host it with your own cloud credentials. Each box keeps running after a laptop disconnects, has a managed tmux session for long-running Codex, Claude, Gemini, or shell work, and can sync the current project to and from the remote machine.
BoxHaven 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.
The web console, API, and documentation are separate surfaces. The backend
serves the console and API, while docs builds as a static site. Set
BOXHAVEN_DOCS_URL when building the console if its footer should link to a
different documentation site. Backend distributions can add in-process
modules through the published @boxhaven/backend contract.
The 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.
The 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:
bh login
bh ssh-config install # one-time setup for normal ssh/scp aliases
bh create work # provisions a box and syncs this project once
bh run work claude # claude starts working in the box's tmux session
bh connect work
On first login, enter your backend API URL, or press Enter to choose hosted
BoxHaven. Later logins reuse the saved URL. For scripts or a noninteractive
terminal, pass --backend-url or set BOXHAVEN_BACKEND_URL.
Mid-conversation with Claude locally? bh run work claude --continue resumes
that exact session on the box.
Sign up with email and password, then follow the one-hour verification link.
The link verifies your email and signs you in automatically, replacing any
other account open in that browser.
The "Continue with GitHub" button appears only when the operator configures a GitHub OAuth app
(GITHUB_CLIENT_ID/ GITHUB_CLIENT_SECRET). GitHub's provider-verified email
does not need a second verification step; bh login then works the same either
way.
Run 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:
launch_logs=$(mktemp -d)
(bh create work-2 && bh run work-2 codex 'Review the project and report improvements.') </dev/null >"$launch_logs/work-2.log" 2>&1 &
codex_pid=$!
(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 &
claude_pid=$!
wait "$codex_pid" || cat "$launch_logs/work-2.log"
wait "$claude_pid" || cat "$launch_logs/work-3.log"
bh connect work-2
Teach your local Codex or Claude agent to operate BoxHaven with the public BoxHaven skill. Install it using Vercel's Skills CLI, the installer behind skills.sh (Node.js 22.20 or later):
npx skills add finbarr/boxhaven --skill boxhaven -g -a codex claude-code
Invoke $boxhaven in Codex or /boxhaven in Claude. The skill teaches box
creation, persistent sessions, parallel launches, progress and preview checks,
.boxhavenignore, and retrieving results. It bundles a parallel launcher and
sync reference, and links to current docs for details. It requires bh 0.2.0
or later and your existing login.
Update it with npx skills update boxhaven -g.
Installation, project scope, and version pins
are documented on the site. Inside a remote box, a separate image-installed
boxhaven-web-preview skill describes its web preview setup.
bh create asks the backend for a machine, waits for it to be reachable, and
syncs the current project into /opt/boxhaven/project. After that the box
owns its copy: bh run does not mirror local files, so nothing an agent does
on the box is ever overwritten by a routine command — bh sync up pushes
local changes explicitly (mirroring deletions) and bh sync down retrieves
the box's work. Project sync excludes common dependency/cache directories such
as node_modules/, .next/, and .venv/ by default. Add a .boxhavenignore
file at the project root for additional rsync-style exclude patterns. Sync
completion reports elapsed time, network bytes, changed bytes, and file counts.
When you start claude or codex with bh run, bh forwards your newest
local sessions for the project, so claude --continue on the box picks up
the conversation exactly where your laptop left it. Agent commands use the
machine's managed tmux session: they attach with a terminal and start detached
without one. Other commands run over direct SSH. Inspect session output before
calling an agent working; a successful detached launch is not task completion.
Run the one-time setup after logging in to add managed bh-<name> aliases to
your normal OpenSSH configuration:
bh ssh-config install
ssh bh-work
scp ./notes.txt bh-work:/opt/boxhaven/project/
rsync -az ./fixtures/ bh-work:/opt/boxhaven/project/fixtures/
The aliases also work in tools that use OpenSSH configuration, including VS
Code Remote SSH. Box aliases update after create, list, status, rename, and
destroy operations; bh ssh-config refresh forces an update. Each connection
transparently replaces the local short-lived certificate while reusing a
device key stored under ~/.boxhaven/ssh. Remove the managed include with
bh ssh-config uninstall.
- Named remote boxes: create, list, inspect, connect, sync, run, rename, and destroy.
- Project sync to
/opt/boxhaven/projectwith explicit sync up/down commands. - A managed tmux session per box for long-running AI agent sessions.
- Mouse-wheel scrolling through tmux history in interactive sessions.
- Direct SSH using backend-signed short-lived user certificates.
- GitHub HTTPS credential forwarding from local
GH_TOKENorGITHUB_TOKEN. - Git safe-directory configuration for the synced project path.
- Optional preview hostnames for HTTP services running on the box.
- Multiple cloud providers per backend: DigitalOcean and Hetzner Cloud.
- Provider plan discovery with team-owned size shortcuts.
- Team-owned boxes with roles, shareable invite links, and per-team visibility.
- Admin-managed golden images that become the default for new boxes.
- An open-source Fastify/Better Auth backend.
Agents can discover the documentation through
llms.txt. Each page also has a Markdown
URL, such as commands.md, generated
from the same source on every docs build.
BoxHaven is open source under the GNU Affero General Public License v3.0 only
(AGPL-3.0-only). See NOTICE and LICENSE for the
copyright notice and full license text.
Run the complete open-source backend with your own provider credentials and no built-in limits. No BoxHaven subscription or company account is required. See backend/README.md for running the backend and deploy for the deployment bundle. Your cloud provider charges for the machines you create.
One-liner (macOS and Linux, installs the latest release):
curl -fsSL https://raw.githubusercontent.com/finbarr/boxhaven/master/install.sh | sh
Homebrew (via the finbarr/tap tap):
brew install finbarr/tap/boxhaven
Or build from source:
go build -o bh ./cmd/bh
./bh version
Normal CLI commands check the public BoxHaven GitHub releases in the
background. Results are cached for 24 hours under the BoxHaven config
directory, so update notices never wait on the network and offline checks stay
silent. When a newer release is known, bh prints one concise link after
reading the cached result.
Then jump straight into the quickstart:
bh login
bh ssh-config install # one-time setup for normal ssh/scp aliases
bh create work # provisions the box and syncs this project once
bh run work claude # start Claude in the box's tmux session
bh connect work # reattach after disconnecting
BoxHaven reads global config from ~/.config/boxhaven/config.toml and project
config from .boxhaven.toml.
[remote]
backend_url = "https://api.example.com"
token = "browser-granted-session-token"
ssh_user = "boxhaven"
provider = "hetzner"
setup = [
"docker compose up -d db"
]
Environment overrides:
BOXHAVEN_BACKEND_URLBOXHAVEN_TOKENGH_TOKENorGITHUB_TOKENfor GitHub repository access inside remote boxes
A single backend can serve multiple cloud providers. GET /v1/providers lists
what a backend has configured, and bh create picks the backend default unless
a provider is requested explicitly:
bh create work --provider hetzner
bh create work --provider digitalocean --region sfo3
bh create work --provider hetzner --region fsn1 --image 12345678
--region is passed through to the provider. --image accepts a name or ID from the target team’s image list. Set a
project-wide default with the provider key under [remote] in
.boxhaven.toml or the global config.
The backend enables a provider when its credentials are present and selects the
default with BOXHAVEN_BACKEND_PROVIDER. When unset, the first configured
provider is the default (DigitalOcean when both are configured).
DigitalOcean:
DIGITALOCEAN_ACCESS_TOKEN: API token, enables the provider.DIGITALOCEAN_REGION: defaultnyc3.DIGITALOCEAN_SIZE: defaults-2vcpu-4gb.DIGITALOCEAN_IMAGE: base image fallback, defaultubuntu-24-04-x64.BOXHAVEN_REMOTE_IMAGE_DIGITALOCEANorBOXHAVEN_REMOTE_IMAGE: golden snapshot id for new boxes.
Hetzner Cloud:
HCLOUD_TOKEN: API token, enables the provider.HETZNER_LOCATION: defaultnbg1(alsofsn1,hel1,sin).HETZNER_SERVER_TYPE: provider plan behind the built-insmallsize, defaultcpx22.HETZNER_IMAGE: base image fallback, defaultubuntu-24.04.BOXHAVEN_REMOTE_IMAGE_HETZNER: golden snapshot id for new boxes.
Every provider supplies the built-in small, medium, and large sizes.
Use bh size list to see their exact CPU, memory, disk, provider plan, and
effective hourly price with day and month estimates. bh size plans shows the
same price context before an owner or admin gives a provider plan a team-owned
shortcut:
bh size plans --provider digitalocean
bh size create gpu --provider digitalocean --plan gpu-4000adax1-20gb
bh create model-work --size gpu
bh size rm gpu
Shortcuts 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.
Every box belongs to a team. Each account automatically gets a default team
named <name>'s team, so boxes work with no setup. Create another team in the
console or from the CLI:
bh team create acme
Invite teammates by shareable link. bh team invite <email> (or the console
Teams view) creates an invitation and prints an invite URL such as
https://app.example.com/invite?id=<invitation-id>; send that link to the
teammate, who accepts it after signing in with the invited email address.
BoxHaven does not send invitation emails.
New boxes land in the session's active team: bh login pins it, and accepting
an invitation, creating a team, or selecting a team in the console's Team view
switches it for that session. Control placement explicitly:
bh create work --team acme # create a box directly in a team
bh team switch acme # change the CLI default team for new boxes
bh move work acme # move one of your boxes to another of your teams
Members have one of three roles: owner, admin, or member. Team members
see exactly the boxes in that team and who owns each one; boxes in your other
teams stay invisible to them. Owners and admins can destroy team boxes;
members can only destroy their own.
When 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.
Only an owner can delete a team. Deletion is refused until every box in the
team has been destroyed and every in-progress create has finished. Failed
boxes still require explicit destruction because BoxHaven does not assume
provider cleanup succeeded. If the backend restarts during creation, its stale
reservation is cleared and the durable recovery box remains for that explicit
cleanup. Provider discovery updates the record for cleanup but never makes it
ready; bh list reports recovery required, and the box must be destroyed and
created again. A deployment-specific module can add another fail-closed check
for external account state.
Moving 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:
bh image create work --name dev-tools
bh create work-clone --image dev-tools
Golden 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.
bh image ls
bh image create work --name dev-tools
bh create work-clone --image dev-tools
bh image rm dev-tools --force
When the current project's origin, or the origin of an immediate child
repository in a multi-repository workspace, points at GitHub, bh create,
bh run, bh connect, and bh sync up forward GitHub auth to the remote box in
/run/boxhaven/session.env. The file lives in tmpfs, is readable only by the
remote SSH user and root, and is replaced or removed by the CLI on the next
command. The remote image includes a Git credential helper that uses those
variables, so agents and shells inside the box can push to HTTPS GitHub remotes.
GH_TOKEN or GITHUB_TOKEN are used when set. Otherwise, if the GitHub CLI is
installed and authenticated locally, bh uses gh auth token and forwards that
token for the remote session. For the smoothest agent workflow, use an HTTPS
GitHub origin and either export a token with the repository scopes your team
allows or run gh auth login before starting the remote session.
bh create, bh run, bh connect, and bh sync up also forward selected
local agent login files for Claude, Codex, Gemini, GitHub Copilot, and
opencode (for example ~/.codex/auth.json, ~/.claude.json,
~/.claude/.credentials.json on Linux, and ~/.claude/settings.json).
Recent claude/codex sessions for the current project are forwarded when you
start those agents so they can resume; broader histories, caches, and
databases are never copied.
Those commands also forward the effective local Git author identity for the
current project by setting user.name and user.email in the remote SSH user's
global Git config. BoxHaven does not copy the full local Git config.
Open a box's Public preview link in the console, or use Open preview in its details drawer. Previews open in a new tab. Each box has a small character inspired by the logo; its appearance stays the same across renames and team moves.
Each box receives a public preview URL when the backend is configured
with a preview base domain. The backend warms the preview URL during machine
create so Caddy has already completed on-demand certificate issuance before the
URL is shown. Public HTTPS and WebSocket traffic terminate at the BoxHaven
control plane, then the backend proxies plain HTTP/WebSocket traffic to the
machine's BOXHAVEN_PREVIEW_TARGET_PORT, default 80.
Inside the box, commands receive:
BOXHAVEN_PREVIEW_URL: the browser URL to share.BOXHAVEN_PREVIEW_HOSTNAME: the public hostname.BOXHAVEN_PREVIEW_TARGET_PORT/BOXHAVEN_WEB_PORT: the machine port to serve, normally80.BOXHAVEN_WEB_BIND: the bind address to use, normally0.0.0.0./run/boxhaven/context.json: structured runtime context with the same preview details under.preview.
Apps should bind HTTP to 0.0.0.0:$BOXHAVEN_WEB_PORT or run a reverse proxy on
that port to the app's internal dev-server port. Framework dev-server
WebSockets, including Vite HMR, use the same preview URL. The default
boxhaven user has sudo access if binding to port 80 is required.
The open-source backend in backend provides:
- Better Auth browser/device login
- team-centric box ownership with automatic default teams
- DigitalOcean and Hetzner Cloud provisioning
- multi-member teams via Better Auth organizations with roles and invite links
- admin-managed golden images per provider
- backend-signed short-lived SSH certificates
- VM agent RPC for setup commands and tmux session lifecycle
- generated preview hostnames and a browser console/auth app
Run it locally:
cd backend
npm ci
BETTER_AUTH_SECRET="$(openssl rand -hex 32)" \
DIGITALOCEAN_ACCESS_TOKEN=dop_v1_example \
npm run dev
Then point the CLI at it:
bh login --backend-url http://127.0.0.1:8787
bh create work
Production deployment and golden-image tooling live in deploy/digitalocean. Deploy the public self-hosted stack from the repository root with:
npm run deploy:app -- --target root@app.example.com
The SSH target is required: use -- --target user@host or set
BOXHAVEN_DEPLOY_TARGET. The command fast-forwards /opt/boxhaven/app on
master, builds the docs site, and runs the Compose deploy on that machine.
Health checks use BOXHAVEN_API_URL, BOXHAVEN_APP_URL, and
BOXHAVEN_DOCS_URL from its Compose environment. The remote VM snapshot is
rebuilt separately. On the Droplet itself, use
npm run deploy:production:local.
The deploy script supports distribution-specific build and service wiring through
BOXHAVEN_PRODUCTION_COMPOSE_OVERLAY_FILE and
BOXHAVEN_PRODUCTION_COMPOSE_OVERLAY_ENV_FILE (or the matching
--compose-overlay flags). It refuses to deploy or verify an existing
distribution without an overlay when the backend's Compose metadata records one.
After changing the VM runtime or image-builder code, explicitly rebuild and publish the remote VM image:
npm run deploy:runtime -- --target root@app.example.com
The runtime deploy creates and snapshots a temporary DigitalOcean builder
Droplet, updates BOXHAVEN_REMOTE_IMAGE, then restarts and verifies the backend
so new boxes use the image. When an active BOXHAVEN_REMOTE_IMAGE exists, the
builder starts from that snapshot by default instead of reinstalling the full
OS/toolchain from Ubuntu. Use npm run deploy:runtime -- --target root@app.example.com --full-base-image only
for base OS or runtime dependency rebuilds.
Both deploy commands forward your SSH agent for Git access. Set
BOXHAVEN_DEPLOY_DIR if the server checkout is not /opt/boxhaven/app.
Run the reusable remote lifecycle smoke against your test backend after remote VM, SSH, sync, snapshot, or agent changes:
BOXHAVEN_SMOKE_BACKEND_URL=https://api.example.com \
BOXHAVEN_TOKEN=... \
GH_TOKEN=... \
BOXHAVEN_SMOKE_GIT_REMOTE=https://github.com/<org>/<smoke-repo>.git \
make smoke-remote
The default smoke is intentionally fast: it creates one box from the active
snapshot, syncs a temporary Git project, verifies runtime tools, fetches the
preview URL, optionally pushes and deletes a temporary GitHub smoke branch, and
destroys the box unless BOXHAVEN_SMOKE_KEEP=1 is set.
Use make smoke-remote-full with BOXHAVEN_SMOKE_RESTART_BACKEND_CMD when the
agent reconnect path needs coverage. Use make smoke-remote-two-box only for
concurrency, provider import, or multiple-machine behavior.