Koyeb Sandbox - Interactive execution environment for running arbitrary code on Koyeb
Command execution utilities for Koyeb Sandbox instances Using SandboxClient HTTP API
class CommandStatus(str, Enum)Command execution status
@dataclass
class CommandResult()Result of a command execution using Koyeb API models
@property
def success() -> boolCheck if command executed successfully
@property
def output() -> strGet combined stdout and stderr output
class SandboxCommandError(SandboxError)Raised when command execution fails
class SandboxExecutor()Synchronous command execution interface for Koyeb Sandbox instances. Bound to a specific sandbox instance.
For async usage, use AsyncSandboxExecutor instead.
def __call__(
command: str,
cwd: Optional[str] = None,
env: Optional[Dict[str, str]] = None,
timeout: int = 30,
on_stdout: Optional[Callable[[str], None]] = None,
on_stderr: Optional[Callable[[str], None]] = None) -> CommandResultExecute a command in a shell synchronously. Supports streaming output via callbacks.
Arguments:
command- Command to execute as a string (e.g., "python -c 'print(2+2)'")cwd- Working directory for the commandenv- Environment variables for the commandtimeout- Command timeout in seconds (enforced for HTTP requests)on_stdout- Optional callback for streaming stdout chunkson_stderr- Optional callback for streaming stderr chunks
Returns:
CommandResult- Result of the command execution
Example:
```python
# Synchronous execution
result = sandbox.exec("echo hello")
# With streaming callbacks
result = sandbox.exec(
"echo hello; sleep 1; echo world",
on_stdout=lambda data: print(f"OUT: {data}"),
on_stderr=lambda data: print(f"ERR: {data}"),
)
```
class AsyncSandboxExecutor(SandboxExecutor)Async command execution interface for Koyeb Sandbox instances. Bound to a specific sandbox instance.
Inherits from SandboxExecutor and provides async command execution.
async def __call__(
command: str,
cwd: Optional[str] = None,
env: Optional[Dict[str, str]] = None,
timeout: int = 30,
on_stdout: Optional[Callable[[str], None]] = None,
on_stderr: Optional[Callable[[str], None]] = None) -> CommandResultExecute a command in a shell asynchronously. Supports streaming output via callbacks.
Arguments:
command- Command to execute as a string (e.g., "python -c 'print(2+2)'")cwd- Working directory for the commandenv- Environment variables for the commandtimeout- Command timeout in seconds (enforced for HTTP requests)on_stdout- Optional callback for streaming stdout chunkson_stderr- Optional callback for streaming stderr chunks
Returns:
CommandResult- Result of the command execution
Example:
```python
# Async execution
result = await sandbox.exec("echo hello")
# With streaming callbacks
result = await sandbox.exec(
"echo hello; sleep 1; echo world",
on_stdout=lambda data: print(f"OUT: {data}"),
on_stderr=lambda data: print(f"ERR: {data}"),
)
```
Filesystem operations for Koyeb Sandbox instances Using SandboxClient HTTP API
class SandboxFilesystemError(SandboxError)Base exception for filesystem operations
class SandboxFileNotFoundError(SandboxFilesystemError)Raised when file or directory not found
class SandboxFileExistsError(SandboxFilesystemError)Raised when file already exists
@dataclass
class FileInfo()File information
class SandboxFilesystem()Synchronous filesystem operations for Koyeb Sandbox instances. Using SandboxClient HTTP API.
For async usage, use AsyncSandboxFilesystem instead.
def write_file(path: str,
content: Union[str, bytes],
encoding: str = "utf-8") -> NoneWrite content to a file synchronously.
Arguments:
path- Absolute path to the filecontent- Content to write (string or bytes)encoding- File encoding (default: "utf-8"). Use "base64" for binary data.
def read_file(path: str, encoding: str = "utf-8") -> FileInfoRead a file from the sandbox synchronously.
Arguments:
path- Absolute path to the fileencoding- File encoding (default: "utf-8"). Use "base64" for binary data, which will decode the base64 content and return bytes.
Returns:
FileInfo- Object with content (str or bytes if base64) and encoding
def mkdir(path: str) -> NoneCreate a directory synchronously.
Note: Parent directories are always created automatically by the API.
Arguments:
path- Absolute path to the directory
def list_dir(path: str = ".") -> List[str]List contents of a directory synchronously.
Arguments:
path- Path to the directory (default: current directory)
Returns:
List[str]- Names of files and directories within the specified path.
def delete_file(path: str) -> NoneDelete a file synchronously.
Arguments:
path- Absolute path to the file
def delete_dir(path: str) -> NoneDelete a directory synchronously.
Arguments:
path- Absolute path to the directory
def rename_file(old_path: str, new_path: str) -> NoneRename a file synchronously.
Arguments:
old_path- Current file pathnew_path- New file path
def move_file(source_path: str, destination_path: str) -> NoneMove a file to a different directory synchronously.
Arguments:
source_path- Current file pathdestination_path- Destination path
def write_files(files: List[Dict[str, str]]) -> NoneWrite multiple files in a single operation synchronously.
Arguments:
files- List of dictionaries, each with 'path', 'content', and optional 'encoding'.
def exists(path: str) -> boolCheck if file/directory exists synchronously
def is_file(path: str) -> boolCheck if path is a file synchronously
def is_dir(path: str) -> boolCheck if path is a directory synchronously
def upload_file(local_path: str,
remote_path: str,
encoding: str = "utf-8") -> NoneUpload a local file to the sandbox synchronously.
Arguments:
local_path- Path to the local fileremote_path- Destination path in the sandboxencoding- File encoding (default: "utf-8"). Use "base64" for binary files.
Raises:
SandboxFileNotFoundError- If local file doesn't existUnicodeDecodeError- If file cannot be decoded with specified encoding
def download_file(remote_path: str,
local_path: str,
encoding: str = "utf-8") -> NoneDownload a file from the sandbox to a local path synchronously.
Arguments:
remote_path- Path to the file in the sandboxlocal_path- Destination path on the local filesystemencoding- File encoding (default: "utf-8"). Use "base64" for binary files.
Raises:
SandboxFileNotFoundError- If remote file doesn't exist
def ls(path: str = ".") -> List[str]List directory contents synchronously.
Arguments:
path- Path to list
Returns:
List of file/directory names
def rm(path: str, recursive: bool = False) -> NoneRemove file or directory synchronously.
Arguments:
path- Path to removerecursive- Remove recursively
def open(path: str, mode: str = "r", encoding: str = "utf-8") -> SandboxFileIOOpen a file in the sandbox synchronously.
Arguments:
path- Path to the filemode- Open mode ('r', 'w', 'a', etc.)encoding- File encoding (default: "utf-8"). Use "base64" for binary data.
Returns:
SandboxFileIO- File handle
class AsyncSandboxFilesystem(SandboxFilesystem)Async filesystem operations for Koyeb Sandbox instances. Inherits from SandboxFilesystem and provides async methods.
@async_wrapper("write_file")
async def write_file(path: str,
content: Union[str, bytes],
encoding: str = "utf-8") -> NoneWrite content to a file asynchronously.
Arguments:
path- Absolute path to the filecontent- Content to write (string or bytes)encoding- File encoding (default: "utf-8"). Use "base64" for binary data.
@async_wrapper("read_file")
async def read_file(path: str, encoding: str = "utf-8") -> FileInfoRead a file from the sandbox asynchronously.
Arguments:
path- Absolute path to the fileencoding- File encoding (default: "utf-8"). Use "base64" for binary data, which will decode the base64 content and return bytes.
Returns:
FileInfo- Object with content (str or bytes if base64) and encoding
@async_wrapper("mkdir")
async def mkdir(path: str) -> NoneCreate a directory asynchronously.
Note: Parent directories are always created automatically by the API.
Arguments:
path- Absolute path to the directory
@async_wrapper("list_dir")
async def list_dir(path: str = ".") -> List[str]List contents of a directory asynchronously.
Arguments:
path- Path to the directory (default: current directory)
Returns:
List[str]- Names of files and directories within the specified path.
@async_wrapper("delete_file")
async def delete_file(path: str) -> NoneDelete a file asynchronously.
Arguments:
path- Absolute path to the file
@async_wrapper("delete_dir")
async def delete_dir(path: str) -> NoneDelete a directory asynchronously.
Arguments:
path- Absolute path to the directory
@async_wrapper("rename_file")
async def rename_file(old_path: str, new_path: str) -> NoneRename a file asynchronously.
Arguments:
old_path- Current file pathnew_path- New file path
@async_wrapper("move_file")
async def move_file(source_path: str, destination_path: str) -> NoneMove a file to a different directory asynchronously.
Arguments:
source_path- Current file pathdestination_path- Destination path
async def write_files(files: List[Dict[str, str]]) -> NoneWrite multiple files in a single operation asynchronously.
Arguments:
files- List of dictionaries, each with 'path', 'content', and optional 'encoding'.
@async_wrapper("exists")
async def exists(path: str) -> boolCheck if file/directory exists asynchronously
@async_wrapper("is_file")
async def is_file(path: str) -> boolCheck if path is a file asynchronously
@async_wrapper("is_dir")
async def is_dir(path: str) -> boolCheck if path is a directory asynchronously
@async_wrapper("upload_file")
async def upload_file(local_path: str,
remote_path: str,
encoding: str = "utf-8") -> NoneUpload a local file to the sandbox asynchronously.
Arguments:
local_path- Path to the local fileremote_path- Destination path in the sandboxencoding- File encoding (default: "utf-8"). Use "base64" for binary files.
@async_wrapper("download_file")
async def download_file(remote_path: str,
local_path: str,
encoding: str = "utf-8") -> NoneDownload a file from the sandbox to a local path asynchronously.
Arguments:
remote_path- Path to the file in the sandboxlocal_path- Destination path on the local filesystemencoding- File encoding (default: "utf-8"). Use "base64" for binary files.
async def ls(path: str = ".") -> List[str]List directory contents asynchronously.
Arguments:
path- Path to list
Returns:
List of file/directory names
@async_wrapper("rm")
async def rm(path: str, recursive: bool = False) -> NoneRemove file or directory asynchronously.
Arguments:
path- Path to removerecursive- Remove recursively
def open(path: str,
mode: str = "r",
encoding: str = "utf-8") -> AsyncSandboxFileIOOpen a file in the sandbox asynchronously.
Arguments:
path- Path to the filemode- Open mode ('r', 'w', 'a', etc.)encoding- File encoding (default: "utf-8"). Use "base64" for binary data.
Returns:
AsyncSandboxFileIO- Async file handle
class SandboxFileIO()Synchronous file I/O handle for sandbox files
def read() -> Union[str, bytes]Read file content synchronously
def write(content: Union[str, bytes]) -> NoneWrite content to file synchronously
def close() -> NoneClose the file
class AsyncSandboxFileIO()Async file I/O handle for sandbox files
async def read() -> Union[str, bytes]Read file content asynchronously
async def write(content: Union[str, bytes]) -> NoneWrite content to file asynchronously
def close() -> NoneClose the file
Koyeb Sandbox - Python SDK for creating and managing Koyeb sandboxes
@dataclass
class ProcessInfo()Type definition for process information returned by list_processes.
Process ID (UUID string)
The command that was executed
Process status (e.g., "running", "completed")
OS process ID (if running)
Exit code (if completed)
ISO 8601 timestamp when process started
@dataclass
class ExposedPort()Result of exposing a port via TCP proxy.
class Sandbox()Synchronous sandbox for running code on Koyeb infrastructure. Provides creation and deletion functionality with proper health polling.
@property
def id() -> strGet the service ID of the sandbox.
@classmethod
def create(cls,
image: str = "koyeb/sandbox",
name: str = "quick-sandbox",
wait_ready: bool = True,
instance_type: str = "micro",
exposed_port_protocol: Optional[str] = None,
env: Optional[Dict[str, str]] = None,
region: Optional[str] = None,
api_token: Optional[str] = None,
timeout: int = 300,
idle_timeout: int = 300,
enable_tcp_proxy: bool = False,
privileged: bool = False,
registry_secret: Optional[str] = None,
_experimental_enable_light_sleep: bool = False,
_experimental_deep_sleep_value: int = 3900,
delete_after_delay: int = 0,
delete_after_inactivity_delay: int = 0,
app_id: Optional[str] = None,
enable_mesh: bool = None,
poll_interval: float = DEFAULT_POLL_INTERVAL) -> SandboxCreate a new sandbox instance.
Arguments:
image- Docker image to use (default: koyeb/sandbox)name- Name of the sandboxwait_ready- Wait for sandbox to be ready (default: True)instance_type- Instance type (default: micro)exposed_port_protocol- Protocol to expose ports with ("http" or "http2"). If None, defaults to "http". If provided, must be one of "http" or "http2".env- Environment variablesregion- Region to deploy to. Defaults to KOYEB_REGION env var, or "na" if not set.api_token- Koyeb API token (if None, will try to get from KOYEB_API_TOKEN env var)timeout- Timeout for sandbox creation in secondsidle_timeout- Sleep timeout in seconds. Behavior depends on _experimental_enable_light_sleep:- If _experimental_enable_light_sleep is True: sets light_sleep value (deep_sleep=3900)
- If _experimental_enable_light_sleep is False: sets deep_sleep value
- If 0: disables scale-to-zero (keep always-on)
- If None: uses default values
enable_tcp_proxy- If True, enables TCP proxy for direct TCP access to port 3031privileged- If True, run the container in privileged mode (default: False)registry_secret- Name of a Koyeb secret containing registry credentials for pulling private images. Create the secret via Koyeb dashboard or CLI first._experimental_enable_light_sleep- If True, uses idle_timeout for light_sleep and sets deep_sleep=3900. If False, uses idle_timeout for deep_sleep (default: False)delete_after_create- If >0, automatically delete the sandbox if there was no activity after this many seconds since creation.delete_after_sleep- If >0, automatically delete the sandbox if service sleeps due to inactivity after this many seconds.app_id- If provided, create the sandbox service in an existing app instead of creating a new one.enable_mesh- Enable or disable mesh for this sandbox. Disabled by defaultpoll_interval- Time between health checks in seconds when wait_ready is True (default: 0.5)
Returns:
Sandbox- A new Sandbox instance
Raises:
ValueError- If API token is not providedSandboxTimeoutError- If wait_ready is True and sandbox does not become ready within timeout
Example:
sandbox = Sandbox.create()
sandbox = Sandbox.create( ... image="ghcr.io/myorg/myimage:latest", ... registry_secret="my-ghcr-secret" ... )
@classmethod
def get_from_id(cls, id: str, api_token: Optional[str] = None) -> "Sandbox"Get a sandbox by service ID.
Arguments:
id- Service ID of the sandboxapi_token- Koyeb API token (if None, will try to get from KOYEB_API_TOKEN env var)
Returns:
Sandbox- The Sandbox instance
Raises:
ValueError- If API token is not provided or id is invalidSandboxError- If sandbox is not found or retrieval fails
def wait_ready(timeout: int = DEFAULT_INSTANCE_WAIT_TIMEOUT,
poll_interval: Optional[float] = None) -> boolWait for sandbox to become ready with exponential backoff polling.
Starts polling at 0.1s intervals, doubling each time up to poll_interval.
Arguments:
timeout- Maximum time to wait in secondspoll_interval- Maximum time between health checks in seconds (defaults to instance poll_interval)
Returns:
bool- True if sandbox became ready, False if timeout
def wait_tcp_proxy_ready(timeout: int = DEFAULT_INSTANCE_WAIT_TIMEOUT,
poll_interval: Optional[float] = None) -> boolWait for TCP proxy to become ready and available.
Polls the deployment metadata with exponential backoff until the TCP proxy information is available. Starts at 0.1s intervals, doubling up to poll_interval.
Arguments:
timeout- Maximum time to wait in secondspoll_interval- Maximum time between checks in seconds (defaults to instance poll_interval)
Returns:
bool- True if TCP proxy became ready, False if timeout
def delete() -> NoneDelete the sandbox instance.
def get_domain() -> Optional[str]Get the public domain of the sandbox.
Returns the domain (e.g., "app-name-org.koyeb.app/r/routing_key/" or "app-name-org.koyeb.app") without protocol. To get the full URL with protocol, use sandbox._get_url()
Returns:
Optional[str]- The domain or None if unavailable
def get_tcp_proxy_info() -> Optional[tuple[str, int]]Get the TCP proxy host and port for the sandbox.
Returns the TCP proxy host and port as a tuple (host, port) for direct TCP access to port 3031. This is only available if enable_tcp_proxy=True was set when creating the sandbox.
Returns:
Optional[tuple[str, int]]: A tuple of (host, port) or None if unavailable
def is_healthy() -> boolCheck if sandbox is healthy and ready for operations
@property
def filesystem() -> "SandboxFilesystem"Get filesystem operations interface
@property
def exec() -> "SandboxExecutor"Get command execution interface
def expose_port(port: int) -> ExposedPortExpose a port to external connections via TCP proxy.
Binds the specified internal port to the TCP proxy, allowing external connections to reach services running on that port inside the sandbox. Automatically unbinds any existing port before binding the new one.
Arguments:
port- The internal port number to expose (must be a valid port number between 1 and 65535)
Returns:
ExposedPort- An object withportandexposed_atattributes:- port: The exposed port number
- exposed_at: The full URL with https:// protocol (e.g., "https://app-name-org.koyeb.app")
Raises:
ValueError- If port is not in valid range [1, 65535]SandboxError- If the port binding operation fails
Notes:
- Only one port can be exposed at a time
- Any existing port binding is automatically unbound before binding the new port
- The port must be available and accessible within the sandbox environment
- The TCP proxy is accessed via get_tcp_proxy_info() which returns (host, port)
Example:
result = sandbox.expose_port(8080) result.port 8080 result.exposed_at 'https://app-name-org.koyeb.app'
def unexpose_port() -> NoneUnexpose a port from external connections.
Removes the TCP proxy port binding, stopping traffic forwarding to the previously bound port.
Raises:
SandboxError- If the port unbinding operation fails
Notes:
- After unexposing, the TCP proxy will no longer forward traffic
- Safe to call even if no port is currently bound
def launch_process(cmd: str,
cwd: Optional[str] = None,
env: Optional[Dict[str, str]] = None) -> strLaunch a background process in the sandbox.
Starts a long-running background process that continues executing even after the method returns. Use this for servers, workers, or other long-running tasks.
Arguments:
cmd- The shell command to execute as a background processcwd- Optional working directory for the processenv- Optional environment variables to set/override for the process
Returns:
str- The unique process ID (UUID string) that can be used to manage the process
Raises:
SandboxError- If the process launch fails
Example:
process_id = sandbox.launch_process("python -u server.py") print(f"Started process: {process_id}")
def kill_process(process_id: str) -> NoneKill a background process by its ID.
Terminates a running background process. This sends a SIGTERM signal to the process, allowing it to clean up gracefully. If the process doesn't terminate within a timeout, it will be forcefully killed with SIGKILL.
Arguments:
process_id- The unique process ID (UUID string) to kill
Raises:
SandboxError- If the process kill operation fails
Example:
sandbox.kill_process("550e8400-e29b-41d4-a716-446655440000")
def list_processes() -> List[ProcessInfo]List all background processes.
Returns information about all currently running and recently completed background processes. This includes both active processes and processes that have completed (which remain in memory until server restart).
Returns:
List[ProcessInfo]- List of process objects, each containing:- id: Process ID (UUID string)
- command: The command that was executed
- status: Process status (e.g., "running", "completed")
- pid: OS process ID (if running)
- exit_code: Exit code (if completed)
- started_at: ISO 8601 timestamp when process started
- completed_at: ISO 8601 timestamp when process completed (if applicable)
Raises:
SandboxError- If listing processes fails
Example:
processes = sandbox.list_processes() for process in processes: ... print(f"{process.id}: {process.command} - {process.status}")
def kill_all_processes() -> intKill all running background processes.
Convenience method that lists all processes and kills them all. This is useful for cleanup operations.
Returns:
int- The number of processes that were killed
Raises:
SandboxError- If listing or killing processes fails
Example:
count = sandbox.kill_all_processes() print(f"Killed {count} processes")
def update_lifecycle(delete_after_delay: Optional[int] = None,
delete_after_inactivity: Optional[int] = None) -> NoneUpdate the sandbox's life cycle settings.
Arguments:
delete_after_delay- If >0, automatically delete the sandbox if there was no activity after this many seconds since creation.delete_after_inactivity- If >0, automatically delete the sandbox if service sleeps due to inactivity after this many seconds.
Raises:
SandboxError- If updating life cycle fails
Example:
sandbox.update_life_cycle(delete_after_delay=600, delete_after_inactivity=300)
def __enter__() -> "Sandbox"Context manager entry - returns self.
def __exit__(exc_type, exc_val, exc_tb) -> NoneContext manager exit - automatically deletes the sandbox.
class AsyncSandbox(Sandbox)Async sandbox for running code on Koyeb infrastructure. Inherits from Sandbox and provides async wrappers for all operations.
@classmethod
async def get_from_id(cls,
id: str,
api_token: Optional[str] = None) -> "AsyncSandbox"Get a sandbox by service ID asynchronously.
Arguments:
id- Service ID of the sandboxapi_token- Koyeb API token (if None, will try to get from KOYEB_API_TOKEN env var)
Returns:
AsyncSandbox- The AsyncSandbox instance
Raises:
ValueError- If API token is not provided or id is invalidSandboxError- If sandbox is not found or retrieval fails
@classmethod
async def create(cls,
image: str = "koyeb/sandbox",
name: str = "quick-sandbox",
wait_ready: bool = True,
instance_type: str = "micro",
exposed_port_protocol: Optional[str] = None,
env: Optional[Dict[str, str]] = None,
region: Optional[str] = None,
api_token: Optional[str] = None,
timeout: int = 300,
idle_timeout: int = 0,
enable_tcp_proxy: bool = False,
privileged: bool = False,
registry_secret: Optional[str] = None,
_experimental_enable_light_sleep: bool = False,
_experimental_deep_sleep_value: int = 3900,
delete_after_delay: int = 0,
delete_after_inactivity_delay: int = 0,
app_id: Optional[str] = None,
enable_mesh: bool = False,
poll_interval: float = DEFAULT_POLL_INTERVAL) -> AsyncSandboxCreate a new sandbox instance with async support.
Arguments:
image- Docker image to use (default: koyeb/sandbox)name- Name of the sandboxwait_ready- Wait for sandbox to be ready (default: True)instance_type- Instance type (default: micro)exposed_port_protocol- Protocol to expose ports with ("http" or "http2"). If None, defaults to "http". If provided, must be one of "http" or "http2".env- Environment variablesregion- Region to deploy to. Defaults to KOYEB_REGION env var, or "na" if not set.api_token- Koyeb API token (if None, will try to get from KOYEB_API_TOKEN env var)timeout- Timeout for sandbox creation in secondsidle_timeout- Sleep timeout in seconds. Behavior depends on _experimental_enable_light_sleep:- If _experimental_enable_light_sleep is True: sets light_sleep value (deep_sleep uses _experimental_deep_sleep_value)
- If _experimental_enable_light_sleep is False: sets deep_sleep value
- If 0: disables scale-to-zero (keep always-on)
- If None: uses default values
enable_tcp_proxy- If True, enables TCP proxy for direct TCP access to port 3031privileged- If True, run the container in privileged mode (default: False)registry_secret- Name of a Koyeb secret containing registry credentials for pulling private images. Create the secret via Koyeb dashboard or CLI first._experimental_enable_light_sleep- If True, uses idle_timeout for light_sleep and configurable deep_sleep (default: False)_experimental_deep_sleep_value- Number of seconds for deep sleep when light sleep is enabled (default: 3900). Only used if _experimental_enable_light_sleep is Truedelete_after_delay- If >0, automatically delete the sandbox if there was no activity after this many seconds since creation.delete_after_inactivity_delay- If >0, automatically delete the sandbox if service sleeps due to inactivity after this many seconds.app_id- If provided, create the sandbox service in an existing app instead of creating a new one.enable_mesh- Enable or disable mesh for this sandbox. Disabled by defaultpoll_interval- Time between health checks in seconds when wait_ready is True (default: 0.5)
Returns:
AsyncSandbox- A new AsyncSandbox instance
Raises:
ValueError- If API token is not providedSandboxTimeoutError- If wait_ready is True and sandbox does not become ready within timeout
async def wait_ready(timeout: int = DEFAULT_INSTANCE_WAIT_TIMEOUT,
poll_interval: Optional[float] = None) -> boolWait for sandbox to become ready with exponential backoff async polling.
Starts polling at 0.1s intervals, doubling each time up to poll_interval.
Arguments:
timeout- Maximum time to wait in secondspoll_interval- Maximum time between health checks in seconds (defaults to instance poll_interval)
Returns:
bool- True if sandbox became ready, False if timeout
async def wait_tcp_proxy_ready(timeout: int = DEFAULT_INSTANCE_WAIT_TIMEOUT,
poll_interval: Optional[float] = None) -> boolWait for TCP proxy to become ready and available asynchronously.
Polls the deployment metadata with exponential backoff until the TCP proxy information is available. Starts at 0.1s intervals, doubling up to poll_interval.
Arguments:
timeout- Maximum time to wait in secondspoll_interval- Maximum time between checks in seconds (defaults to instance poll_interval)
Returns:
bool- True if TCP proxy became ready, False if timeout
@async_wrapper("delete")
async def delete() -> NoneDelete the sandbox instance asynchronously.
@async_wrapper("is_healthy")
async def is_healthy() -> boolCheck if sandbox is healthy and ready for operations asynchronously
@property
def exec() -> "AsyncSandboxExecutor"Get async command execution interface
@property
def filesystem() -> "AsyncSandboxFilesystem"Get filesystem operations interface
@async_wrapper("expose_port")
async def expose_port(port: int) -> ExposedPortExpose a port to external connections via TCP proxy asynchronously.
@async_wrapper("unexpose_port")
async def unexpose_port() -> NoneUnexpose a port from external connections asynchronously.
@async_wrapper("launch_process")
async def launch_process(cmd: str,
cwd: Optional[str] = None,
env: Optional[Dict[str, str]] = None) -> strLaunch a background process in the sandbox asynchronously.
@async_wrapper("kill_process")
async def kill_process(process_id: str) -> NoneKill a background process by its ID asynchronously.
@async_wrapper("list_processes")
async def list_processes() -> List[ProcessInfo]List all background processes asynchronously.
async def kill_all_processes() -> intKill all running background processes asynchronously.
@async_wrapper("update_lifecycle")
async def update_lifecycle(
delete_after_delay: Optional[int] = None,
delete_after_inactivity: Optional[int] = None) -> NoneUpdate the sandbox's life cycle settings asynchronously.
async def __aenter__() -> "AsyncSandbox"Async context manager entry - returns self.
async def __aexit__(exc_type, exc_val, exc_tb) -> NoneAsync context manager exit - automatically deletes the sandbox.
Utility functions for Koyeb Sandbox
seconds
seconds
seconds
seconds for HTTP requests
def get_api_client(
api_token: Optional[str] = None,
host: Optional[str] = None
) -> tuple[AppsApi, ServicesApi, InstancesApi, CatalogInstancesApi,
DeploymentsApi]Get configured API clients for Koyeb operations.
Arguments:
api_token- Koyeb API token. If not provided, will try to get from KOYEB_API_TOKEN env varhost- Koyeb API host URL. If not provided, will try to get from KOYEB_API_HOST env var (defaults to https://app.koyeb.com)
Returns:
Tuple of (AppsApi, ServicesApi, InstancesApi, CatalogInstancesApi) instances
Raises:
ValueError- If API token is not provided
def build_env_vars(env: Optional[Dict[str, str]]) -> List[DeploymentEnv]Build environment variables list from dictionary.
Arguments:
env- Dictionary of environment variables
Returns:
List of DeploymentEnv objects
def create_docker_source(
image: str,
command_args: List[str],
privileged: Optional[bool] = None,
image_registry_secret: Optional[str] = None) -> DockerSourceCreate Docker source configuration.
Arguments:
image- Docker image namecommand_args- Command and arguments to run (optional, empty list means use image default)privileged- If True, run the container in privileged mode (default: None/False)image_registry_secret- Name of the secret containing registry credentials for pulling private images
Returns:
DockerSource object
def create_koyeb_sandbox_ports(protocol: str = "http") -> List[DeploymentPort]Create port configuration for koyeb/sandbox image.
Creates two ports:
- Port 3030 exposed on HTTP, mounted on /koyeb-sandbox/
- Port 3031 exposed with the specified protocol, mounted on /
Arguments:
protocol- Protocol to use for port 3031 ("http" or "http2"), defaults to "http"
Returns:
List of DeploymentPort objects configured for koyeb/sandbox
def create_koyeb_sandbox_proxy_ports() -> List[DeploymentProxyPort]Create TCP proxy port configuration for koyeb/sandbox image.
Creates proxy port for direct TCP access:
- Port 3031 exposed via TCP proxy
Returns:
List of DeploymentProxyPort objects configured for TCP proxy access
def create_koyeb_sandbox_routes() -> List[DeploymentRoute]Create route configuration for koyeb/sandbox image to make it publicly accessible.
Creates two routes:
- Port 3030 accessible at /koyeb-sandbox/
- Port 3031 accessible at /
Returns:
List of DeploymentRoute objects configured for koyeb/sandbox
def create_deployment_definition(
name: str,
docker_source: DockerSource,
env_vars: List[DeploymentEnv],
instance_type: str,
exposed_port_protocol: Optional[str] = None,
region: Optional[str] = None,
routes: Optional[List[DeploymentRoute]] = None,
idle_timeout: int = 300,
enable_tcp_proxy: bool = False,
_experimental_enable_light_sleep: bool = False,
_experimental_deep_sleep_value: int = 3900,
enable_mesh: bool = None) -> DeploymentDefinitionCreate deployment definition for a sandbox service.
Arguments:
name- Service namedocker_source- Docker configurationenv_vars- Environment variablesinstance_type- Instance typeexposed_port_protocol- Protocol to expose ports with ("http" or "http2"). If None, defaults to "http". If provided, must be one of "http" or "http2".region- Region to deploy to. Defaults to KOYEB_REGION env var, or "na" if not set.routes- List of routes for public accessidle_timeout- Number of seconds to wait before sleeping the instance if it receives no trafficenable_tcp_proxy- If True, enables TCP proxy for direct TCP access to port 3031_experimental_enable_light_sleep- If True, uses light sleep when reaching idle_timeout. Light Sleep reduces cold starts to ~200ms. After scaling to zero, the service stays in Light Sleep for idle_timeout seconds before going into Deep Sleep._experimental_deep_sleep_value- Number of seconds for deep sleep when light sleep is enabled (default: 3900). Only used if _experimental_enable_light_sleep is True. Ignored otherwise.enable_mesh- Enable or disable mesh for this sandbox. Disabled by default
Returns:
DeploymentDefinition object
def escape_shell_arg(arg: str) -> strEscape a shell argument for safe use in shell commands.
Arguments:
arg- The argument to escape
Returns:
Properly escaped shell argument
def validate_port(port: int) -> NoneValidate that a port number is in the valid range.
Arguments:
port- Port number to validate
Raises:
ValueError- If port is not in valid range [1, 65535]
def check_error_message(error_msg: str, error_type: str) -> boolCheck if an error message matches a specific error type. Uses case-insensitive matching against known error patterns.
Arguments:
error_msg- The error message to checkerror_type- The type of error to check for (key in ERROR_MESSAGES)
Returns:
True if error message matches the error type
async def run_sync_in_executor(method: Callable[..., Any], *args: Any,
**kwargs: Any) -> AnyRun a synchronous method in an async executor.
Helper function to wrap synchronous methods for async execution. Used by AsyncSandbox and AsyncSandboxFilesystem to wrap sync parent methods.
Arguments:
method- The synchronous method to run*args- Positional arguments for the method**kwargs- Keyword arguments for the method
Returns:
Result of the synchronous method call
def async_wrapper(method_name: str)Decorator to automatically create async wrapper for sync methods.
This decorator creates an async method that wraps a sync method from the parent class. The sync method is called via super() and executed in an executor.
Arguments:
-
method_name- Name of the sync method to wrap (from parent class)Usage: @async_wrapper("delete") async def delete(self) -> None: """Delete the sandbox instance asynchronously.""" pass # Implementation is handled by decorator
def create_sandbox_client(conn_info: Optional['ConnectionInfo'],
existing_client: Optional[Any] = None) -> AnyCreate or return existing SandboxClient instance with validation.
Helper function to create SandboxClient instances with consistent validation. Used by Sandbox, SandboxExecutor, and SandboxFilesystem to avoid duplication.
Arguments:
conn_info- The information needed to connect to the sandbox executor APIexisting_client- Existing client instance to return if not None
Returns:
SandboxClient- Configured client instance
Raises:
SandboxError- If sandbox URL or secret is not available
class SandboxError(Exception)Base exception for sandbox operations
class SandboxTimeoutError(SandboxError)Raised when a sandbox operation times out
Sandbox Executor API Client
A simple Python client for interacting with the Sandbox Executor API.
@dataclass
class ConnectionInfo()Information needed to connect to a sandbox
class SandboxClient()Client for the Sandbox Executor API.
def __init__(conn_info: ConnectionInfo, timeout: float = DEFAULT_HTTP_TIMEOUT)Initialize the Sandbox Client.
Arguments:
conn_info- The parameters needed to connect to the sandboxtimeout- Request timeout in seconds (default: 30)
def close() -> NoneClose the HTTP session and release resources.
def __enter__()Context manager entry - returns self.
def __exit__(exc_type, exc_val, exc_tb) -> NoneContext manager exit - automatically closes the session.
def __del__()Clean up session on deletion (fallback, not guaranteed to run).
def health() -> Dict[str, str]Check the health status of the server.
Uses a short timeout and no retries since callers (wait_ready) already handle polling with backoff.
Returns:
Dict with status information
Raises:
requests.HTTPError- If the health check failsrequests.Timeout- If the health check times out
def run(cmd: str,
cwd: Optional[str] = None,
env: Optional[Dict[str, str]] = None,
timeout: Optional[float] = None) -> Dict[str, Any]Execute a shell command in the sandbox.
Arguments:
cmd- The shell command to executecwd- Optional working directory for command executionenv- Optional environment variables to set/overridetimeout- Optional timeout in seconds for the request
Returns:
Dict containing stdout, stderr, error (if any), and exit code
def run_streaming(cmd: str,
cwd: Optional[str] = None,
env: Optional[Dict[str, str]] = None,
timeout: Optional[float] = None) -> Iterator[Dict[str, Any]]Execute a shell command in the sandbox and stream the output in real-time.
This method uses Server-Sent Events (SSE) to stream command output line-by-line as it's produced. Use this for long-running commands where you want real-time output. For simple commands where buffered output is acceptable, use run() instead.
Arguments:
cmd- The shell command to executecwd- Optional working directory for command executionenv- Optional environment variables to set/overridetimeout- Optional timeout in seconds for the streaming request
Yields:
Dict events with the following types:
-
output events (as command produces output):
-
{"stream"- "stdout"|"stderr", "data": "line of output"}- complete event (when command finishes):
-
{"code"- <exit_code>, "error": false}- error event (if command fails to start):
-
{"error"- "error message"}
Example:
client = SandboxClient("http://localhost:8080", "secret") for event in client.run_streaming("echo 'Hello'; sleep 1; echo 'World'"): ... if "stream" in event: ... print(f"{event['stream']}: {event['data']}") ... elif "code" in event: ... print(f"Exit code: {event['code']}")
def write_file(path: str, content: str) -> Dict[str, Any]Write content to a file.
Arguments:
path- The file path to write tocontent- The content to write
Returns:
Dict with success status and error if any
def read_file(path: str) -> Dict[str, Any]Read content from a file.
Arguments:
path- The file path to read from
Returns:
Dict with file content and error if any
def delete_file(path: str) -> Dict[str, Any]Delete a file.
Arguments:
path- The file path to delete
Returns:
Dict with success status and error if any
def make_dir(path: str) -> Dict[str, Any]Create a directory (including parent directories).
Arguments:
path- The directory path to create
Returns:
Dict with success status and error if any
def delete_dir(path: str) -> Dict[str, Any]Recursively delete a directory and all its contents.
Arguments:
path- The directory path to delete
Returns:
Dict with success status and error if any
def list_dir(path: str) -> Dict[str, Any]List the contents of a directory.
Arguments:
path- The directory path to list
Returns:
Dict with entries list and error if any
def bind_port(port: int) -> Dict[str, Any]Bind a port to the TCP proxy for external access.
Configures the TCP proxy to forward traffic to the specified port inside the sandbox. This allows you to expose services running inside the sandbox to external connections.
Arguments:
port- The port number to bind to (must be a valid port number)
Returns:
Dict with success status, message, and port information
Notes:
- Only one port can be bound at a time
- Binding a new port will override the previous binding
- The port must be available and accessible within the sandbox environment
def unbind_port(port: Optional[int] = None) -> Dict[str, Any]Unbind a port from the TCP proxy.
Removes the TCP proxy port binding, stopping traffic forwarding to the previously bound port.
Arguments:
port- Optional port number to unbind. If provided, it must match the currently bound port. If not provided, any existing binding will be removed.
Returns:
Dict with success status and message
Notes:
- If a port is specified and doesn't match the currently bound port, the request will fail
- After unbinding, the TCP proxy will no longer forward traffic
def start_process(cmd: str,
cwd: Optional[str] = None,
env: Optional[Dict[str, str]] = None) -> Dict[str, Any]Start a background process in the sandbox.
Starts a long-running background process that continues executing even after the API call completes. Use this for servers, workers, or other long-running tasks.
Arguments:
cmd- The shell command to execute as a background processcwd- Optional working directory for the processenv- Optional environment variables to set/override for the process
Returns:
Dict with process id and success status:
- id: The unique process ID (UUID string)
- success: True if the process was started successfully
Example:
client = SandboxClient("http://localhost:8080", "secret") result = client.start_process("python -u server.py") process_id = result["id"] print(f"Started process: {process_id}")
def kill_process(process_id: str) -> Dict[str, Any]Kill a background process by its ID.
Terminates a running background process. This sends a SIGTERM signal to the process, allowing it to clean up gracefully. If the process doesn't terminate within a timeout, it will be forcefully killed with SIGKILL.
Arguments:
process_id- The unique process ID (UUID string) to kill
Returns:
Dict with success status and error message if any
Example:
client = SandboxClient("http://localhost:8080", "secret") result = client.kill_process("550e8400-e29b-41d4-a716-446655440000") if result.get("success"): ... print("Process killed successfully")
def list_processes() -> Dict[str, Any]List all background processes.
Returns information about all currently running and recently completed background processes. This includes both active processes and processes that have completed (which remain in memory until server restart).
Returns:
Dict with a list of processes:
- processes: List of process objects, each containing:
- id: Process ID (UUID string)
- command: The command that was executed
- status: Process status (e.g., "running", "completed")
- pid: OS process ID (if running)
- exit_code: Exit code (if completed)
- started_at: ISO 8601 timestamp when process started
- completed_at: ISO 8601 timestamp when process completed (if applicable)
Example:
client = SandboxClient("http://localhost:8080", "secret") result = client.list_processes() for process in result.get("processes", []): ... print(f"{process['id']}: {process['command']} - {process['status']}")