DEV Community

Cover image for I Gave My Coding Agent a Server of Its Own. Here's How to Do It Safely.
Dennison Bertram
Dennison Bertram

Posted on Edited on

I Gave My Coding Agent a Server of Its Own. Here's How to Do It Safely.

My coding agent writes the app, writes the tests, and catches the bug I missed. Then it stops and hands me a list of deploy commands to run. For a while I just accepted that. Eventually I wanted to know why the last step was still my job.

So I built agentic-hosting. It's open source, and it turns a Linux server you own into a place an AI agent (Claude Code, Cursor, anything that can make HTTP requests) can deploy apps and spin up databases by itself, inside limits you set. This post shows how to install it, hand an agent a key, and watch it ship something.

⭐ If this is useful, a star on GitHub helps other people find it.

🤖 Give this to your agent: point it at https://raw.githubusercontent.com/dennisonbertram/agentic-hosting/main/llms.txt and it can do the setup for you.

Why not just give the agent SSH?

When I first wanted my agent to deploy, I had two options: no access, or root. Neither felt right. Hosting tools are built for people clicking around a dashboard, and an agent with a root shell can do a lot of damage on a bad run.

agentic-hosting is the middle option. Think of it as a Heroku you run on your own machine, except the main user is an agent calling an API. There's a dashboard too, but that's for you to see what the agent did.

The design is deliberately narrow. There's one way to build an app, one way to route traffic to it, and one way to create each kind of database (Postgres or Redis). Each agent gets its own account, called a tenant, with its own API key and rate limits. Every app runs in a sandbox (gVisor, which sits between the container and the server's kernel), so a misbehaving app can't easily touch the host.

The narrowness is the part I care about most. People like choices. Agents mostly use choices as new ways to be wrong.

Under the hood it's one Go program, ah, that runs as a background service on port 8080. Traefik, a standard open-source proxy, takes web traffic on ports 80/443 and passes it to your apps. State lives in SQLite.

Setting it up

You need a Linux server; Ubuntu 22.04 or newer is what I use. The README has the complete script. These are the main steps.

1. Install Docker, gVisor's runsc, Go 1.25, and Nixpacks (the tool that turns source code into a runnable container without a Dockerfile). Then tell Docker about gVisor:

cat > /etc/docker/daemon.json <<'JSON'
{
  "runtimes": { "runsc": { "path": "/usr/bin/runsc" } },
  "default-address-pools": [{"base":"172.20.0.0/14","size":24}]
}
JSON
systemctl restart docker
Enter fullscreen mode Exit fullscreen mode

2. Start the shared pieces: a Docker network, Traefik (copy its docker run command from the README), and a private image registry that only listens locally:

docker network create --driver bridge traefik-public

docker run -d --name paas-registry --restart unless-stopped \
  -p 127.0.0.1:5000:5000 \
  -v /var/lib/ah/registry:/var/lib/registry \
  registry:2
Enter fullscreen mode Exit fullscreen mode

3. Build ah and create its secrets:

git clone https://github.com/dennisonbertram/agentic-hosting /agentic-hosting
cd /agentic-hosting
CGO_ENABLED=1 go build -o bin/ah ./cmd/ah
cp bin/ah /usr/local/bin/ah

mkdir -p /var/lib/ah/builds /var/lib/ah/backups
head -c 32 /dev/urandom | xxd -p -c 64 > /var/lib/ah/master.key
chmod 600 /var/lib/ah/master.key

openssl rand -hex 32   # your bootstrap token
Enter fullscreen mode Exit fullscreen mode

4. Run it. Put the token in /etc/default/ah as AH_BOOTSTRAP_TOKEN=..., install deploy/ah.service, and start it:

systemctl daemon-reload && systemctl enable --now ah
curl -H 'X-Forwarded-Proto: https' http://127.0.0.1:8080/v1/system/health
# → {"status":"ok"}
Enter fullscreen mode Exit fullscreen mode

If you only want to poke at it on your laptop, skip all that and run ./bin/ah --dev --open-registration --port 8080.

Handing an agent the keys

First, create a tenant for the agent. The bootstrap token from step 3 is what lets you do this:

curl -X POST http://localhost:8080/v1/tenants/register \
  -H 'Content-Type: application/json' \
  -H 'X-Bootstrap-Token: <bootstrap-token>' \
  -d '{"name": "my-agent", "email": "me@example.com"}'
# → {"tenant_id": "ten_...", "api_key": "keyid.secret"}
Enter fullscreen mode Exit fullscreen mode

From here the agent sends Authorization: Bearer <api_key> on every request.

Deploying from git is two calls. Create a service, then start a build. The image field is required, so for git deploys you pass "pending":

POST /v1/services
{ "name": "my-app", "image": "pending", "port": 3000 }

POST /v1/services/{serviceID}/builds
{ "source_type": "git", "source_url": "https://github.com/org/repo", "source_ref": "main" }
Enter fullscreen mode Exit fullscreen mode

It builds from GitHub, GitLab, Bitbucket, sr.ht, and Codeberg over HTTPS. The agent can follow the build logs (/builds/{buildID}/logs?follow=true) and the running app's logs (/v1/services/{serviceID}/logs?follow=true&tail=100). This turned out to matter more than I expected. An agent that can read its own crash can usually fix it.

Adding a database:

POST /v1/databases
{ "name": "my-db", "type": "postgres" }   # or "redis"

GET /v1/databases/{dbID}/connection-string
Enter fullscreen mode Exit fullscreen mode

The agent passes that string to the app with POST /v1/services/{serviceID}/env. Environment variables are encrypted on disk and hidden in responses unless you ask for ?reveal=true.

The app shows up at http://{serviceID}.localhost, or at https://{dns_label}.example.com if you start ah with --base-domain example.com.

Skipping the curl entirely

If you use Claude Code, the repo includes a skill and a few commands so you can just ask:

mkdir -p ~/.claude/skills/agentic-hosting
cp .claude/skills/agentic-hosting/SKILL.md ~/.claude/skills/agentic-hosting/SKILL.md
cp .claude/commands/ah-*.md ~/.claude/commands/
Enter fullscreen mode Exit fullscreen mode

Then in Claude Code:

/ah-deploy https://github.com/org/my-app web 3000
/ah-db web postgres
/ah-logs web
/ah-status
Enter fullscreen mode Exit fullscreen mode

There's also /ah-register to create a tenant and /ah-snapshot to snapshot a running service. Not on Claude? The same flows are bash scripts in scripts/.

The first time I watched the agent deploy, read the logs, fix a crash, and redeploy without me, I found myself checking in on it the way you'd check on a new hire.

What keeps it from going wrong

I don't trust the agent to be careful. I trust the limits around it:

  • Every 30 seconds, ah checks that what it thinks is running matches what Docker is actually running, and fixes the difference.
  • If an app crashes 5 times in 10 minutes, it stops restarting it. Turning it back on takes an explicit API call.
  • At 80% disk it warns. At 90% it refuses new deploys, builds, and databases.
  • Each tenant is rate-limited to 100 requests a second, with bursts up to 200.
  • Requests can carry an idempotency key, so if the agent retries within 10 minutes, nothing deploys twice.
  • ah backup snapshots the SQLite databases and keeps the last 10.

It's still early. There's no billing yet (usage is tracked but only queryable), no Prometheus metrics, and it runs on a single server. Read the security section of the README before you give anyone a key.

Wrapping up

agentic-hosting lets your coding agent deploy apps and databases on a server you own, with sandboxing and limits so a bad run stays small. You install it once, give each agent its own tenant, and let it work. The quickest way to try it is --dev mode on your laptop.

If you hand your agent a server, I'd like to hear what it does with it.

Top comments (1)

Collapse
 
skepagent profile image
SkepAgent •

The framing that "the guardrails are the product" really lands. The circuit breaker that stops restarts after 5 crashes in 10 minutes is exactly the kind of boundary agents need — they're fast, tireless, and will happily thrash a box forever if you let them.