Sandboxes and limits
A sandbox is the virtual machine a cloud run happens on. This page covers its sizes and architectures, what it has installed, what it can reach, the limits it runs under and what is kept afterwards.
Every run, every pre-run and every "Check once" of a GitHub repository or your own image project gets a fresh virtual machine. It runs in London (see run location). It is never reused or shared, and it is deleted when the run ends. How it is isolated is in the security overview.
Choose a class
The class sets the size of the virtual machine. Standard is the default.
| Class | vCPUs | Memory | Disk | x86 machine | ARM machine | Suits |
|---|---|---|---|---|---|---|
| Small | 2 shared (x86) / 1 (ARM) | 4 GB | 30 GB | e2-medium | n4a-standard-1 | Light agents that only call external model APIs |
| Standard | 2 | 8 GB | 30 GB | e2-standard-2 | n4a-standard-2 | Most agents |
| Large | 4 | 16 GB | 50 GB | e2-standard-4 | n4a-standard-4 | Agents with a database and several tool backends |
| Extra large | 8 | 32 GB | 100 GB | e2-standard-8 | n4a-standard-8 | Small local models or heavy dependencies |
| 2X large | 16 | 64 GB | 200 GB | e2-standard-16 | n4a-standard-16 | Large builds, several heavy services, or local models that need more memory |
| GPU (coming soon) | 8, and one GPU with 24 GB of GPU memory | 32 GB | 100 GB | g2-standard-8 | None | Local models that need a GPU |
On ARM a vCPU is a full core; on x86 it is a hyperthread. A class costs the same on x86 and ARM; see pricing. Every class is available on every plan, Free included. The GPU class runs on x86 in London only. It is shown greyed out ("coming soon") until the platform's GPU image is ready and has passed its security checks; then the platform switches it on.
To choose, open the project's "Settings" → "Sandbox", pick the "Class" and click "Save". You can also pick it in the "Class" step of the connection wizard.
The platform recommends a class from the test connection (how many containers start beside the agent), the repository's dependency files (very large packages such as torch, TensorFlow or CUDA) and the latest 3 sandbox runs (memory ran out, the disk filled up, preparing reached the time limit, preparing took 10 minutes or more). When it installs very large packages, or the last preparation was slow, it says so beforehand whatever it recommends, such as "This repository installs very large packages such as torch: preparing the environment may take 20 minutes or more". Both places say the recommended class and why beside the class set now, which the class box keeps showing; "Switch to …" applies it. Once the test connection says what it installs (its review.yaml's setup, or the default setup of the repository's root files), only that counts. It never recommends a smaller class.
The platform doesn't change the class by itself unless your organization's owner switched "Ask me before the AI changes things" off under "Organization" → "Policy" → "What the AI does by itself" (on by default). With that on, the AI switches the project to the recommended class within the AI's spending caps and the project says so; "Settings → Sandbox" switches it back.
Every run of the project uses its class, and the run records which class it ran on. A class the platform has disabled can't be chosen for new runs; runs already going are not affected.
Custom spec
"Custom…", the last option under "Class", sets the size yourself, on every plan, Free included. It is mostly for a size no class has, such as 200 GB of memory.
| Field | Values |
|---|---|
| Cores | An even number from 2 to 80 |
| Memory (GB) | 2 to 640 |
| Disk (GB) | 30 to 2,000 |
| GPU | None, 1, 2 or 4 (24 GB of GPU memory each) |
The platform gives you the cheapest machine at least that big:
- Without a GPU, the cores are the larger of the cores you asked for and the memory ÷ 8, rounded up to an even number (a core comes with at most 8 GB), and the memory is the larger of what you asked for and 2 GB per core. ARM follows the same rule up to 64 cores and 512 GB; for more, choose x86.
- With GPUs (x86 only), you get the smallest machine with that many GPUs that covers the cores and memory you asked for: with 1 GPU, 4 cores and 16 GB, 8 and 32, 12 and 48, 16 and 64, or 32 and 128; with 2 GPUs, 24 cores and 96 GB; with 4 GPUs, 48 cores and 192 GB. Anything bigger is refused.
When that is more than you asked for, a line under the fields says what you get, for example "Actually allocated: 26 cores · 200 GB memory · 30 GB disk", beside the price per hour ("Price: about $3.64/hour"). A custom spec runs in London, and you don't choose where. GPUs in a custom spec open together with the GPU class; until then a spec with a GPU is refused with the same "coming soon" note.
A custom spec is billed like a class, per second, at its own price, and the estimate before a run uses that price. Prices and examples are under pricing.
Choose the architecture
Sandboxes run on x86 (amd64) or ARM (arm64). Set it under "Settings" → "Sandbox" → "Architecture":
| Setting | Repository project | Image project |
|---|---|---|
| Auto (default) | x86 | x86 when the image has it, else ARM |
| x86 | x86 | x86, if the image has it |
| ARM | ARM | ARM, if the image has it |
An image project can only choose architectures its image is built for. When the setting names another, a run is refused, and the message names the architectures the image has. A repository defaults to x86 because some Python packages, downloaded binaries and x86-only tools don't install on ARM.
An exec format error, or a package that can't be found for aarch64, means the architecture is wrong. The platform spots these in the log of the pre-run (or of "Check once") and pre-runs again on the other architecture by itself, leaving the project's setting alone. If that passes, it switches the project's architecture, says why under "Settings" → "Sandbox" and offers "Switch back". When both fail, both results are in the pre-run's result. A test waiting on that pre-run moves with it.
When the image has no build for the architecture the setting names, "Check once" runs on the one it has.
Choose the run location
Set it under "Settings" → "Sandbox" → "Run location":
| Setting | Where the sandbox runs |
|---|---|
| Auto (default) | London |
| London | London |
Both run in London today. They differ only if the platform opens another location: Auto may then start a run there when London is full, while London waits in the queue. Where your data is kept is under data processing.
Since 24 October 2026 sandboxes run in London only. Sweden (EU) is no longer offered, and the settings don't show it; projects that had chosen it were moved to Auto. A project still set to it is refused when a run starts and told to set the run location to "Auto"; its runs are never moved to London without asking.
What the machine image has
Sandboxes boot from the platform's machine image, one per architecture:
- Ubuntu 24.04 LTS, with the week's updates.
- Docker Engine with the Compose plugin, and the devcontainer CLI.
- Node.js 20 and 22 (
nodeis 22;node20andnode22are on the PATH), with corepack for pnpm and yarn. - Python 3.12 and 3.11, each with venv, and uv.
- Go 1.23 and Java 21 (OpenJDK).
- Pre-pulled images:
postgres:16-alpine,mysql:8,redis:7-alpine,node:22-alpine,python:3.12-slim. - Two users:
runnerruns the test runner, andagent(uid 1500) runs your agent, with no sudo and no Docker access.
A virtual machine with a GPU (the GPU class, or a custom spec with GPUs) boots from the same x86 image with the GPU driver and the container toolkit added. An agent that runs on the virtual machine sees the GPU directly; an agent that runs in a container gets it too (its container is started with --gpus all).
The image is rebuilt every week. Each run records the image version it used, as part of its version.
What a sandbox can reach
- Inbound: nothing. Sandboxes have no external IP address.
- Outbound: TCP ports 443 and 80 only, through one fixed IP address in London. Port 80 is there for image builds; your agent itself is held to 443.
- Your agent can reach your services on
127.0.0.1through their published ports, and the internet on port 443. It can't reach the cloud metadata server, the platform's control plane or the container network directly.
Allow-list the sandbox address
If your test backend or model gateway admits only known addresses, ask us for the sandboxes' outbound address at admin@nice2.io and add it to your allow-list. The address in Sweden (EU) is no longer used since 24 October 2026. A chat AI is called from the control plane's own fixed address instead.
Limits
Your organization's policy limits cloud runs. Owners set it under "Organization" → "Policy", within what the plan allows (see organization policy).
| Limit | Default | What happens |
|---|---|---|
| Sandboxes running at once | 2 | Further runs wait in a queue and start in order as earlier ones end. |
| Longest run | 120 minutes | 5 minutes before it, the test runner stops after the attempt in progress and the partial results are reported. A run still going at the limit is stopped and its virtual machine deleted. The virtual machine also has a hard limit of its own: it is deleted 10 minutes after the limit. |
| Sandbox hours per month | None | An owner can set a limit. Once used up, new runs are refused with the reason, and sandboxes still running stop after the attempt in progress. The hours come back on the 1st of the month (UTC). |
The plan caps what the policy can be set to:
| Plan | Sandboxes at once | Longest run | Sandbox hours per month |
|---|---|---|---|
| Free | 1 | 60 minutes | No cap |
| Pro | 3 | 240 minutes | No cap |
| Ultra | 10 | 480 minutes | No cap |
| Prepaid | 3 | 240 minutes | No cap |
| Enterprise | No cap | No cap | No cap |
A pre-run or a "Check once" uses a sandbox and sandbox hours like a run. Sandbox time is billed at its class's price, or its custom spec's (unit prices). On Free, Pro and Ultra it comes out of the period's credit first, then the credit balance.
If the virtual machine never reaches the platform
A new virtual machine must reach the platform within 12 minutes of being created. If it doesn't, the platform deletes it and starts another by itself. It is the same run, started by the same person, and it waits for one of the organization's slots; the page shows the reason and the first machine's console output (with keys and tokens masked, as in the job's log). That time is not charged and doesn't count against the month's sandbox hours.
The run ends only when the second machine doesn't reach the platform either. The platform then sends our engineers (admin@nice2.io) the job id, the cloud and region each machine was in, and their boot logs (secrets masked). The page says "The platform's engineers have been told", and there's nothing for you to do.
The same goes when the cloud can't create the virtual machine. That isn't charged either; the platform tries once more a minute later and tells us if that fails too. A run is started again once only, and not after a cancel.
If the virtual machine disappears or loses contact mid-run
The run is marked unfinished and is not retried: your agent may already have changed data in the test backend. Reset the test backend, then start again. The start form asks you to confirm you've checked. Until someone has, automatic runs and runs started with a project API token are refused. What ran is reported as usual, and the attempts that didn't run can be filled in.
When the virtual machine disappears mid-run, its time after the last partial result it handed in isn't charged and doesn't count against the month's sandbox hours, and the platform's fill-in run after it isn't charged either. A run that loses contact with its sandbox (its lease ran out) is billed as usual. See the platform's problem isn't charged.
When a run loses contact with its sandbox mid-way (its lease runs out), the platform first reads the review.yaml the run used. That is the overlay's copy if it hasn't changed since the run started, and otherwise the repository's at that commit.
When all of the conditions below hold, whatever the agent could change was on that virtual machine and went with it, so the platform checks it itself. The job page says "The platform checked", and the audit log records it as the platform's (job.auto_acknowledge, with the backends it found):
- the agent runs on the virtual machine (
driver: commandorharness); withdriver: http, its endpoint and reset URL are on127.0.0.1orlocalhost; - the tools go through the tool proxy to an upstream on
127.0.0.1(attach: http_proxy), or are the platform's fixture; - the data reset is an HTTP address on
127.0.0.1, or SQL whose database address on127.0.0.1is set inrunner_env; the data reads are a SQLite file on the machine, or an address on127.0.0.1set inrunner_env; - the only project secrets the agent is handed are model keys (
OPENAI_API_KEYand the like), and the addressesagent.envandrunner_envhand it are on127.0.0.1.
If any one of them doesn't hold, a person still checks. Each of these counts as not holding:
- an outside address;
- an address from a project secret (the platform can't read its value);
- a data reset that is a command;
- MCP tool servers (
mcp_wrap, whose connections aren't inreview.yaml), or a connection that only sees replies; - a
review.yamlthat can't be read, or that changed after the run started.
What is kept after a run
| What | How long |
|---|---|
| Your source code or image, and the project secrets | Deleted with the virtual machine |
| The run bundle's evidence files and the sandbox logs | The organization's retention period, 90 days by default |
| The report and its verdict | As long as the project exists |
| The dependency cache (repository projects) | 30 days |
A container image built from your Dockerfile or devcontainer.json, per commit | 30 days |
A run bundle may unpack to at most 512 MB, 10,000 files and 64 MB per file.
Next steps
- review.yaml reference:
environmentandservices. - Organization and members: change the policy.
- Billing and usage: what sandbox time costs and where to see it.