RailwaySandbox
Executes commands in ephemeral, isolated Railway sandboxes. Each sandbox is an isolated Debian Linux VM provisioned on demand through the Railway TypeScript SDK. Supports command execution with streaming output, command timeouts, configurable idle timeout, ISOLATED/PRIVATE network isolation, custom base images via the Railway template builder, forking a running sandbox, and reattaching to an existing sandbox by ID.
For interface details, see WorkspaceSandbox interface.
InstallationDirect link to Installation
- npm
- pnpm
- Yarn
- Bun
npm install @mastra/railway
pnpm add @mastra/railway
yarn add @mastra/railway
bun add @mastra/railway
Set your Railway credentials in one of three ways.
- Shell export
- .env file
- Constructor
export RAILWAY_API_TOKEN=your-api-token
export RAILWAY_ENVIRONMENT_ID=your-environment-id
RAILWAY_API_TOKEN=your-api-token
RAILWAY_ENVIRONMENT_ID=your-environment-id
new RailwaySandbox({
token: 'your-api-token',
environmentId: 'your-environment-id',
})
UsageDirect link to Usage
Add a RailwaySandbox to a workspace and assign it to an agent:
import { Agent } from '@mastra/core/agent'
import { Workspace } from '@mastra/core/workspace'
import { RailwaySandbox } from '@mastra/railway'
const workspace = new Workspace({
sandbox: new RailwaySandbox({
// token + environmentId read from RAILWAY_API_TOKEN / RAILWAY_ENVIRONMENT_ID
idleTimeoutMinutes: 30,
}),
})
const agent = new Agent({
id: 'code-agent',
name: 'Code Agent',
instructions: 'You are a coding assistant working in this workspace.',
model: 'anthropic/claude-sonnet-4-6',
workspace,
})
const response = await agent.generate(
'Print "Hello, world!" and show the current working directory.',
)
console.log(response.text)
Private networkingDirect link to Private networking
Join the environment's private network to reach other Railway services (for example postgres.railway.internal):
const workspace = new Workspace({
sandbox: new RailwaySandbox({
networkIsolation: 'PRIVATE',
env: { NODE_ENV: 'production' },
}),
})
The default ISOLATED mode allows outbound internet access only, with no private network connectivity.
Custom base image (templates)Direct link to Custom base image (templates)
Pre-install packages and run setup steps so every sandbox starts ready. Pass a builder callback over the Railway template builder — the template is built once on the first start():
const workspace = new Workspace({
sandbox: new RailwaySandbox({
template: t => t.withPackages('git', 'curl').run('npm i -g pnpm').workdir('/app'),
}),
})
You can also pass a pre-built SandboxTemplate to reuse it across sandboxes without rebuilding. Templates are ignored when sandboxId is set, since reattaching uses the existing sandbox's filesystem.
Forking a running sandboxDirect link to Forking a running sandbox
Clone a running sandbox's filesystem into a new, independent sandbox — a fresh boot, not live processes. The returned RailwaySandbox is already started:
const child = await sandbox.fork({ idleTimeoutMinutes: 15 })
const result = await child.executeCommand('cat', ['/app/state.json'])
console.log(result.stdout)
The forked sandbox inherits the parent's credentials and defaults unless overridden via the fork() options.
Streaming outputDirect link to Streaming output
Stream command output in real time via onStdout and onStderr callbacks:
await sandbox.executeCommand('bash', ['-c', 'for i in 1 2 3; do echo "line $i"; sleep 1; done'], {
onStdout: chunk => process.stdout.write(chunk),
onStderr: chunk => process.stderr.write(chunk),
})
Both callbacks are optional and can be used independently.
Reattaching to an existing sandboxDirect link to Reattaching to an existing sandbox
A Railway sandbox outlives the process that created it. Reconnect by its Railway ID instead of provisioning a new one:
const sandbox = new RailwaySandbox({ sandboxId: 'existing-railway-sandbox-id' })
await sandbox._start()
const result = await sandbox.executeCommand('cat', ['/tmp/state.txt'])
Constructor parametersDirect link to Constructor parameters
id?:
token?:
environmentId?:
sandboxId?:
idleTimeoutMinutes?:
networkIsolation?:
env?:
template?:
timeout?:
instructions?:
PropertiesDirect link to Properties
id:
name:
provider:
status:
railway:
processes:
MethodsDirect link to Methods
fork:
Background processesDirect link to Background processes
RailwaySandbox includes a built-in process manager for spawning and managing background processes. Each spawned process runs as a Railway exec session.
const sandbox = new RailwaySandbox()
await sandbox.start()
// Spawn a background process
const handle = await sandbox.processes.spawn('node server.js', {
env: { PORT: '3000' },
onStdout: data => console.log(data),
})
// Interact with the process
console.log(handle.stdout)
await handle.kill()
Railway's exec API does not stream stdin, so sendStdin() is not supported.
See SandboxProcessManager reference for the full API.
Editor providerDirect link to Editor provider
Register the provider with MastraEditor to hydrate stored sandbox configs into runtime instances:
import { railwaySandboxProvider } from '@mastra/railway'
const editor = new MastraEditor({
sandboxes: { [railwaySandboxProvider.id]: railwaySandboxProvider },
})
See the Sandbox provider reference for details on registering custom sandbox providers.