Run VibeCody on a DigitalOcean Droplet with Docker Compose.
| Setup time: 5 minutes | Cost: $24–96/month (provider list price, read August 2026) | Free credit: $200 for new accounts |
Quick Start
export DIGITALOCEAN_TOKEN="your-token"
cd vibecody/deploy/digitalocean
./setup.sh --tier lite
Prerequisites
- DigitalOcean account and API token
- Terraform installed
Step-by-Step
1. Get API Token
Create a token at: https://cloud.digitalocean.com/account/api/tokens
export DIGITALOCEAN_TOKEN="dop_v1_..."
2. Deploy
cd deploy/digitalocean
terraform init
terraform apply -var="do_token=$DIGITALOCEAN_TOKEN" -var="tier=pro"
3. Verify
IP=$(terraform output -raw ip)
curl http://$IP:7878/health
Tiers
| Tier | Droplet | Monthly Cost |
|---|---|---|
| lite | s-2vcpu-4gb | $24/mo |
| pro | s-4vcpu-8gb | $48/mo |
| max | s-8vcpu-16gb | $96/mo |
Troubleshooting
| Problem | What is actually happening | Fix |
|---|---|---|
curl http://$IP:7878/health hangs or refuses right after terraform apply |
The daemon is still starting. A cold daemon has been measured at ~16 s to first answer /health, and cloud-init has to pull the image before that |
Poll to a deadline rather than checking once: until curl -sf http://$IP:7878/health; do sleep 3; done |
/health answers but every other route returns 401 |
Working as designed. Almost every route needs the bearer token, and the daemon mints a fresh one on every restart | ssh in and read ~/.vibecli/daemon.token. Re-read it after any restart — a cached token dies with the process |
| Something answers on 7878 but is not VibeCLI | Another process took the port | GET /health returns service: "vibecli". If it does not, the responder is not the daemon |
| The assistant replies “no provider configured” | The droplet has no key and no local model | vibecli set-key <provider> <key> on the host, or install Ollama beside it. Keys go to the encrypted ProfileStore, never a config file |
Terraform: Error: POST .../droplets: 401 |
The API token is missing, expired, or lacks write scope | Regenerate at the provider console and re-export it. --tier is unrelated |
Terraform succeeds, terraform output -raw ip is empty |
The apply targeted a different state or workspace | Run it from the same deploy/<platform>/ directory you applied in |
| Local models are unusably slow | These are CPU instances — no GPU on any tier | Point at a cloud provider, or size for it: see Sizing |
| Out of memory building or running a model | The tier’s RAM is the binding constraint | lite is 4 GB, which is a cloud-provider relay, not a local-inference host |
The firewall opens 7878 to the whole internet
deploy/digitalocean/main.tfallows inbound7878from0.0.0.0/0and::/0. The daemon is not unauthenticated — it mints a random 128-bit token at startup and nearly every route requires it — but the port is world-reachable and the token sits on the box.For anything but a throwaway test, restrict the source range to your own address, or drop the rule entirely and reach the daemon over Tailscale. A token that rotates on every restart is a poor fit for a public address.
Teardown
./teardown.sh