2026-10-12
How to Sandbox AI Agents on a $25 VPS: Two AI Coders, One Small Server
I wanted to know how to sandbox AI agents before I let two of them write code on a server I pay for. The question is not hypothetical now that coding CLIs are good enough to ship real work. This post is about the setup I run today: Grok Build and OpenAI’s Codex CLI share one small rented VPS, while Claude Code on a different machine acts as the coordinator. Nothing here is theoretical. Every layer exists because something went wrong, or because I could see exactly how it might.
The Problem: One Box, Two Coders
Running two AI coding agents on the same machine sounds like a bad idea until you realize the alternative is worse. I am running multiple AI agents at once on one small box, and I chose that on purpose. Cloud sandboxes are convenient, but they move the problem somewhere else: you still have to trust the provider, you still pay for egress and storage, and you lose the ability to wire things together exactly the way you want. Docker containers are useful, but they are not the whole answer. A container with full network access and a shared kernel is still one mistake away from trouble.
I chose a small rented VPS instead: two vCPUs, about four gigabytes of RAM, about thirty-five gigabytes of disk, running Debian, costing roughly twenty-five dollars per year. It holds no keys or routes into the rest of my infrastructure. Its nightly backup is pulled by another server, so the little box never needs credentials to anything else. That single decision already removes a lot of blast radius. If the agents ever did something stupid, the damage is bounded by what is on that one cheap machine.
But cheap does not mean careless. The goal is to run AI agents safely, which means isolating them from each other, from me, from the network, and from anything outside their project folders. The setup below is what I landed on after several iterations. I think of it as depth through layering: each control is simple, but together they add up.
The Seven Layers
Here is the short version:
- Separate Linux user accounts with closed home directories.
- Each agent runs inside its own sandbox.
- Each agent gets its own outbound proxy with a hostname allowlist.
- A private Forgejo git server keeps code isolated by repository and by account.
- Terminal sessions belong to the human account, not to the agents.
- Temp folders, test ports, memory, and CPU are split between the agents.
- Agents cannot install system programs; they file requests in a NEEDS file instead.
The first layer is the simplest: each agent has its own unprivileged Linux user. Their home directories are closed to each other. This is not exciting, but it matters. File permissions and process ownership are the foundation everything else sits on.
On top of that, each agent has its own sandbox. Grok Build runs in a custom profile I built on xAI’s strictest built-in level. It can read only its own project folder and basic system files, and it can write only to its project folder. I switched network access back on because a coding agent that cannot reach package registries is not much use. Codex runs in its own Codex CLI sandbox, configured for workspace-write access with network enabled, but it is not allowed to write to the shared temporary folder. The point is not to block all activity; it is to make sure activity happens only where I expect it.
Network isolation comes next. Each agent has its own outbound HTTP proxy with a default-deny hostname allowlist. The allowed destinations are the AI vendor’s own API, GitHub, npm, PyPI, and Wikimedia. The firewall forces each agent’s traffic through its own proxy and blocks the other agent’s proxy. Everything is logged. This is the layer that prevents a confused agent from phoning home to a server I have never heard of, or from reaching a package source I did not approve.
Code lives on a single private Forgejo git server running on the same box and listening only on the machine itself. This is my Forgejo for AI agents. Sign-ups are disabled. There is one account per agent, and every repository is private. When I want AI code review, I give one agent read-only access to one repository. I tested this: Codex could clone Grok’s repo, and its push was refused. That one test told me the model works.
The terminal sessions matter more than they sound. They run under the human account, not under the agents. The reason is straightforward: an agent that can reach its own terminal can type “yes” into its own permission prompt. Keeping the interactive shell separate from the agent’s identity removes that path entirely.
Shared resources are split down the middle. Each agent has its own temporary folder, its own range of local test ports, and its own memory and CPU cap of about one and a half gigabytes and one and a half cores. The firewall blocks either agent from connecting into the other’s port range. The caps are not punitive; they are there so one runaway process cannot starve the rest of the system.
Finally, agents cannot install system programs because they do not have root. I installed headless Chromium, ImageMagick, and Hugo myself when asked, before the NEEDS file existed. From now on, when they need something, they write it in a NEEDS file. I install it myself. The delay is small, and the accountability is large.
What Went Wrong on the Way
The first version of this setup was not the one I run now. The first attempt put Grok in a read-only mode. It could not save a single file, and its commands had no internet. The job stalled immediately. A sandbox that prevents all work is not a sandbox; it is a cage.
The obvious fix was to relax Grok to a “workspace” profile. That would have let it read the whole server. A second review caught it before I used it, and the final fix was to build on the strict profile and only switch the network back on. That is the recurring theme here: tighten the read surface, loosen only what the tool actually needs.
Another surprise was resource limits. I wrapped a memory limit around the agent’s launch command, and it silently did nothing. The login step moved the process into its own per-user group, so the limit had to be applied to that group instead. A limit that looks correct but does not bind is worse than no limit, because it gives you false confidence.
Codex login: “localhost refused to connect” and the device code fix
- The normal ChatGPT sign-in sends the browser back to localhost, which is your own computer, not the server, so the page says refused to connect.
- In ChatGPT Settings, Security, switch on “Enable device code sign-in for Codex”.
- Then run the codex device code login on the server and enter the code in your browser.
The last bump was permission prompts. I tried to auto-approve the agents’ prompts with a script, and Claude’s safety checker blocked the script itself. Approvals stay with a human, or one command at a time. That turned out to be a feature, not a bug.
A Real Job They Did
This is not a toy setup. Grok recently built a full-screen photo slideshow for the home page of my small business site. It used six freely licensed Copenhagen photos, included credits, and supported keyboard navigation and reduced motion. The photos went from about 3.7 MB down to about 350 KB for phones. It is waiting for review before it goes live.
Then Codex reviewed Grok’s work independently, with read-only access, and produced a ranked findings list. The cross-review was not theater. It flagged real problems, such as pause-state bugs and incomplete photo credits on the slides, which went back to Grok to fix before anything goes live.
How to Sandbox AI Agents on Your Own Server
If you want to sandbox AI coding agents on your own server, start here:
- Use a cheap, dedicated machine that holds no credentials to anything else.
- Give each agent its own unprivileged user account with a closed home directory.
- Run each agent in a sandbox that limits reads and writes to its own project space.
- Force each agent through its own outbound proxy with a default-deny allowlist.
- Keep code on a private git server with per-agent accounts and read-only cross-review.
- Run terminal sessions under the human account, not the agent’s identity.
- Split temp folders, test ports, memory, and CPU between agents.
- Never give agents root. Make them file NEEDS requests instead.
This checklist is not a guarantee, but it is a sane starting point. The point is not to eliminate risk; the point is to make every failure require multiple mistakes.
Closing
I run this privately operated setup because it gives me control without complexity I do not need. Two agents share one small server, each in its own lane, each able to do real work, each unable to reach the things that matter. That balance is what I was looking for when I first asked how to sandbox AI agents. The answer, as usual, is not one big fix. It is a stack of small ones.