How to sandbox AI agents safely: one container per task
Originally published on the Hubfly space blog . Updated 21 October 2026. TL;DR What you get: a fresh, locked-down container for every agent task, a budget with alerts for all agent work, an optional wallet and key per agent, and an off switch you can reach in two clicks. Who it's for: developers le

Posted on Oct 21 • Originally published at blog.hubfly.space • AI-assisted
Originally published on the Hubfly space blog.
Your agent writes a Python script and runs it. Most days that's fine. Then one day it deletes the wrong folder, prints your API keys into a log, or loops for six hours on a paid machine. If that code ran on your laptop or your main server, the damage lands on everything you own.
In this guide you'll give every agent task its own small, locked-down container on Hubfly space, put a budget around all of it, and set up a way to stop everything at once. The goal: when an agent does something dumb, only a throwaway box is hurt, the bill stays small, and you can switch it off in seconds.
Try it on Hubfly space. Steps 1 to 4 and 7 work on the free shared tier. Sub-account wallets (Step 5) need paid balance.
Many sandbox guides cover containers, resource limits and network rules. Fewer cover spend limits, revocable keys per agent, or cleaning up what the agent leaves behind. A safe setup has four walls:
A Hubfly space Cell project holds containers, volumes, environment variables and a budget. Keeping agent work in its own project keeps its private network, its secrets and its bill apart from your real apps.
Why the budget? Agents don't get tired. A loop that keeps starting containers keeps going until something stops it. The project's Settings → General & Budget page shows your projected cost against the cap, and you get a notification at 80% and at 100%. Treat it as an alarm, not a guaranteed stop: Step 7 is how you actually stop things.
Every task the agent runs gets a fresh container. When the task ends, you delete it. Nothing carries over, so a broken or poisoned task can't affect the next one.
python /task/run.py Enter fullscreen mode Exit fullscreen mode Avoid commands that serve files or open ports (like python -m http.server ). Anything listening is reachable by every other container in the project.
Containers in the same project share a private network. If you run untrusted tasks side by side, run them one at a time per project, or give each agent its own sub-account (Step 5).
Linux "capabilities" are small pieces of root power: changing file owners, opening raw network sockets, creating device files. A script that reads a CSV needs almost none of them. About half of the top sandbox guides skip this wall.
Go to Configuration → Security Capabilities. Two protections are already on by default:
Below that, the container starts with 13 default capabilities. Under Drop capabilities, click these five:
The Selection Summary now reads Added 8, Dropped 5.
Keep CHOWN, SETUID, SETGID and DAC_OVERRIDE unless you know your image never switches users or fixes file ownership at start-up. Many official images do, and they fail to start without them. Tighten one capability at a time and watch the logs.
Click Continue to Storage and add nothing. With no volume, the dashboard says "Ephemeral container storage will be used", so nothing the task writes survives it. Then Review and Deploy Container.
Don't add a domain or a public port to a task container. Without one, nobody outside can reach it. The Review step shows its private address, like agent-task-001-<id>.eucentral.hubfly.internal, and the private network is isolated to containers inside the same project.
So far, every agent works inside your account. If an agent holds a key to your account and that key leaks, all your projects are exposed. Hubfly space sub-accounts fix that: each agent becomes a separate tenant with its own projects, its own wallet and its own API keys.
Careful: Revoke and Suspend act immediately, with no confirmation dialog. And suspending a sub-account doesn't stop its running containers, so stop or delete them first (Step 7).
Clicking through the dashboard for every task doesn't scale. Your orchestrator (the code that hands tasks to the agent) can do Steps 2 and 3 with two API calls: create before the task, remove after. Field names follow the containers API reference. Check the resources units there before you rely on the values below, which mirror the shared tier.
# Run on your orchestrator, never inside the sandbox. # HUBFLY_TOKEN: a key for the account that owns PROJECT # (for a sub-account agent: its hfsk_ key and a project inside that sub-account). API = https://api.hubfly.space/api/v1 PROJECT = <your-project-id> TASK = agent-task- $( date +%s ) curl -sS -X POST " $API /projects/ $PROJECT /containers/create" \ -H "Authorization: Bearer $HUBFLY_TOKEN " \ -H "Content-Type: application/json" \ -d '{ "name": "' " $TASK " '", "kind": "job", "tier": "shared", "source": { "type": "docker", "dockerImage": "ghcr.io/your-org/agent-runner:1.0" }, "resources": { "cpu": 0.3, "ram": 256, "storage": 2 }, "security": { "noNewPrivileges": true, "seccompProfile": "runtime-default", "dropCapabilities": ["NET_RAW", "MKNOD", "SYS_CHROOT", "SETFCAP", "SETPCAP"] } }' Enter fullscreen mode Exit fullscreen mode Bake the task command into the image ( CMD ["python", "/task/run.py"] ), so the API call doesn't need it. When the task is done, remove the container.
This is permanent:
curl -sS -X DELETE \ " $API /projects/ $PROJECT /containers/<container-id>/remove" \ -H "Authorization: Bearer $HUBFLY_TOKEN " Enter fullscreen mode Exit fullscreen mode Run the remove call in a finally block or an exit trap, so it also runs when the task crashes. Forgotten containers are a common way sandbox bills creep up.
Know where this is before you need it. Open the project's Settings → Danger Zone (for a sub-account agent, the project inside its sub-account):
If the agent has its own sub-account, open it next and Revoke its key. Hibernate already stopped what was running; the revoked key means the agent can't start anything new.
1. "Monthly free tier compute hours exhausted. Upgrade this container to a dedicated tier to resume running." The account has used its 360 free shared-tier hours for the month. Stop containers you don't need, or pick Dedicated Tier for task containers. A dedicated 1 vCPU / 512 MB container costs $0.005 an hour, so a 10-minute task costs less than a tenth of a cent.
2. The container exits right away after you dropped capabilities. The image needed one of them, usually to switch users or fix file ownership at start-up. Open Logs, look for "operation not permitted", and add back only that capability (often SETUID / SETGID or CHOWN ).
3. You suspended an agent and it's still costing money. Suspend blocks the agent's keys and changes, but doesn't stop running containers. Stop or delete them first, then suspend.
Rates: $0.004 per vCPU-hour and $0.002 per GB-hour of RAM.
The dashboard says shared containers suspend after 15 minutes of inactivity. Don't count on it to clean up: delete task containers yourself.
An agent sandbox isn't one setting. It's four walls: a fresh container per task, fewer Linux permissions, money limits, and an off switch you've already found. Set them up once, and a bad agent run costs you a throwaway container and a few cents.
Read next: Docker health checks that catch real problems, explained simply
Create your agent sandbox project on Hubfly space: free shared tier with 360 compute hours a month.