Concepts

Modes

Every workspace runs in one of two modes: permanent or temporary. The mode determines which lifecycle fields are available.

Permanent mode

Permanent workspaces are designed to run continuously. They have no time limit and no auto-removal.

const workspace = await ws.create({
  image: 'node-20',
  mode: 'permanent',
});

Characteristics:

  • restart_policy is configurable (default "always")
  • No TTL — runs until you explicitly stop or delete
  • No ttl_action, no remove_on_exit
  • Static internal IP assigned
  • Survives host maintenance

Best for: Production services, long-running daemons, development environments, persistent sandboxes.

Convert to permanent

// Default: restart_policy = 'always'
await ws.lifecycle.makePermanent('ws_a1b2c3d4');

// Or specify a restart policy
await ws.lifecycle.makePermanent('ws_a1b2c3d4', {
  restart_policy: 'on-failure',
});

This clears any TTL, ttl_action, and remove_on_exit, and sets the restart policy. The VM is not started automatically — use start() separately if needed.

Temporary mode

Temporary workspaces auto-expire after a configurable time to live (TTL). They're designed for short-lived tasks.

const workspace = await ws.create({
  image: 'python-3.12',
  mode: 'temporary',
  config: {
    ttl: '1h',
    ttl_action: 'stop',
  },
});

Characteristics:

  • TTL is required — auto-expires when it elapses
  • Configurable action on expiry: "stop", "pause", or "remove"
  • Optional remove_on_exit to auto-delete when VM exits
  • Restart policy is locked to "no" (the Go VM server enforces this for any workspace with TTL > 0)
  • Dynamic internal IP

Best for: CI/CD jobs, code execution sandboxes, time-limited test environments, one-off tasks.

TTL format

TTL accepts a duration string or seconds as a number:

FormatDuration
"5m"5 minutes
"30m"30 minutes
"1h"1 hour
"6h"6 hours
"24h"24 hours
"7d"7 days
36001 hour (seconds as number)

TTL actions

ActionBehavior
"stop"VM is stopped when TTL expires — workspace can be restarted later
"pause"VM is paused (frozen in memory) when TTL expires — can be resumed
"remove"VM is stopped and workspace is deleted when TTL expires

Keep-alive (ping)

Extend the TTL while a user is active:

// Renew the TTL
await ws.lifecycle.ping('ws_a1b2c3d4');

// Renew with a specific duration
await ws.lifecycle.ping('ws_a1b2c3d4', { ttl_seconds: 3600 });

Convert to temporary

await ws.lifecycle.makeTemporary('ws_a1b2c3d4', {
  ttl: '2h',
  ttl_action: 'stop',
});

Restart policy is automatically set to "no" — the Go VM server enforces that any workspace with active TTL cannot auto-restart.

Restart policies

Control what happens when the VM process exits:

PolicyBehavior
"no"Don't restart (default for temporary)
"always"Always restart (default for permanent)
"on-failure"Restart only on non-zero exit code
"unless-stopped"Restart unless manually stopped
await ws.create({
  image: 'node-20',
  config: {
    restart_policy: 'on-failure',
    max_restarts: 5,
  },
});

max_restarts limits the number of automatic restart attempts. Set to 0 for unlimited restarts.

Restart policy and temporary mode

The Go VM server enforces a strict rule: TTL > 0 → restart_policy = "no". This means temporary workspaces cannot have an auto-restart policy. The API does not accept restart_policy for temporary mode endpoints (makeTemporary, updateTTL) — it is always locked to "no" server-side.

If you need crash recovery, use permanent mode with restart_policy: 'on-failure' instead.

Auto-delete on exit

The remove_on_exit flag automatically deletes the workspace when the VM exits for any reason. Only available in temporary mode.

await ws.create({
  image: 'python-3.12',
  mode: 'temporary',
  config: {
    ttl: '5m',
    remove_on_exit: true,
    cmd: ['python3', 'script.py'],
  },
});
// Workspace auto-deletes after script.py finishes or TTL expires

This is ideal for fire-and-forget execution where you don't need the workspace afterward.

Comparison

FeaturePermanentTemporary
TTLNoneRequired
ttl_actionN/A"stop" | "pause" | "remove"
remove_on_exitN/AOptional
Restart policyAny (default "always")Locked to "no"
Static IPYesNo
Auto-expiryNoYes
Best forServices, dev envsJobs, sandboxes

Switching modes

You can switch between modes at any time:

// Permanent → Temporary
await ws.lifecycle.makeTemporary(id, { ttl: '1h', ttl_action: 'stop' });

// Temporary → Permanent
await ws.lifecycle.makePermanent(id);

// Temporary → Permanent with specific restart policy
await ws.lifecycle.makePermanent(id, { restart_policy: 'on-failure' });

// Update TTL without changing mode
await ws.lifecycle.updateTtl(id, { ttl: '2h', ttl_action: 'pause' });

Mode changes are config-only and take effect immediately. All lifecycle changes are applied hot — the Go orchestrator picks them up without restart. Use start()/stop() separately for power control.