How Lemon AI's Docker VM Sandbox Isolates Code Execution for Security

Lemon AI runs every user-generated action inside a dedicated Docker container called lemon-runtime-sandbox, using process isolation, filesystem bind-mounts, network bridge mode, and non-privileged execution to prevent host access.

Lemon AI's Docker VM sandbox provides a secure, disposable environment for executing untrusted user code. By leveraging containerization, the platform ensures that each action runs in isolation from the host system and other users. This article examines the specific security mechanisms implemented in the hexdocom/lemonai repository, including process boundaries, filesystem restrictions, and network policies.

Process Isolation

Each user request triggers the creation or reuse of a container named lemon-runtime-sandbox. Inside this container, a dedicated Node.js process runs action_execution_server.js to handle code execution. No host-level processes are exposed to the container, and the container lifecycle is managed independently of the main application.

In src/runtime/DockerRuntime.local.js, the connect_container method handles this logic:

// src/runtime/DockerRuntime.local.js
async connect_container() {
  try {
    // Try to reuse an existing sandbox container
    const container = docker.getContainer('lemon-runtime-sandbox')
    const info = await container.inspect()
    if (info.State.Status === 'exited') await container.start()
  } catch (err) {
    // Container not found → create a fresh one
    const container = await this.init_container()
  }
  // Record the dynamically assigned ports
  const info = await container.inspect()
  this.host_port  = Object.keys(info.NetworkSettings.Ports)[0].split('/')[0]
  this.vscode_port = Object.keys(info.NetworkSettings.Ports)[1].split('/')[0]
  // …
}

Filesystem Isolation

The sandbox restricts filesystem access through bind-mounting. Only the user's workspace directory is mounted into the container at /workspace with read-write permissions. The rest of the container filesystem is built fresh from the runtime image, preventing access to host system files.

This is configured in init_container within src/runtime/DockerRuntime.local.js:

// src/runtime/DockerRuntime.local.js – init_container (Binds)
HostConfig: {
  Binds: [`${this.workspace_dir}:/workspace:rw`],
  // …
}

Network Isolation

Network traffic is isolated using Docker's default bridge network. The container does not use host network mode. Instead, Docker assigns random host ports from safe ranges that are forwarded to the container's internal ports, preventing direct network access to the host or other containers.

The port binding configuration in init_container shows this setup:

// src/runtime/DockerRuntime.local.js – init_container (PortBindings)
HostConfig: {
  PortBindings: {
    '3000/tcp': [{ HostPort: '0' }],  // Random host port
    '8080/tcp': [{ HostPort: '0' }]   // Random host port
  },
  // …
}

Resource Limits and Privilege Restrictions

The sandbox runs without privileged mode and without additional Linux capabilities. The container inherits default Docker cgroup limits for CPU and memory unless explicitly overridden. This prevents container escape vulnerabilities and resource exhaustion attacks.

The init_container method explicitly omits the Privileged flag, relying on Docker's default security profile.

Image Trust and Verification

The sandbox image hexdolemonai/lemon-runtime-sandbox:latest is built from a controlled Dockerfile that installs only required runtimes and tools. Before the UI launches, Lemon AI verifies that Docker is installed and that the required image is present. If missing, the platform pulls it automatically.

In dockerSetupService.js, the REQUIRED_DOCKER_IMAGE constant and checkDockerEnvironmentReady function handle this:

// dockerSetupService.js
const REQUIRED_DOCKER_IMAGE = 'hexdolemonai/lemon-runtime-sandbox:latest';

async function checkDockerEnvironmentReady() {
  return new Promise((resolve) => {
    exec(`docker images -q ${REQUIRED_DOCKER_IMAGE}`, (err, stdout) => {
      resolve(stdout.trim().length > 0);
    });
  });
}

Remote Deployment Isolation

For remote deployments, Lemon AI uses the same sandbox image on an external ECI (Elastic Container Instance) server. The src/runtime/DockerRuntime.js file handles remote initialization, ensuring that even when execution occurs on a remote host, the same isolation boundaries apply.

// src/runtime/DockerRuntime.js – init_container (ECI request)
async init_container() {
  // Request remote ECI to create container with same image and constraints
  const response = await requestECI({
    image: 'hexdolemonai/lemon-runtime-sandbox:latest',
    binds: [`${this.workspace_dir}:/workspace:rw`],
    // ... same security constraints
  });
}

Summary

Lemon AI's Docker VM sandbox implements defense in depth through multiple isolation layers:

  • Process isolation via dedicated lemon-runtime-sandbox containers running action_execution_server.js independently of host processes
  • Filesystem isolation through selective bind-mounting of only the workspace directory (/workspace)
  • Network isolation using Docker bridge mode with randomly assigned host ports, avoiding host network exposure
  • Privilege restrictions by running without --privileged mode or additional capabilities
  • Image verification ensuring only the trusted hexdolemonai/lemon-runtime-sandbox image executes user code

Frequently Asked Questions

How does Lemon AI prevent containers from accessing host files?

Lemon AI restricts filesystem access by bind-mounting only the specific workspace directory (${this.workspace_dir}:/workspace:rw) when initializing the container in src/runtime/DockerRuntime.local.js. The rest of the container filesystem is built fresh from the runtime image, preventing access to sensitive host paths like /etc, /root, or system binaries.

Can user code escape the Docker sandbox and run on the host?

Container escape is mitigated by running the sandbox without privileged mode (Privileged: false) and without additional Linux capabilities. The container uses the default Docker security profile and inherits cgroup limits for resource constraints. Additionally, the lemon-runtime-sandbox container runs as an isolated Node.js process (action_execution_server.js) with no direct access to host namespaces or devices.

What happens if the required Docker image is missing when starting Lemon AI?

Before launching the UI, dockerSetupService.js runs checkDockerEnvironmentReady() to verify that the hexdolemonai/lemon-runtime-sandbox:latest image exists locally. If the image is missing, the service automatically pulls it from the registry or loads it from a verified tarball. The application only proceeds once the image is confirmed present, ensuring consistent sandbox environments.

Does Lemon AI support remote sandbox execution?

Yes, Lemon AI supports remote execution through the src/runtime/DockerRuntime.js module. When configured for remote ECI (Elastic Container Instance) deployment, the platform launches the same lemon-runtime-sandbox image on the remote host using identical isolation constraints: filesystem bind-mounts, bridge networking, and non-privileged execution. This ensures security parity between local and remote execution environments.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →