Apache-2.0
Run your own. It is the same thing we run.
Devpipe is a control plane for persistent agent machines. DigitalOcean, Runpod, and local Docker share one adapter contract. Machine operations are journalled, so an API restart does not turn a half-finished provider call into an invisible resource.
Nothing is sold and nothing is charged. There is no payment processor in this and no billing code — Devpipe creates machines in the provider account you connect, and that provider bills you. What the software adds is durable lifecycle control, cost visibility, and a cap that stops new machines when you say stop.
Choose the backing
| Backend | Status | Use it for |
|---|---|---|
| DigitalOcean | Production | Public VMs, persistent volumes, provider firewall and DNS, usage metering, and GPUs. |
| Local Docker | Developer preview | Lifecycle development and contract tests on one host. Named volumes persist; it is not a multi-tenant security boundary. |
| Runpod | Preview | CPU Pods using a published OCI image, trusted HTTPS proxy endpoints, and persistent network volumes. No per-box DNS or provider firewall is claimed. |
What to do at DigitalOcean
Do these four in the DigitalOcean console before you start the wizard. Each takes a minute, and the wizard checks all of them against the API rather than taking your word for it — because every one has a failure that shows up hours later looking like something else.
-
Make an API token
API → Tokens → Generate New Token. Give it a name you will recognise a year from now, set the expiry you are willing to live with, and tick Write as well as Read — without write it can list droplets and not create one.
This token creates and destroys droplets, attaches and detaches volumes, and writes DNS records. It is worth its own DigitalOcean account rather than one shared with unrelated infrastructure, because anything that gets hold of it can delete everything that account can see. You paste it once, into the wizard; it is encrypted at rest and never leaves your instance.
-
Put a domain on the account
Networking → Domains → Add a domain. Then, at whoever you registered the name with, point its nameservers at DigitalOcean:
ns1.digitalocean.com ns2.digitalocean.com ns3.digitalocean.comThis is the step people get wrong. Owning the domain is not enough — DigitalOcean has to be answering for it, because every box gets an
Arecord written under it and that record is what makes its Let's Encrypt certificate possible. Point it anywhere else and you get boxes that build perfectly, never resolve, never get TLS, and fail in ways that all look like something else. Nameserver changes take anywhere from minutes to a day to propagate; the wizard will refuse the domain until DigitalOcean is actually serving it. -
Upload an SSH key
Settings → Security → Add SSH Key. Paste your public key —
~/.ssh/id_ed25519.pub, not the private one.The wizard puts this on every box it builds. It is the only way onto a machine that wedges partway through setup, which is exactly the moment you need to look at one and the moment nothing else works. Skippable, and you will wish you had not.
-
Check what the account will let you make
A new account is usually limited to 10 droplets and has no GPU access at all. Both are raised by asking support, and both are worth knowing before you plan around them rather than after. The wizard shows the droplet limit it sees as soon as the token is connected.
GPU machines — RTX 4000 Ada at $0.76/hr through H100 at $4.41/hr — have to be enabled on the account and exist in only a couple of regions. Devpipe reads the live list from your account, so it offers exactly what you can actually create and nothing else.
Using Runpod instead
Build the same box image, publish it to a registry Runpod can read, and select the Runpod adapter before starting the API. The setup wizard then validates a Runpod API key; DigitalOcean DNS and SSH-key steps are not required.
docker build -f deploy/docker/box.Dockerfile -t registry.example/devpipe-box:latest .
docker push registry.example/devpipe-box:latest
export DEVPIPE_MACHINE_PROVIDER=runpod
export DEVPIPE_RUNPOD_IMAGE=registry.example/devpipe-box:latest
export RUNPOD_API_KEY=…
bun run dev
The stock image includes the daemon, shells, Git, and search tools. Publish a derived image when your agents need additional runtimes or CLIs.
And two things outside the provider
Postgres
Anywhere the control plane can reach — a managed database, or one on the same host. It holds the sessions, the boxes, the audit trail and the encrypted provider token, so it wants a backup, and the backup wants to live somewhere the database does not.
A host to run it on
A small droplet is plenty; it provisions and proxies, it does not compile anything. Give it a name under the same domain — that is the address people sign in at.
Installing it
-
Clone it and give it a database
git clone https://github.com/wess/devpipe cd devpipe && bun install export DATABASE_URL=postgres://…Migrations run at startup. There is no separate migrate step to forget.
-
Give it a key to encrypt credentials with
openssl rand -base64 32 # → DEVPIPE_SECRET_KEY in /etc/devpipe.envThis is the one secret that cannot live in the database, because it is what encrypts the database's secrets. Without it the DigitalOcean token is stored as readable text and a copy of a backup is a copy of it — the wizard will generate one and say so plainly rather than let that pass quietly.
-
Start it and claim it
Set
DEVPIPE_SETUP_TOKENbefore making a fresh instance public. The first account must present it and becomes the only owner; remove the token after that claim succeeds. -
Answer five questions
The wizard opens by itself: the encryption key, the provider token, the domain, an SSH key, a spending cap. Two of them are optional and say what you give up by skipping them. The domain is checked against your DigitalOcean account before it is accepted, and the token is checked before it is stored.
Local Docker
docker build -f deploy/docker/box.Dockerfile -t devpipe-box:local .
export DEVPIPE_MACHINE_PROVIDER=docker
export DEVPIPE_SECRET_KEY="$(openssl rand -base64 32)"
bun run dev
Each box is a constrained container whose daemon is published on a random loopback port and reached through the control plane. Workspaces are named Docker volumes. This path currently supplies the terminal runtime and basic development tools; the selectable cloud tool catalogue remains a DigitalOcean bootstrap feature.
Deploy without guessing
The production deploy requires DATABASE_URL and
DEVPIPE_SECRET_KEY, takes a timestamped database backup,
retains the previous binaries, and waits for the API's database-backed
readiness endpoint. Provider mutations are written to an operation
journal before they start and unfinished provisioning resumes after a
process restart.
One owner, and a team under them
The line between the roles is money and irreversibility. It exists because the alternative — letting somebody help run the instance meant handing them the token that can destroy every box on the account.
| Role | Can | Cannot |
|---|---|---|
| Owner exactly one |
Everything. The provider token, the spending cap, GPU machines, and who else administers the instance. | Be two people. Promoting somebody is a transfer — you become an admin in the same breath. |
| Admin | Invite people, suspend an abuser, read the audit log, see every box and what the instance is spending. Start a GPU machine. | See the provider token, change the spending cap, promote anyone, or suspend another admin. |
| Member | Their own boxes, workspaces, vault entries and terminal sessions, and see what they have cost. | Anything administrative at all. |
A cap, because the bill is yours
There is nothing to sell here and nobody to charge. DigitalOcean invoices the account the machines are on, which is yours, so the only money question the software can usefully answer is what it has cost you so far — and that is the question a cap is built on.
- Every box is measured from the moment the droplet exists — not from when it finishes building, because the provider is charging for that part too.
- Volumes are counted as well as droplets. They are charged for whether or not a box is attached, and they are what is left on the bill after everything else has been tidied away.
- It accumulates over the provider's own calendar month. Destroying a box does not undo what it already cost.
- You are warned at a threshold you set, refused a new box that would cross the line, and past the cap the running ones are put to sleep.
Asleep, never deleted. Sleeping releases the droplet and keeps the workspace, so the files come back when you raise the cap. A box carrying no workspace is never touched by any sweep in this product — that is the difference between reclaiming a machine and destroying somebody's afternoon.
What the hosted instance does that you would have to
Said plainly, because the honest list is short and none of it is code.
- Keeps a prebaked image current, so a box boots in minutes instead of installing its whole toolchain on first run.
- Holds the DigitalOcean quota — the droplet limit, and GPU access, which a new account does not have.
- Watches the provider account: what is on the bill that nothing claims, what is sending more traffic than a developer plausibly needs.
- Answers the provider when a box earns a complaint, which is the part that cannot be automated and the reason the abuse controls in here exist at all.
Source and issues →
Bun and TypeScript for the control plane and web workspace; Rust for the terminal core, per-box daemon, and CLI. Apache-2.0.
Devpipe CLI →
Install one binary, then use devpipe attach <box> to resume a persistent terminal without opening SSH.
Or just use ours →
Same software, none of the above. Claim a username and we will write when there is something to sign into.