# Custom sandbox templates for AI agents, tools preinstalled

> Source: <https://dev.to/ianqqu/custom-sandbox-templates-for-ai-agents-tools-preinstalled-3dn2>
> Published: 2026-06-25 14:00:00+00:00

If you run AI agents in sandboxes, you've probably hit this: every run boots clean, so the agent reinstalls ripgrep, ffmpeg, whatever it needs before it can do anything useful. That's seconds and money spent on the same setup, every single run.

EU-hosted sandboxes for AI agents on orkestr now bake custom templates, so those tools are preinstalled instead of reinstalled. Pick a base, give it a recipe of shell steps, and it builds into a reusable template your agents start from with everything already there. The build happens one time. After that, the agent's first command is a real command, not `apt-get install`

.

Here's the whole thing - one request to build the template:

```
curl -X POST https://api.orkestr.eu/v1/templates \
  -H "Authorization: Bearer $ORKESTR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "my-stack",
    "base_template": "debian-12",
    "recipe": ["apt-get update && apt-get install -y ripgrep ffmpeg"],
    "network": "restricted"
  }'

# -> { "id": "tmpl_01J...", "status": "building", ... }
```

It builds in the background. Once it's ready you pass that id like any other template, and the tools are already in the box:

``` python
from orkestr import Sandbox

with Sandbox.create(template="tmpl_01J...") as sbx:
    print(sbx.exec("rg --version").stdout)   # ripgrep, already there
```

You don't have to write any of that by hand, either. The whole thing is exposed over MCP, so you can hand it to your favorite assistant - Claude, Cursor, whatever speaks MCP - and just say "build me a template with ripgrep and ffmpeg on Debian." The agent calls `create_template`

itself, polls the build, and comes back with the `tmpl_...`

id ready to use. The thing that's going to run in the sandbox can set the sandbox up.

The recipe runs once, at build time, as root. Whatever it does - `apt-get install`

, `pip install`

, cloning a repo, dropping in a config file - gets snapshotted into the image, and every sandbox you launch from that template starts from that snapshot.

You don't strictly *need* a template to install something. The sandbox root is writable, so `apt-get install`

or `apk add`

works at runtime in a plain sandbox. But a runtime install is ephemeral - it's gone the moment the box terminates. Fine for a one-off, wasteful when every run reinstalls the same thing. A template is how you stop paying that cost: install once at build time, and every run after that starts ready. You also get reproducibility - the recipe is the source of truth for what's in the image, so two sandboxes from the same template are the same box.

A few things to know about recipes:

`HOME=/root`

. Anything they install sticks.`apt-get install -y <pkg>`

on `debian-12`

, `apk add <pkg>`

on the Wolfi bases (more on the bases below).`"network": "open"`

if a step genuinely needs the open internet.You can build templates from the [console](https://orkestr.eu/sandboxes), the REST API, or the MCP server, so an agent can build its own templates as a tool call. Full reference is in the [templates docs](https://orkestr.eu/docs/sandboxes/templates).

Early sandbox images were minimal for a real reason. Packing many tenants onto shared infrastructure means a smaller guest is a smaller attack surface. Minimal was a security posture, not a size preference.

That changed when each sandbox became its own [virtual machine](https://dev.to/managed-sandboxes-for-ai-agents/). The isolation boundary is the VM now - its own kernel, its own filesystem, its own slice of hardware. Whether the guest ships `build-essential`

has nothing to do with whether one tenant can reach another. They can't, because the boundary is the machine, not the package list. So the constraint that justified a stripped-down general-purpose base is gone, and it can just be real Debian. That also matches what E2B, Modal, and Daytona default to, so an agent that works against one of them works against ours without relearning the package manager.

The Wolfi bases haven't gone anywhere. Want a small, fast Python or Node start? `python-3.12`

, `python-3.12-bare`

, and `node-22`

are still there and still use `apk`

. Rule of thumb: `debian-12`

when you want a general-purpose box where `apt-get`

works, the Wolfi bases when you want a lean runtime and a quick boot.

Tools in the image are half the picture. The other half is what the sandbox is allowed to reach. Sandboxes default to `network="restricted"`

, which routes egress through an allowlisting proxy - package registries, GitHub, the major LLM APIs, and the Debian mirrors are reachable, and nothing else is. That's the right default for code an LLM wrote thirty seconds ago.

The gap was everything in between. If your agent needed to hit one internal API or a private package mirror, your only escape used to be `network="open"`

- full egress, which is a much bigger give-up than the one host you actually wanted. So restricted sandboxes can now carry their own allowlist. You pass `allow_domains`

and the sandbox reaches the HTTPS hosts you name - on every plan, free included. Naming the one host you trust is strictly safer than opening the whole internet, and you already had `open`

, so there's no reason to paywall the safer option:

``` python
from orkestr import Sandbox

sbx = Sandbox.create(
    template="debian-12",
    network="restricted",
    allow_domains=["pypi.org", "pythonhosted.org", "api.internal.example.com"],
)
```

One thing to know: the list **replaces** the default set, it doesn't add to it. So include the registries you still need - that's why `pypi.org`

is in the example. It's still HTTPS-only and proxy-mediated; no raw sockets, no DNS tricks, no widening to full egress. `Sandbox.limits()`

hands back the default allowlist so you've got a starting point to edit rather than retype.

This is where it loops back to templates. You can bake an `allow_domains`

default into a custom template, and every restricted sandbox built from it inherits that allowlist unless the create call overrides it. So a template isn't just "my tools are preinstalled" - it's "my tools are preinstalled and the box already knows which hosts it's allowed to talk to." Resolution is simple: the per-sandbox flag wins, then the template's baked default, then the platform default.

Custom templates are open - EU hardware, per-second billing, `debian-12`

ready to go. Build one from the [templates docs](https://orkestr.eu/docs/sandboxes/templates) and point your agent at the id.

We're still tuning the build and caching layer, so if you put a real workload through it, tell us how it went - what built slow, what you wanted to bake in that you couldn't. That feedback is what shapes the next pass.

**What's a custom sandbox template?**

An image you define once with a base plus a recipe of shell steps. It builds into a `tmpl_...`

id, and every sandbox you create from it boots with your tools already installed - no per-run setup.

**Do I need a custom template to install packages?**

No. The sandbox root is writable, so `apt-get install`

and `apk add`

work at runtime in a plain sandbox. But a runtime install is ephemeral - it's gone when the sandbox terminates. A template bakes the install in at build time so every run starts with it already there, no reinstall.

**Why does apt-get fail in some templates?**

`python-3.12`

, `python-3.12-bare`

, and `node-22`

are built on Wolfi, which uses `apk`

, not `apt`

. Use `debian-12`

for an `apt`

-based image, or `apk add`

on the Wolfi bases.**Does apt work with restricted networking?**

Yes. The restricted-egress proxy allows `deb.debian.org`

and `security.debian.org`

alongside the package registries it already permitted, so `apt-get`

works on the default `network="restricted"`

without opening full egress.

**Can a restricted sandbox reach my internal API?**

Yes, on every plan. Pass `allow_domains`

with your HTTPS hosts and the sandbox reaches exactly those. The list replaces the default allowlist, so include any package registries you still need. No need to fall back to full open egress for one host. (Baking a default allowlist into a custom template is the one paid-gated path, since custom templates require a paid plan to begin with.)

**Is the old ubuntu-24.04 base gone?**

It's retired from the docs and UI but still bootable as an alias, so existing callers don't break. New templates should use `debian-12`

- it's real Debian with working `apt-get`

; the old one never was.
