Skip to content

Add exec-sandbox code executor (self-hosted QEMU microVMs) #4643

Closed as not planned
Closed as not planned

Description

@clemlesne

Add exec-sandbox code executor (self-hosted QEMU microVMs)

Summary

Proposing exec-sandbox as a new BaseCodeExecutor backend. It runs code in ephemeral QEMU microVMs with hardware-level isolation (KVM on Linux, HVF on macOS) — self-hosted, no cloud account needed.

Why

  • Self-hosted — ContainerCodeExecutor needs Docker, GkeCodeExecutor needs GKE, VertexAiCodeExecutor needs Vertex. exec-sandbox runs on bare metal with just QEMU. Data never leaves the machine — relevant for customer data privacy, regulated environments, and cheaper at scale.
  • macOS + Linux — HVF on macOS, KVM on Linux. Same codebase for dev and prod.
  • Fast — ~1-2ms from warm pool, ~200ms from memory snapshots.
  • Stronger isolation than containers — Hardware-level VM boundary vs Docker process isolation.
  • Apache-2.0

How it maps

exec-sandbox is a Python library with an async API. A BaseCodeExecutor subclass bridges async → sync, following the same pattern as ContainerCodeExecutor:

import asyncio
from exec_sandbox import Scheduler

class ExecSandboxCodeExecutor(BaseCodeExecutor):
    stateful: bool = True

    def __init__(self, **kwargs):
        super().__init__(**kwargs)
        self._loop = asyncio.new_event_loop()
        self._scheduler = self._loop.run_until_complete(Scheduler().__aenter__())
        self._sessions: dict[str, Session] = {}

    def execute_code(self, invocation_context, code_execution_input):
        # Reuse session for stateful execution
        eid = code_execution_input.execution_id
        if eid and eid in self._sessions:
            session = self._sessions[eid]
        else:
            session = self._loop.run_until_complete(
                self._scheduler.session(language="python")
            )
            if eid:
                self._sessions[eid] = session

        result = self._loop.run_until_complete(session.exec(code_execution_input.code))
        return CodeExecutionResult(
            stdout=result.stdout,
            stderr=result.stderr,
        )

Usage:

agent = LlmAgent(
    code_executor=ExecSandboxCodeExecutor(),
    ...
)

Open questions

  • Async/sync bridge — exec-sandbox is fully async. The sketch uses a dedicated event loop. A background-thread loop would be safer if called from an existing async context.
  • File I/O — CodeExecutionInput.input_files and CodeExecutionResult.output_files could use exec-sandbox's native write_file/read_file APIs instead of writing to a shared volume.
  • Shipping — External package (exec-sandbox[adk] extra) vs upstream in code_executors/ following the GkeCodeExecutor pattern from PR feat(code_executors): Add GkeCodeExecutor for sandboxed code execution on GKE #1629.

Links

  • Repo · PyPI
  • Reference: gke_code_executor.py in code_executors/ (same integration pattern)

Happy to submit a PR if there's interest.

Activity

  1. added
    tools[Component] This issue is related to tools
    on Feb 27, 2026
  2. added theissue type on Feb 27, 2026
  3. self-assigned this
    on Mar 2, 2026
  4. klateefa commented on Mar 2, 2026

    @klateefa

    @clemlesne Thanks for reporting!
    Your request is being reviewed, and I’ll update you if we need anything else. Thanks for your patience.

  5. added
    needs review[Status] The PR/issue is awaiting review from the maintainer
    on Mar 2, 2026
  6. DeanChensj commented on Oct 14, 2026

    @DeanChensj
    Collaborator

    Thank you for sharing exec-sandbox and for the detailed proposal!

    To keep the core google-adk package dependencies and maintenance surface focused, we only maintain first-party and standard local/container executors directly in the core repository (UnsafeLocalCodeExecutor, ContainerCodeExecutor, VertexAiCodeExecutor, AgentEngineSandboxCodeExecutor).

    BaseCodeExecutor is designed as an extensible public interface so that specialized sandbox backends like exec-sandbox can be published and maintained as standalone community packages.

    We're closing this issue for now, but if you run into any limitations or friction in the BaseCodeExecutor interface while integrating exec-sandbox, please feel free to open a new issue!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Labels

needs review[Status] The PR/issue is awaiting review from the maintainertools[Component] This issue is related to tools

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions