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-sandboxcontainers runningaction_execution_server.jsindependently 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
hostnetwork exposure - Privilege restrictions by running without
--privilegedmode or additional capabilities - Image verification ensuring only the trusted
hexdolemonai/lemon-runtime-sandboximage 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:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →