Safely Execute Untrusted Code with Remote Sandboxes

Three new sandbox providers for safely running agent code in isolated cloud containers.

Paul ScanlonPaul Scanlon·

Mar 11, 2026

·

6 min read

Mastra Workspaces now support remote sandboxes. We're launching with three providers: Daytona, E2B, and Blaxel, with more to follow.

With remote sandboxes, agents can install packages, run untrusted code, or spawn long-lived processes in their own isolated environment instead of on your application server. Each sandbox has its own filesystem, network, and process space. Agent workloads don't compete for CPU or memory. And if an agent does something destructive, the blast radius is contained.

What are workspaces?

If you haven't read the Workspaces overview, here's the short version. A workspace has two layers:

  • Filesystem: Where files live (local disk, S3, GCS)
  • Sandbox: Where commands run (local, Daytona, E2B, Blaxel)
 1// src/mastra/agents/agent.ts
 2
 3import { Agent } from "@mastra/core/agent";
 4import { Workspace, LocalFilesystem, LocalSandbox } from "@mastra/core/workspace";
 5
 6export const agent = new Agent({
 7  id: "agent",
 8  name: "Agent",
 9  model: "anthropic/claude-sonnet-4-6",
10  workspace: new Workspace({
11    mounts: {
12      "/data": new LocalFilesystem({ basePath: "./my-data" }),
13    },
14    sandbox: new LocalSandbox(),
15  }),
16});
  • mounts map paths to filesystem providers like S3 or GCS, so your agent can work with cloud storage the same way it would a local filesystem.
  • sandbox is where commands actually execute. You can pair any filesystem with any sandbox.

When you assign a workspace to an agent, Mastra gives it tools for reading and writing files, listing and searching directories, and running commands. The tools work the same regardless of which sandbox is configured.

Daytona

@mastra/daytona wraps the Daytona SDK and supports snapshots, network isolation, persistent volumes, and resource configuration.

 1// src/mastra/agents/agent.ts
 2
 3import { Agent } from "@mastra/core/agent";
 4import { Workspace } from "@mastra/core/workspace";
 5import { DaytonaSandbox } from "@mastra/daytona";
 6
 7export const agent = new Agent({
 8  id: "agent",
 9  name: "Agent",
10  model: "anthropic/claude-sonnet-4-6",
11  workspace: new Workspace({
12    sandbox: new DaytonaSandbox({
13      apiKey: process.env.DAYTONA_API_KEY,
14      target: "us",
15      language: "typescript",
16      snapshot: "my-snapshot-id",
17    }),
18  }),
19});

Daytona features

  • Snapshots: Let you pre-bake a sandbox image so cold starts are near-instant. If you're installing dependencies or FUSE tools, do it once in a snapshot instead of on every startup.
  • Network isolation: Set networkBlockAll: true to block all outbound traffic, or use networkAllowList to whitelist specific CIDRs. If your agent shouldn't be calling external APIs, this is how you enforce it.
  • Persistent volumes: Survive sandbox restarts. Attach them with volumes: [{ volumeId: 'my-vol', mountPath: '/persist' }] and data stays around even if the sandbox is stopped and restarted.
  • Ephemeral mode: Set ephemeral: true and the sandbox is deleted immediately when it stops. One-shot tasks, no cleanup.
  • Resource configuration: CPU, memory, and disk in GiB. Useful when your agent is doing something heavier than echo "hello".
  • Reconnection: Pass the same id to the constructor and it reconnects to the existing sandbox. If it's stopped or archived, it restarts automatically. If it's been destroyed, a fresh sandbox is created instead.
  • Direct SDK access: sandbox.instance exposes the underlying Daytona Sandbox object. If you need Daytona's filesystem, git, or LSP APIs directly, they're there.

E2B

@mastra/e2b wraps the E2B SDK and supports template-based sandboxes, background processes, and self-hosted deployments.

 1// src/mastra/agents/agent.ts
 2
 3import { Agent } from "@mastra/core/agent";
 4import { Workspace } from "@mastra/core/workspace";
 5import { E2BSandbox } from "@mastra/e2b";
 6
 7export const agent = new Agent({
 8  id: "agent",
 9  name: "Agent",
10  model: "anthropic/claude-sonnet-4-6",
11  workspace: new Workspace({
12    sandbox: new E2BSandbox({
13      apiKey: process.env.E2B_API_KEY,
14      template: "my-custom-template",
15      timeout: 300_000,
16    }),
17  }),
18});

E2B features

  • Templates: You can configure them four ways:

    1. Pass a template ID string to use an existing E2B template
    2. Use a TemplateBuilder to define one programmatically (aptInstall, pipInstall, npmInstall)
    3. Pass a function that customizes the default template
    4. Pass nothing and get a default template with s3fs and FUSE pre-installed

    Templates are deterministically hashed, so the same config produces the same template ID. They're only built once, then reused across instances.

  • Self-hosted support: Set domain, apiUrl, or accessToken to point at your own E2B deployment.

  • Background processes: Full support via E2BProcessManager: spawn(), get(), kill(), sendStdin(), and streaming output callbacks. You can also reconnect to externally spawned processes via get(pid).

Blaxel

@mastra/blaxel wraps the Blaxel SDK and supports nine language runtimes, port exposure, and TTL-based lifecycle management.

 1// src/mastra/agents/agent.ts
 2
 3import { Agent } from "@mastra/core/agent";
 4import { Workspace } from "@mastra/core/workspace";
 5import { BlaxelSandbox } from "@mastra/blaxel";
 6
 7export const agent = new Agent({
 8  id: "agent",
 9  name: "Agent",
10  model: "anthropic/claude-sonnet-4-6",
11  workspace: new Workspace({
12    sandbox: new BlaxelSandbox({
13      timeout: "5m",
14      memory: 4096,
15      runtimes: ["node", "python", "bash"],
16      ports: [{ name: "api", target: 3000, protocol: "HTTP" }],
17    }),
18  }),
19});

Blaxel features

  • Nine runtimes: Node, Python, Bash, Ruby, Go, Rust, Java, C++, and R. The default is ['node', 'python', 'bash'] but you can configure any combination.
  • Port exposure: Run servers inside the sandbox and access them externally. Configure ports with protocol (HTTP, TCP, UDP) via the ports option.
  • TTL-based lifecycle: Human-readable timeout strings like '5m' or '1h'. When the TTL expires, the sandbox stops.
  • Abort signals: Cancel running commands via abortSignal, with partial output preserved in the result.
  • No stdin support: If your agent needs to send input to a running process, use Daytona or E2B instead.

Choosing a provider

This is one of those "it depends" situations. Here's how I'd think about it:

Daytona if you need network isolation, persistent volumes, or fine-grained resource control. Snapshots make it good for repeatable environments where cold starts matter. It has the most configuration surface and the most robust reconnection logic.

E2B if you want ephemeral, fast-spinning sandboxes. The template system is well-designed: build once, reuse everywhere. Good fit for code execution tasks that don't need persistent state.

Blaxel if you need broad runtime support or port exposure for running servers inside the sandbox. The TTL-based lifecycle keeps things simple. Nine runtimes means your agent can run Go or Rust without swapping providers.

All three support S3 and GCS mounting, background processes, and automatic reconnection. Mastra's Workspace API is the same across all of them, so switching providers is a one-line change.

Pre-building for faster cold starts

All providers support some form of pre-building to avoid installing FUSE tools at runtime:

  • Daytona: Create snapshots with s3fs and gcsfuse pre-installed.
  • E2B: Build custom templates with TemplateBuilder.aptInstall().
  • Blaxel: Use custom Docker images with tools baked in.

If you're mounting cloud storage, pre-building is worth doing. Installing gcsfuse from scratch takes a few seconds, and that adds up across requests.

Wrapping up

Agents need to run code, but you probably don't want them doing it on the same server as your application. Daytona, E2B, and Blaxel each give you a way to move that execution into isolated containers that spin up fast and clean up after themselves.

You can develop locally using LocalSandbox then switch to a remote provider for prod. We're starting with these three, and we'll be adding more soon.

For setup guides and provider-specific configuration, see the sandbox docs. For a broader look at how filesystems, mounts, and sandboxes fit together, see the workspace overview.

Share:
Paul Scanlon
Paul ScanlonTechnical Product Marketing Manager

Paul Scanlon sits between Developer Education and Product Marketing at Mastra. Previously, he was a Technical Product Marketing Manager at Neon and worked in Developer Relations at Gatsby, where he created educational content and developer experiences.

All articles by Paul Scanlon