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 retain their files until deletion. They run continuously by default and can optionally sleep when idle and wake on web requests.

const workspace = await ws.create({
  image: 'oblien/node:24',
  mode: 'permanent',
});

Characteristics:

  • restart_policy is configurable (default "always")
  • No expiry by default; an optional idle policy hibernates the VM
  • No automatic removal
  • 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.

To save host memory while keeping app state, use ws.lifecycle.setIdle(id, { suspend_after: '15m' }). The normal idle timer uses a memory snapshot and wakes on traffic to exposed ports or connected custom domains. Switching modes clears this policy. See Sleep and wake on request.

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: 'oblien/python:3.13',
  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: 'oblien/node:24',
  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: 'oblien/python:3.13',
  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.