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_policyis 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_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: '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 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.