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_policyis configurable (default"always")- No TTL — runs until you explicitly stop or delete
- No
ttl_action, noremove_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_exitto 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:
| Format | Duration |
|---|---|
"5m" | 5 minutes |
"30m" | 30 minutes |
"1h" | 1 hour |
"6h" | 6 hours |
"24h" | 24 hours |
"7d" | 7 days |
3600 | 1 hour (seconds as number) |
TTL actions
| Action | Behavior |
|---|---|
"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:
| Policy | Behavior |
|---|---|
"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 expiresThis is ideal for fire-and-forget execution where you don't need the workspace afterward.
Comparison
| Feature | Permanent | Temporary |
|---|---|---|
| TTL | None | Required |
ttl_action | N/A | "stop" | "pause" | "remove" |
remove_on_exit | N/A | Optional |
| Restart policy | Any (default "always") | Locked to "no" |
| Static IP | Yes | No |
| Auto-expiry | No | Yes |
| Best for | Services, dev envs | Jobs, 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.