> ## Documentation Index
> Fetch the complete documentation index at: https://docs.celesto.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# VM lifecycle management

> Manage the full SmolVM sandbox lifecycle — created, running, paused, and stopped states — and learn when to use the context manager versus manual control.

Every SmolVM sandbox follows a simple lifecycle: you create it, start it, run your work, and tear it down. Most of the time the context manager (`with SmolVM() as vm`) handles all of this for you. This guide covers the full lifecycle for cases where you need more control.

## Lifecycle states

A sandbox progresses through these states:

* `CREATED` - VM configured but not started
* `RUNNING` - VM is booted and operational
* `PAUSED` - VM execution is suspended (Firecracker only)
* `STOPPED` - VM has been gracefully shut down
* `ERROR` - VM encountered a fatal error

## Creating a sandbox

<Steps>
  <Step title="Import SmolVM">
    ```python theme={null} theme={null}
    from smolvm import SmolVM
    ```
  </Step>

  <Step title="Create VM instance">
    ```python theme={null} theme={null}
    # Auto-configuration mode
    vm = SmolVM()
    print(f"Created VM: {vm.vm_id}")
    print(f"Status: {vm.status}")  # VMState.CREATED
    ```
  </Step>

  <Step title="Verify VM is created">
    The VM is now registered in SmolVM's state database but not yet running.
  </Step>
</Steps>

## Starting a sandbox

Start the sandbox to boot the guest operating system:

```python theme={null} theme={null}
from smolvm import SmolVM

vm = SmolVM()
vm.start(boot_timeout=30.0)
print(f"VM started: {vm.status}")  # VMState.RUNNING
print(f"IP address: {vm.get_ip()}")
```

### Boot process

When you call `start()`, SmolVM:

1. Launches the Firecracker/QEMU process
2. Boots the kernel with configured boot arguments
3. Waits for the VM to become responsive
4. Injects environment variables if configured (see `env_vars` in VMConfig)

### Method signature

```python theme={null} theme={null}
def start(self, boot_timeout: float = 30.0) -> SmolVM:
    """Start the VM.

    Args:
        boot_timeout: Maximum seconds to wait for boot.

    Returns:
        self for method chaining.
    """
```

<Note>
  If a sandbox is already running, calling `start()` is a no-op and returns immediately.
</Note>

## Stopping a sandbox

Gracefully shut down a running sandbox:

```python theme={null} theme={null}
vm.stop(timeout=3.0)
print(f"VM stopped: {vm.status}")  # VMState.STOPPED
```

The `stop()` method:

* Sends a shutdown signal to the guest
* Waits up to `timeout` seconds for graceful shutdown
* Cleans up port forwarding rules
* Closes SSH connections

### Method signature

```python theme={null} theme={null}
def stop(self, timeout: float = 3.0) -> SmolVM:
    """Stop the VM.

    Args:
        timeout: Seconds to wait for graceful shutdown.

    Returns:
        self for method chaining.
    """
```

## Pausing and resuming a VM

On the Firecracker backend, you can pause a running VM and resume it later. This suspends execution without stopping the VM process, so the guest resumes exactly where it left off.

```python theme={null} theme={null}
vm.pause()
print(f"Status: {vm.status}")  # VMState.PAUSED

# Later, resume execution
vm.resume()
print(f"Status: {vm.status}")  # VMState.RUNNING
```

<Note>
  Pausing cleans up port forwarding rules and closes SSH connections. After resuming, the next `run()` call re-establishes SSH automatically.
</Note>

<Note>
  If you call `start()` on a paused VM, it automatically calls `resume()` instead.
</Note>

## Deleting a sandbox

Permanently delete a sandbox and release all resources:

```python theme={null} theme={null}
vm.delete()
# VM is now removed from state database
# All network resources are cleaned up
```

<Warning>
  Deletion is permanent. The VM cannot be recovered after deletion.
</Warning>

## Context manager pattern

The recommended way to manage the lifecycle is with Python's `with` statement:

```python theme={null} theme={null}
from smolvm import SmolVM

with SmolVM() as vm:
    # VM automatically starts on context entry
    print(f"VM running: {vm.vm_id}")
    result = vm.run("hostname")
    print(result.output)
    # VM automatically stops and deletes on context exit
```

### Context manager behavior

<Tabs>
  <Tab title="New VMs (owns_vm=True)">
    When you create a VM with `SmolVM(config)` or `SmolVM()`:

    * `__enter__`: Automatically starts the VM
    * `__exit__`: Stops AND deletes the VM

    ```python theme={null} theme={null}
    # VM is created, started, and cleaned up automatically
    with SmolVM() as vm:
        vm.run("echo 'hello'")
    # VM is now deleted
    ```
  </Tab>

  <Tab title="Reconnected VMs (owns_vm=False)">
    When you reconnect with `SmolVM.from_id(vm_id)`:

    * `__enter__`: Does NOT start the VM (preserves state)
    * `__exit__`: Stops but does NOT delete

    ```python theme={null} theme={null}
    # Reconnect to existing VM
    with SmolVM.from_id("my-vm") as vm:
        vm.run("echo 'hello'")
    # VM is stopped but still exists
    ```
  </Tab>
</Tabs>

## Reconnecting to existing sandboxes

Reconnect to a sandbox that was created earlier:

```python theme={null} theme={null}
from smolvm import SmolVM

# Create a VM without context manager
vm = SmolVM()
vm.start()
vm_id = vm.vm_id
vm.close()  # Release resources but don't delete

# Later, reconnect to the same VM
vm2 = SmolVM.from_id(vm_id)
print(f"Reconnected to {vm2.vm_id}")
print(f"Status: {vm2.status}")  # VMState.RUNNING

# Continue using the VM
result = vm2.run("uptime")
print(result.output)
```

### Class method signature

```python theme={null} theme={null}
@classmethod
def from_id(
    cls,
    vm_id: str,
    *,
    data_dir: Path | None = None,
    socket_dir: Path | None = None,
    backend: str | None = None,
    ssh_user: str = "root",
    ssh_key_path: str | None = None,
) -> SmolVM:
    """Reconnect to an existing VM by ID."""
```

## Manual lifecycle management

For advanced use cases where you need explicit control:

```python theme={null} theme={null}
from smolvm import SmolVM

try:
    # Create but don't start
    vm = SmolVM()
    print(f"VM created: {vm.vm_id} (status: {vm.status})")

    # Manually start when ready
    vm.start()
    print(f"VM started (status: {vm.status})")

    # Do work
    result = vm.run("cat /etc/os-release")
    print(result.output)

    # Manually stop
    vm.stop()
    print(f"VM stopped (status: {vm.status})")

finally:
    # Clean up resources
    vm.delete()
    print("VM deleted")
```

## Long-running sandboxes

For persistent sandboxes that survive across Python sessions:

<Steps>
  <Step title="Create a VM with a stable ID">
    ```python theme={null} theme={null}
    from smolvm import SmolVM, VMConfig
    from smolvm.build import ImageBuilder, SSH_BOOT_ARGS

    builder = ImageBuilder()
    kernel, rootfs = builder.build_alpine_ssh()

    config = VMConfig(
        vm_id="persistent-agent-vm",  # Stable, reusable ID
        vcpu_count=2,
        memory=2048,
        kernel_path=kernel,
        rootfs_path=rootfs,
        boot_args=SSH_BOOT_ARGS,
    )

    vm = SmolVM(config)
    vm.start()
    print(f"Created persistent VM: {vm.vm_id}")
    vm.close()  # Release SDK resources, keep VM running
    ```
  </Step>

  <Step title="Reconnect in another session">
    ```python theme={null} theme={null}
    from smolvm import SmolVM

    # In a different Python session or script
    vm = SmolVM.from_id("persistent-agent-vm")
    print(f"Reconnected to {vm.vm_id}")
    print(f"Status: {vm.status}")

    result = vm.run("uptime")
    print(result.output)
    ```
  </Step>

  <Step title="Clean up when done">
    ```python theme={null} theme={null}
    vm = SmolVM.from_id("persistent-agent-vm")
    vm.stop()
    vm.delete()
    print("VM permanently deleted")
    ```
  </Step>
</Steps>

## Waiting for SSH

You can explicitly wait for SSH to become available:

```python theme={null} theme={null}
vm = SmolVM()
vm.start()

# Wait up to 60 seconds for SSH
vm.wait_for_ssh(timeout=60.0)
print("SSH is ready")

# Now run commands
result = vm.run("whoami")
print(result.output)
```

<Note>
  You typically don't need to call `wait_for_ssh()` explicitly. The `run()` method automatically waits for SSH on first use.
</Note>

## Refreshing state

Properties like `status` and `info` are cached. Refresh them from the state store:

```python theme={null} theme={null}
vm = SmolVM.from_id("my-vm")
print(vm.status)  # Cached value

vm.refresh()
print(vm.status)  # Fresh value from database
```

## Snapshots

You can save the full state of a running VM and restore it later using snapshots. This is useful for checkpointing before risky operations or reusing a configured environment.

```python theme={null} theme={null}
# Create a snapshot
snapshot = vm.snapshot(snapshot_id="my-checkpoint")

# Later, restore it
restored_vm = SmolVM.from_snapshot("my-checkpoint", resume_vm=True)
```

See the [Snapshots guide](/smolvm/features/snapshots) for full details.

## Next steps

<CardGroup cols={2}>
  <Card title="Snapshots" icon="camera" href="/smolvm/features/snapshots">
    Save and restore sandbox state
  </Card>

  <Card title="Port forwarding" icon="network-wired" href="/smolvm/features/port-forwarding">
    Expose services running inside sandboxes to your host
  </Card>

  <Card title="Environment variables" icon="code" href="/smolvm/guides/environment-variables">
    Configure sandbox environment dynamically
  </Card>

  <Card title="AI agent integration" icon="robot" href="/smolvm/guides/ai-agent-integration">
    Build secure AI agent sandboxes
  </Card>
</CardGroup>
