Vibebox: a hardened, agent-ready VPS in about ten minutes
Vibebox turns three tokens into a hardened ~$5/mo VPS in ~10 minutes: closed firewall, Tailscale SSH, Claude Code on the box. Why I built it.
There's a setup that's been making the rounds: levelsio's "vibe coding on a server." You rent a cheap box, SSH in, point Claude Code at it, edit files, refresh your domain. No git push. No deploy step. No CI. The server is the dev loop. It's a genuinely lovely way to build small things fast.
The problem is the part nobody shows you: standing the box up.
Doing it by hand is a minefield. Lock yourself out of SSH and you're starting over. Leave port 22 open to the world and you'll get brute-forced or cryptomined before you've finished your coffee. Forget backups and your SQLite file is one fat-fingered rm away from gone. Every one of those is a known failure mode, and every one is easy to hit when you're moving fast.
So I built Vibebox to make that setup boring.
What Vibebox actually does
Hand it three tokens — Hetzner, Cloudflare, Tailscale. Confirm three gates. About ten minutes later you have a hardened, agent-ready dev server.
The shape of it:
- Inbound firewall 100% closed. Web traffic arrives over a Cloudflare outbound tunnel, so there are no open ports to scan. SSH comes in only over Tailscale.
- Claude Code installed on the box. Code from your laptop, or from your phone with Termius + Mosh + tmux. That last one is dangerously fun on a train.
- PHP + SQLite + nginx starter. No framework, no build step.
- Litestream backups for SQLite, pre-wired so you get a sane default instead of assembling one at 1am.
You can drive the whole thing with an agent — paste a prompt, it runs Packer + Terraform + the APIs and you never touch the Cloudflare dashboard. Or run vibebox.sh by hand on a stock Ubuntu box. Same result.
The wall: I locked myself out
First real end-to-end test, I locked myself out of my own server.
The firewall phase armed before Tailscale was actually routing. From the script's point of view Tailscale was "up." In reality the IP hadn't propagated yet. So it slammed the door, dropped inbound, and the only key was on the other side of a network path that didn't exist. Box was unreachable. I nuked it and started over, feeling stupid.
That mistake is the most important feature in the whole thing now.
There's a deadman timer. When the firewall arms, it starts a countdown. You SSH back in over Tailscale and run bash vibebox.sh confirm to cancel it. If you can't get back in — you do nothing. The firewall auto-reverts in ten minutes and you're back where you started. The safe action when you're locked out is to sit on your hands, which is exactly what a panicking person wants permission to do.
The tailscale_up phase also got stricter: it now verifies a real Tailscale IP exists before the firewall is allowed to touch anything. Order of operations is a security control.
A design call I'd make again
PHP + SQLite + nginx. No framework, no build step. Some people will read that and wince.
It's deliberate. It's the stack coding agents make the fewest mistakes on. Plain files, an obvious request lifecycle, no bundler state to get out of sync, no node_modules archaeology. When the thing editing your code is an LLM, "boring and legible" beats "modern and clever" every time. I optimized the starter for the agent, not for my own taste.
What surprised me
Running it on a real Hetzner box caught ten bugs that aren't in any documentation. A Hetzner SKU got renamed. nginx reloads were failing silently. apt hooks hung forever over a no-TTY SSH session. Litestream's release asset names had churned. None of that shows up when you read the guide. It only shows up on metal.
That's the honest takeaway: infra "works on paper" is worth almost nothing. The value here isn't secret knowledge — every step is documented and free to do by hand. That's literally the point. What you're buying is convenience and the ten footguns I already stepped on so you don't.
One more bit of honesty, because it matters: the c bypass alias (--dangerously-skip-permissions) is for disposable boxes only. IS_SANDBOX=1 is not real isolation. The actual safety boundary is that this box is one isolated, backed-up project you could lose without crying.
Try it
If you want a secure little server to vibe-code on without the setup tax, Vibebox is here. It's field-validated, the script is idempotent, and you can read every line before you run it. Please do read it before you run it.
If you spin one up — especially if it saves you an afternoon — come tell me on X. That's the highest compliment this site gets.
Now copy Josh.