<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/">
    <channel>
        <title>OpenDsec blog</title>
        <link>https://kernflux.github.io/en/blog/</link>
        <description>Release notes, design decisions and test reports from OpenDsec</description>
        <lastBuildDate>Sun, 25 Oct 2026 08:49:02 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <language>en</language>
        <copyright>© 2026 Yibo Zhou · Kernflux</copyright>
        <item>
            <title><![CDATA[DeepSeek's DSec paper: sandbox infrastructure for agentic RL at scale]]></title>
            <link>https://kernflux.github.io/en/blog/posts/dsec-paper</link>
            <guid>https://kernflux.github.io/en/blog/posts/dsec-paper</guid>
            <pubDate>Sun, 25 Oct 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[DeepSeek's DSec paper explained: how its agentic RL sandbox platform schedules, overcommits, loads images on demand and blocks cheating, and how OpenDsec differs.]]></description>
            <content:encoded><![CDATA[<h1 id="deepseek-s-dsec-paper-sandbox-infrastructure-for-agentic-rl-at-scale" tabindex="-1">DeepSeek's DSec paper: sandbox infrastructure for agentic RL at scale <a class="header-anchor" href="#deepseek-s-dsec-paper-sandbox-infrastructure-for-agentic-rl-at-scale" aria-label="Permalink to &quot;DeepSeek's DSec paper: sandbox infrastructure for agentic RL at scale&quot;">&ZeroWidthSpace;</a></h1>
<p>Training AI agents that write code and use tools takes a sandbox for every reinforcement learning (RL) rollout: an isolated environment that can install dependencies, run commands and read and write files. DeepSeek's paper <em>DeepSeek Elastic Compute (DSec): A Sandbox Infrastructure for Effective Agentic Training at Scale</em> (<a href="https://arxiv.org/abs/2609.22978" target="_blank" rel="noreferrer">arXiv:2609.22978</a>, September 2026) describes the sandbox platform they run in production.</p>
<p>The short version. Five things in the paper are worth copying:</p>
<ol>
<li><strong>Sandboxes use little of what they ask for.</strong> About 90% of sandboxes use at most 5% of the CPU they request on average, so you can overcommit heavily, as long as CPU core scheduling protects the latency-sensitive work.</li>
<li><strong>Don't pull whole images.</strong> A sandbox reads only 4–13% of its image, so on-demand loading is far faster than a full pull.</li>
<li><strong>Scheduling can be simple.</strong> Power-of-k random choice, a local record of placements in flight, and the node has the final say.</li>
<li><strong>RL needs task-level operations.</strong> When training is preempted or finishes, pause, resume or stop all of a task's sandboxes at once.</li>
<li><strong>Agents cheat.</strong> The paper records real reward hacking and escape attempts, and the sandbox has to defend against them.</li>
</ol>
<p>The details follow. Our open-source project <a href="https://github.com/Kernflux/opendsec" target="_blank" rel="noreferrer">OpenDsec</a> implements this paper; the end of this post says where it chose differently. OpenDsec is not affiliated with DeepSeek.</p>
<h2 id="scale" tabindex="-1">Scale <a class="header-anchor" href="#scale" aria-label="Permalink to &quot;Scale&quot;">&ZeroWidthSpace;</a></h2>
<p>The paper's production numbers:</p>
<table tabindex="0">
<thead>
<tr>
<th></th>
<th>Number</th>
</tr>
</thead>
<tbody>
<tr>
<td>One production unit</td>
<td>about 160 nodes</td>
</tr>
<tr>
<td>Sandboxes created per day</td>
<td>about 3 million</td>
</tr>
<tr>
<td>Sandboxes running at once</td>
<td>over 380,000</td>
</tr>
<tr>
<td>Creation rate</td>
<td>over 5,000 per second</td>
</tr>
<tr>
<td>One training job</td>
<td>up to 32K sandboxes</td>
</tr>
<tr>
<td>One node, stable</td>
<td>3,200 containers or 800 microVMs</td>
</tr>
<tr>
<td>RL sandbox lifetime</td>
<td>p99 over 3 hours</td>
</tr>
</tbody>
</table>
<p>The last row is easy to miss: sandboxes outlive a deploy, so upgrading a node cannot kill them.</p>
<h2 id="one-sdk-four-backends-no-abstraction" tabindex="-1">One SDK, four backends, no abstraction <a class="header-anchor" href="#one-sdk-four-backends-no-abstraction" aria-label="Permalink to &quot;One SDK, four backends, no abstraction&quot;">&ZeroWidthSpace;</a></h2>
<p>DSec has four backends: function calls (FnCall), containers, microVMs and full VMs. They share one SDK, but the SDK does <strong>not</strong> try to hide them behind one interface: the caller picks the backend, and each backend has its own arguments. The reason is practical. Containers, microVMs and VMs differ a lot in start time, isolation and whether they can have a GPU, and a common abstraction would only hide that.</p>
<h2 id="scheduling-power-of-k-and-the-node-decides" tabindex="-1">Scheduling: power-of-k, and the node decides <a class="header-anchor" href="#scheduling-power-of-k-and-the-node-decides" aria-label="Permalink to &quot;Scheduling: power-of-k, and the node decides&quot;">&ZeroWidthSpace;</a></h2>
<p>Placement uses the classic power-of-k choices (the paper cites Mitzenmacher): pick k candidate nodes at random and take the least loaded. It needs no exact global state and still spreads load evenly.</p>
<p>Two details make it work under heavy concurrency:</p>
<ul>
<li><strong>In-flight overlay.</strong> Node load is reported periodically, so a sandbox placed between two reports is invisible. Each scheduler instance records a placement the moment it picks a node and adds it to the load it computes, so a burst does not pile onto one node.</li>
<li><strong>The node has the final admission decision.</strong> Placement is best effort. The node checks its own capacity and memory pressure again and refuses if it is short; the scheduler retries elsewhere.</li>
</ul>
<p>The control-plane services (API server, state collector) keep no persistent state: after a restart they poll the nodes again. A sandbox id encodes its node, so routing needs no lookup.</p>
<h2 id="overcommit-and-cpu-core-scheduling" tabindex="-1">Overcommit and CPU core scheduling <a class="header-anchor" href="#overcommit-and-cpu-core-scheduling" aria-label="Permalink to &quot;Overcommit and CPU core scheduling&quot;">&ZeroWidthSpace;</a></h2>
<p>About 90% of sandboxes use at most 5% of the CPU they request on average. Allocating by request wastes most of the machines, so DSec overcommits CPU heavily.</p>
<p>The cost falls on latency-sensitive work. Marking background work <code>SCHED_IDLE</code> is not enough: it only decides who runs first on one hardware thread, not what the neighbour on the other hyperthread of the same core does to the shared execution units and caches. The paper measured that, under a 50% interfering load, latency rose 45.2% unprotected; <code>SCHED_IDLE</code> improved that by at most 3.4%; with Linux CPU core scheduling the rise was 17.3%.</p>
<p>For memory, the paper also shares and reclaims: paused sandboxes have their memory swapped out, and identical read-only data is shared between sandboxes.</p>
<h2 id="images-layered-builds-on-demand-loading" tabindex="-1">Images: layered builds, on-demand loading <a class="header-anchor" href="#images-layered-builds-on-demand-loading" aria-label="Permalink to &quot;Images: layered builds, on-demand loading&quot;">&ZeroWidthSpace;</a></h2>
<p>RL environments are many, and each is used little, so pulling a whole image onto a node first is wasteful. DSec instead:</p>
<ul>
<li><strong>Layers</strong> environments from independently versioned parts such as base, workspace and toolkit, so changing one part does not rebuild the whole image.</li>
<li><strong>Loads on demand</strong> from 3FS, DeepSeek's distributed file system: a sandbox fetches only the data it uses, which the paper measured at 4–13% of the image.</li>
<li><strong>Result:</strong> a burst of 8,192 containers took 35 minutes with on-demand loading and over 60 minutes with a full pull.</li>
<li><strong>Separates building from running:</strong> the accounts that build environments are not the accounts that run tasks, so a running task cannot write into an image others use.</li>
</ul>
<h2 id="a-lifecycle-designed-for-rl" tabindex="-1">A lifecycle designed for RL <a class="header-anchor" href="#a-lifecycle-designed-for-rl" aria-label="Permalink to &quot;A lifecycle designed for RL&quot;">&ZeroWidthSpace;</a></h2>
<p>DSec is co-designed with the RL training framework: stateful rollout execution happens in sandboxes, separate from GPU training, which can be preempted. For that the platform provides:</p>
<ul>
<li><strong>Task-level operations:</strong> pause, resume or stop all of a task's sandboxes. Pause them all when training is preempted; resume them all when it is back.</li>
<li><strong>Transparent resume:</strong> any operation on a paused sandbox resumes it first.</li>
<li><strong>Reclaim on timeout:</strong> an explicit stop, or an idle timeout.</li>
<li><strong>Nested projects and bounded delegation:</strong> projects nest to any depth (say <code>root/rl/job-42/agent-7</code>), and an identity can only delegate permissions it holds.</li>
</ul>
<h2 id="cheating-and-escape-attempts" tabindex="-1">Cheating and escape attempts <a class="header-anchor" href="#cheating-and-escape-attempts" aria-label="Permalink to &quot;Cheating and escape attempts&quot;">&ZeroWidthSpace;</a></h2>
<p>The paper records what agents actually did inside sandboxes: unbounded output filling the disk, forged requests to the sandbox's own management process, reading that process's logs for the answers, overwriting <code>/bin/bash</code>, corrupting file system metadata with ioctls, and scanning ports for package mirrors.</p>
<p>DSec's defences include AppArmor confinement and a network policy enforced with eBPF: outbound traffic denied by default and allowed by rule.</p>
<h2 id="cloud-bursting" tabindex="-1">Cloud bursting <a class="header-anchor" href="#cloud-bursting" aria-label="Permalink to &quot;Cloud bursting&quot;">&ZeroWidthSpace;</a></h2>
<p>Above 80% utilization of the on-premises cluster, DSec sends part of the container work to the cloud. Only tasks whose images are in a deduplicated EROFS set of about 30 TB qualify; that set covers about 70% of container tasks.</p>
<h2 id="lessons-for-building-your-own-sandbox-platform" tabindex="-1">Lessons for building your own sandbox platform <a class="header-anchor" href="#lessons-for-building-your-own-sandbox-platform" aria-label="Permalink to &quot;Lessons for building your own sandbox platform&quot;">&ZeroWidthSpace;</a></h2>
<ul>
<li><strong>Plan capacity by use, not by request</strong>, then protect latency-sensitive work with core scheduling.</li>
<li><strong>Image size is not the problem; how much gets read is.</strong> Load on demand first, tune storage later.</li>
<li><strong>Simple random placement is enough</strong> if nodes have the final say and schedulers remember what they just placed.</li>
<li><strong>Design task-level operations and pause from day one.</strong> RL training will be preempted.</li>
<li><strong>Treat the agent as untrusted code:</strong> capped output, deny-by-default network, grading kept apart from the agent.</li>
</ul>
<h2 id="opendsec-an-open-source-implementation-with-different-choices" tabindex="-1">OpenDsec: an open-source implementation, with different choices <a class="header-anchor" href="#opendsec-an-open-source-implementation-with-different-choices" aria-label="Permalink to &quot;OpenDsec: an open-source implementation, with different choices&quot;">&ZeroWidthSpace;</a></h2>
<p><a href="/en/">OpenDsec</a> implements the paper's architecture (IAM, placement, state collection, nodes, SDK and image store) and runs in production on two 384-thread bare-metal nodes. We chose differently in a few places:</p>
<table tabindex="0">
<thead>
<tr>
<th></th>
<th>DSec paper</th>
<th>OpenDsec</th>
</tr>
</thead>
<tbody>
<tr>
<td>Main backend</td>
<td>Containers; microVMs where stronger isolation is needed</td>
<td>Firecracker microVMs: no shared host kernel, and memory snapshots. Containers for GPUs and machines without KVM</td>
</tr>
<tr>
<td>Fork and pause</td>
<td>Task-level pause and resume</td>
<td>The same, plus forking a running microVM with its memory (<code>fork(count=N)</code>), for tree search</td>
</tr>
<tr>
<td>Network policy</td>
<td>eBPF</td>
<td>nftables and a built-in DNS proxy, same semantics: deny by default, allow by domain, address and port, changeable at run time</td>
</tr>
<tr>
<td>Image storage</td>
<td>3FS</td>
<td>Any shared file system: NFS, CephFS, JuiceFS</td>
</tr>
<tr>
<td>Compatibility</td>
<td>Its own SDK</td>
<td>Its own SDK, plus an E2B-compatible gateway</td>
</tr>
</tbody>
</table>
<p>Some measurements made against the paper (the project's bare-metal test machines, 2 × AMD EPYC 9Y24):</p>
<ul>
<li><strong>CPU core scheduling:</strong> under a 50% interfering load, the latency rise fell from 18.0% unprotected to 2.1% (<code>SCHED_IDLE</code> plus core scheduling); under 100%, from 81.0% to 10.0%. Same direction as the paper.</li>
<li><strong>Density:</strong> one 384-thread node created a burst of 5,120 microVMs in 37 seconds; two nodes created a burst of 8,192 containers, all successfully.</li>
<li><strong>Anti-cheating:</strong> each incident the paper lists is handled, for example at most 64 MB recorded per output stream of a command, and agent sessions as an unprivileged user.</li>
</ul>
<p>OpenDsec also has a fleet service for batched rolling upgrades and config changes, with running sandboxes untouched.</p>
<p>Not done yet: no on-demand versus full-pull comparison, and GPUs are untested on real hardware.</p>
<h2 id="faq" tabindex="-1">FAQ <a class="header-anchor" href="#faq" aria-label="Permalink to &quot;FAQ&quot;">&ZeroWidthSpace;</a></h2>
<h3 id="is-dsec-open-source" tabindex="-1">Is DSec open source? <a class="header-anchor" href="#is-dsec-open-source" aria-label="Permalink to &quot;Is DSec open source?&quot;">&ZeroWidthSpace;</a></h3>
<p>The paper describes DeepSeek's internal system and comes with no code. OpenDsec is not DeepSeek's code but our independent open-source implementation of the paper (server AGPL-3.0, SDKs Apache-2.0) and is not affiliated with DeepSeek.</p>
<h3 id="does-dsec-use-containers-or-virtual-machines" tabindex="-1">Does DSec use containers or virtual machines? <a class="header-anchor" href="#does-dsec-use-containers-or-virtual-machines" aria-label="Permalink to &quot;Does DSec use containers or virtual machines?&quot;">&ZeroWidthSpace;</a></h3>
<p>Both. DSec has four backends (function calls, containers, microVMs and full VMs), with containers as the main one; the caller picks per task.</p>
<h3 id="why-does-rl-training-need-its-own-sandbox-platform" tabindex="-1">Why does RL training need its own sandbox platform? <a class="header-anchor" href="#why-does-rl-training-need-its-own-sandbox-platform" aria-label="Permalink to &quot;Why does RL training need its own sandbox platform?&quot;">&ZeroWidthSpace;</a></h3>
<p>One RL job can need tens of thousands of sandboxes at once, each installing dependencies, running tests and living for hours, and training can be preempted at any time. General container platforms usually lack task-level pause, large bursts of creation and defences against cheating.</p>
<h3 id="how-much-can-sandboxes-be-overcommitted" tabindex="-1">How much can sandboxes be overcommitted? <a class="header-anchor" href="#how-much-can-sandboxes-be-overcommitted" aria-label="Permalink to &quot;How much can sandboxes be overcommitted?&quot;">&ZeroWidthSpace;</a></h3>
<p>About 90% of sandboxes use at most 5% of the CPU they request. OpenDsec overcommits CPU 8× by default and protects latency-sensitive sandboxes with CPU core scheduling. The right ratio depends on your workload.</p>
<h3 id="what-is-reward-hacking-and-how-does-a-sandbox-prevent-it" tabindex="-1">What is reward hacking, and how does a sandbox prevent it? <a class="header-anchor" href="#what-is-reward-hacking-and-how-does-a-sandbox-prevent-it" aria-label="Permalink to &quot;What is reward hacking, and how does a sandbox prevent it?&quot;">&ZeroWidthSpace;</a></h3>
<p>Reward hacking is an agent earning reward by gaming the grader instead of solving the task, for example by reading the answers from grading logs or replacing system commands. The usual defence is to keep the agent away from grading: different users for agent and grader, capped output, and network denied by default.</p>
<h3 id="is-this-useful-if-i-only-need-a-code-execution-sandbox-for-an-ai-agent" tabindex="-1">Is this useful if I only need a code execution sandbox for an AI agent? <a class="header-anchor" href="#is-this-useful-if-i-only-need-a-code-execution-sandbox-for-an-ai-agent" aria-label="Permalink to &quot;Is this useful if I only need a code execution sandbox for an AI agent?&quot;">&ZeroWidthSpace;</a></h3>
<p>Yes. Overcommit, on-demand loading and escape defences apply to any service that runs untrusted code. OpenDsec also works as a code execution sandbox for AI agents and speaks the E2B SDK's API: see <a href="/en/blog/posts/self-hosted-e2b.html">self-hosting E2B</a>.</p>
<h2 id="further-reading" tabindex="-1">Further reading <a class="header-anchor" href="#further-reading" aria-label="Permalink to &quot;Further reading&quot;">&ZeroWidthSpace;</a></h2>
<ul>
<li>The paper: <a href="https://arxiv.org/abs/2609.22978" target="_blank" rel="noreferrer">arXiv:2609.22978</a></li>
<li><a href="/en/docs/concepts.html">OpenDsec concepts</a></li>
<li><a href="/en/docs/compare.html">OpenDsec compared with E2B, Daytona and Modal</a></li>
<li><a href="/en/docs/quickstart.html">Quickstart</a>: OpenDsec on one Linux machine</li>
</ul>
<p><em>OpenDsec is an open-source project by Kernflux and is not affiliated with DeepSeek. Everything said here about DSec comes from the paper above.</em></p>
]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Self-hosting E2B: run E2B code sandboxes on your own servers]]></title>
            <link>https://kernflux.github.io/en/blog/posts/self-hosted-e2b</link>
            <guid>https://kernflux.github.io/en/blog/posts/self-hosted-e2b</guid>
            <pubDate>Sun, 25 Oct 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[Two ways to self-host E2B, and a step-by-step E2B-compatible code sandbox with open-source OpenDsec: one-machine install, code interpreter, fork and snapshots.]]></description>
            <content:encoded><![CDATA[<h1 id="self-hosting-e2b-run-e2b-code-sandboxes-on-your-own-servers" tabindex="-1">Self-hosting E2B: run E2B code sandboxes on your own servers <a class="header-anchor" href="#self-hosting-e2b-run-e2b-code-sandboxes-on-your-own-servers" aria-label="Permalink to &quot;Self-hosting E2B: run E2B code sandboxes on your own servers&quot;">&ZeroWidthSpace;</a></h1>
<p><a href="https://e2b.dev" target="_blank" rel="noreferrer">E2B</a> is one of the most used code execution sandboxes for AI agents: the code an agent writes runs in an isolated Firecracker microVM, and many agent frameworks support E2B. But E2B is mainly a hosted cloud service, so your code and data go to E2B's cloud. When data must stay on your network, the network is air-gapped, or you run enough sandboxes that the hosted price stops making sense, you self-host.</p>
<p>The short version. There are two ways to self-host E2B:</p>
<ol>
<li><strong>E2B's own open-source runtime</strong> (<a href="https://github.com/e2b-dev/runtime" target="_blank" rel="noreferrer">e2b-dev/runtime</a>, Apache-2.0). It can be self-hosted on one machine (Embed); deploying into your own cloud account (BYOC) is an enterprise feature.</li>
<li><strong>OpenDsec's E2B gateway.</strong> <a href="https://github.com/Kernflux/opendsec" target="_blank" rel="noreferrer">OpenDsec</a> is an open-source sandbox platform with a gateway that serves the E2B API. Code written for the E2B SDK runs unchanged on your own machine or cluster after you set three environment variables.</li>
</ol>
<p>This post walks through the second: a single-machine install, calling it with the E2B SDK, setting up the code interpreter, growing into a cluster, and how it differs from E2B.</p>
<h2 id="step-1-install-on-one-machine" tabindex="-1">Step 1: install on one machine <a class="header-anchor" href="#step-1-install-on-one-machine" aria-label="Permalink to &quot;Step 1: install on one machine&quot;">&ZeroWidthSpace;</a></h2>
<p>You need a Linux x86_64 machine (tested on Ubuntu 24.04) with systemd and cgroup v2. For microVMs (Firecracker, as in E2B) you also need <code>/dev/kvm</code>: bare metal, or a cloud VM with nested virtualization.</p>
<div class="language-sh vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">sh</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">sudo</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> -i</span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">                                    # the rest runs as root</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">apt</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> update</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> &#x26;&#x26; </span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">apt</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> install</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> -y</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> make</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> git</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> python3-venv</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">curl</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> -fsSL</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> https://go.dev/dl/go1.27.3.linux-amd64.tar.gz</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> |</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> tar</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> -C</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> /usr/local</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> -xz</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">git</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> clone</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> https://github.com/kernflux/opendsec</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> &#x26;&#x26; </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">cd</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> opendsec</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">deploy/quickstart.sh</span></span></code></pre>
</div><p>The script builds the binaries, downloads Firecracker and the guest kernel, and installs every service, <strong>including the E2B gateway</strong> (port 7400) with a template named <code>base</code> (Python 3.12). On a fresh 8-core VM the whole run took about 11 minutes, most of it building.</p>
<h2 id="step-2-call-it-with-the-e2b-sdk" tabindex="-1">Step 2: call it with the E2B SDK <a class="header-anchor" href="#step-2-call-it-with-the-e2b-sdk" aria-label="Permalink to &quot;Step 2: call it with the E2B SDK&quot;">&ZeroWidthSpace;</a></h2>
<p>The install writes <code>/root/opendsec-ops/demo.env</code>, which already holds the three variables the E2B SDK reads:</p>
<div class="language-sh vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">sh</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">.</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> /root/opendsec-ops/demo.env</span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">       # sets E2B_API_URL, E2B_SANDBOX_URL, E2B_API_KEY</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">python3</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> -m</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> venv</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> .venv</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> &#x26;&#x26; </span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">.venv/bin/pip</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> install</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> e2b</span></span></code></pre>
</div><p><code>E2B_API_KEY</code> is an OpenDsec token. From here the code is exactly what you would write for E2B's cloud (run it with <code>.venv/bin/python</code>):</p>
<div class="language-python vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">python</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">from</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> e2b </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">import</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> Sandbox</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">sbx </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> Sandbox.create()                          </span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D"># template "base"</span></span>
<span class="line"><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">print</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(sbx.commands.run(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"python3 -V"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">).stdout)</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">sbx.files.write(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"/tmp/a.txt"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"hello"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">print</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(sbx.files.read(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"/tmp/a.txt"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">))</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">sbx.kill()</span></span></code></pre>
</div><p>To call it from another machine, point the three variables at this machine:</p>
<div class="language-sh vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">sh</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">export</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> E2B_API_URL</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">http://</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">&#x3C;</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">gateway</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">></span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:7400</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">export</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> E2B_SANDBOX_URL</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">http://</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">&#x3C;</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">gateway</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">></span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:7400</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">export</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> E2B_API_KEY</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=&#x3C;</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">your OpenDsec token</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">></span></span></code></pre>
</div><p>The JS/TS SDK works too (Node.js 22.19 or newer).</p>
<h2 id="fork-snapshots-and-pause" tabindex="-1">Fork, snapshots and pause <a class="header-anchor" href="#fork-snapshots-and-pause" aria-label="Permalink to &quot;Fork, snapshots and pause&quot;">&ZeroWidthSpace;</a></h2>
<p>On microVMs, fork, snapshots and pause all keep <strong>memory</strong>, so running processes carry on:</p>
<div class="language-python vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">python</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">from</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> e2b </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">import</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> Sandbox</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">src </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> Sandbox.create(</span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">timeout</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">300</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">src.files.write(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"/tmp/state"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"v1"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">forks </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> src.fork(</span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">count</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">2</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)                       </span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D"># two copies of the running sandbox</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">snap </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> src.create_snapshot()                    </span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D"># a snapshot</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">again </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> Sandbox.create(snap.snapshot_id)        </span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D"># a sandbox from the snapshot</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D"># Pause instead of ending at the timeout; connect resumes</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">s </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> Sandbox.create(</span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">timeout</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">60</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">lifecycle</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">{</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"on_timeout"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"pause"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"auto_resume"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">True</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">})</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">s2 </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> Sandbox.connect(s.sandbox_id)</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">for</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> x </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">in</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> [src, </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">*</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">forks, again, s2]:</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    x.kill()</span></span></code></pre>
</div><p>Forks suit an agent's tree search, or fanning out many RL rollouts from one prepared environment.</p>
<h2 id="step-3-set-up-the-code-interpreter" tabindex="-1">Step 3: set up the code interpreter <a class="header-anchor" href="#step-3-set-up-the-code-interpreter" aria-label="Permalink to &quot;Step 3: set up the code interpreter&quot;">&ZeroWidthSpace;</a></h2>
<p>E2B's code interpreter (<code>run_code</code>, with results, charts and pandas tables) needs a template of its own. OpenDsec builds it from E2B's open-source code-interpreter server: a memory snapshot of a microVM already running that server and a Python Jupyter kernel, so a new sandbox runs code at once.</p>
<p>The build installs packages inside the sandbox, so the machine needs access to apt mirrors and PyPI. Still in the <code>opendsec</code> directory:</p>
<div class="language-sh vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">sh</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">curl</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> -fsSL</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> https://codeload.github.com/e2b-dev/code-interpreter/tar.gz/refs/heads/main</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> |</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> tar</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> -xz</span></span>
<span class="line"><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">.</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> /root/opendsec-ops/env</span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">                        # the admin token: publishing into the root project</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">.venv/bin/pip</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> install</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> ./sdk/python</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">.venv/bin/python</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> deploy/e2b/build-code-interpreter.py</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> code-interpreter-main/template</span></span></code></pre>
</div><p>That builds the image <code>root/code-interpreter:v1</code>. Add a template for it under <code>e2b.templates</code> in <code>/root/opendsec-ops/opendsec.json</code>:</p>
<div class="language-json vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">json</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"code-interpreter-v1"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: {</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">"image"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"root/code-interpreter:v1"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">"backend"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"microvm"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">"cpu"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">2</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">"memory_mb"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">2048</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span></code></pre>
</div><p>Push the config and restart:</p>
<div class="language-sh vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">sh</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">export</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> OPENDSEC_OPS</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">/root/opendsec-ops</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">deploy/prod/opendsecops</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> push-config</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> all</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> &#x26;&#x26; </span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">deploy/prod/opendsecops</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> restart</span></span></code></pre>
</div><p>Callers then install <code>e2b-code-interpreter</code> and use it:</p>
<div class="language-python vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">python</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">from</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> e2b_code_interpreter </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">import</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> Sandbox</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">sbx </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> Sandbox.create(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"code-interpreter-v1"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">print</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(sbx.run_code(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"6 * 7"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">).text)               </span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D"># 42</span></span></code></pre>
</div><h2 id="speed" tabindex="-1">Speed <a class="header-anchor" href="#speed" aria-label="Permalink to &quot;Speed&quot;">&ZeroWidthSpace;</a></h2>
<p>Measured on 2026-10-23 on the project's bare-metal test machines (AMD EPYC 9Y24), through the gateway, on Firecracker microVMs:</p>
<table tabindex="0">
<thead>
<tr>
<th>Operation</th>
<th>Time</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>Sandbox.create()</code>, template <code>base</code> (2 vCPU, 1 GB, cold start)</td>
<td>p50 166 ms</td>
</tr>
<tr>
<td>First command after create</td>
<td>p50 12 ms</td>
</tr>
<tr>
<td><code>Sandbox.create(&quot;code-interpreter-v1&quot;)</code> (2 GB memory snapshot)</td>
<td>about 250 ms</td>
</tr>
<tr>
<td>First <code>run_code</code></td>
<td>about 40 ms</td>
</tr>
<tr>
<td><code>fork(count=2)</code></td>
<td>about 100–150 ms</td>
</tr>
<tr>
<td><code>create_snapshot()</code></td>
<td>about 40–70 ms</td>
</tr>
<tr>
<td><code>Sandbox.create(snapshot_id)</code></td>
<td>about 65–85 ms</td>
</tr>
</tbody>
</table>
<h2 id="growing-into-a-cluster" tabindex="-1">Growing into a cluster <a class="header-anchor" href="#growing-into-a-cluster" aria-label="Permalink to &quot;Growing into a cluster&quot;">&ZeroWidthSpace;</a></h2>
<p>If one machine is enough, stop here. With more sandboxes, OpenDsec grows into a multi-machine cluster (<a href="/en/docs/cluster.html">deployment docs</a>), and the E2B gateway into several replicas:</p>
<ol>
<li>In the cluster config's <code>e2b</code> section, list the gateways' addresses, the project sandboxes go into, and the templates.</li>
<li>Give nodes the fleet role <code>e2b=&lt;index&gt;</code>.</li>
<li>Put a load balancer in front: TCP 7400, health check HTTP 9467 <code>/readyz</code>.</li>
</ol>
<p>The gateway keeps no state of its own and makes every call to OpenDsec with the caller's token, so permissions, quotas and audit records apply as usual.</p>
<h2 id="how-it-differs-from-e2b-s-cloud" tabindex="-1">How it differs from E2B's cloud <a class="header-anchor" href="#how-it-differs-from-e2b-s-cloud" aria-label="Permalink to &quot;How it differs from E2B's cloud&quot;">&ZeroWidthSpace;</a></h2>
<p>The full list is in <a href="/en/docs/e2b.html">E2B compatibility</a>. The main points:</p>
<table tabindex="0">
<thead>
<tr>
<th></th>
<th>E2B</th>
<th>OpenDsec gateway</th>
</tr>
</thead>
<tbody>
<tr>
<td>Templates</td>
<td>Built with E2B's template build</td>
<td>An admin maps template names to OpenDsec images in the config; no template build API</td>
</tr>
<tr>
<td>Sandbox ports</td>
<td>Can be public</td>
<td>Need the token (the SDK sends it)</td>
</tr>
<tr>
<td>Filesystem-only pause</td>
<td>Supported</td>
<td>Refused (400): pause always keeps memory</td>
</tr>
<tr>
<td>Filesystem-only snapshots</td>
<td>Supported</td>
<td>Refused (400) on microVMs; container snapshots are filesystem-only anyway</td>
</tr>
<tr>
<td>Code interpreter languages</td>
<td>Python, JavaScript, R, Java, Bash</td>
<td>Python</td>
</tr>
<tr>
<td>Not supported</td>
<td></td>
<td>Directory watching, signed file URLs, MCP, volumes</td>
</tr>
</tbody>
</table>
<p>Options the gateway cannot honour return 400 instead of being ignored, so a program never runs without a setting it asked for.</p>
<h2 id="e2b-self-hosted-or-opendsec" tabindex="-1">E2B self-hosted or OpenDsec? <a class="header-anchor" href="#e2b-self-hosted-or-opendsec" aria-label="Permalink to &quot;E2B self-hosted or OpenDsec?&quot;">&ZeroWidthSpace;</a></h2>
<ul>
<li><strong>E2B self-hosted (Embed)</strong> suits one machine that is enough and a preference for E2B's own code; deploying into your own cloud account (BYOC) is an E2B enterprise feature.</li>
<li><strong>OpenDsec</strong> suits multi-machine clusters, air-gapped installs, large bursts of creation such as RL rollouts, or wanting containers, QEMU VMs and task-level pause besides the E2B SDK. The cost is the compatibility gaps above, and the project is still 0.x.</li>
</ul>
<p>More in <a href="/en/docs/compare.html">OpenDsec compared with E2B, Daytona and Modal</a>.</p>
<h2 id="faq" tabindex="-1">FAQ <a class="header-anchor" href="#faq" aria-label="Permalink to &quot;FAQ&quot;">&ZeroWidthSpace;</a></h2>
<h3 id="can-e2b-be-self-hosted" tabindex="-1">Can E2B be self-hosted? <a class="header-anchor" href="#can-e2b-be-self-hosted" aria-label="Permalink to &quot;Can E2B be self-hosted?&quot;">&ZeroWidthSpace;</a></h3>
<p>Yes. E2B's runtime is open source (Apache-2.0) and can be self-hosted on one machine; deploying into your own cloud account is an enterprise feature. Alternatively, OpenDsec's E2B gateway serves the API the E2B SDK calls on your own machine or cluster.</p>
<h3 id="do-i-need-to-change-my-e2b-code-to-use-opendsec" tabindex="-1">Do I need to change my E2B code to use OpenDsec? <a class="header-anchor" href="#do-i-need-to-change-my-e2b-code-to-use-opendsec" aria-label="Permalink to &quot;Do I need to change my E2B code to use OpenDsec?&quot;">&ZeroWidthSpace;</a></h3>
<p>No. Set <code>E2B_API_URL</code>, <code>E2B_SANDBOX_URL</code> and <code>E2B_API_KEY</code> to point the SDK at the gateway. Features the gateway lacks, such as directory watching, return a clear error.</p>
<h3 id="does-it-support-e2b-s-code-interpreter" tabindex="-1">Does it support E2B's code interpreter? <a class="header-anchor" href="#does-it-support-e2b-s-code-interpreter" aria-label="Permalink to &quot;Does it support E2B's code interpreter?&quot;">&ZeroWidthSpace;</a></h3>
<p>Yes. Build the template with <code>deploy/e2b/build-code-interpreter.py</code> and <code>run_code</code> works, with results, logs, errors, charts and pandas tables. Only the Python kernel for now.</p>
<h3 id="does-it-work-on-a-cloud-vm-without-kvm" tabindex="-1">Does it work on a cloud VM without KVM? <a class="header-anchor" href="#does-it-work-on-a-cloud-vm-without-kvm" aria-label="Permalink to &quot;Does it work on a cloud VM without KVM?&quot;">&ZeroWidthSpace;</a></h3>
<p>Yes. Without <code>/dev/kvm</code> the install sets up containers only, and the gateway's <code>base</code> template uses the container backend. Fork and snapshots then keep only the file system, and the code interpreter template needs microVMs.</p>
<h3 id="can-it-run-fully-offline" tabindex="-1">Can it run fully offline? <a class="header-anchor" href="#can-it-run-fully-offline" aria-label="Permalink to &quot;Can it run fully offline?&quot;">&ZeroWidthSpace;</a></h3>
<p>Yes. OpenDsec has an offline bundle (<code>deploy/offline</code>); we tested installing it and using the E2B SDK on a VM with no internet access. See <a href="/en/docs/install.html">Install</a>.</p>
<h3 id="what-does-self-hosting-cost" tabindex="-1">What does self-hosting cost? <a class="header-anchor" href="#what-does-self-hosting-cost" aria-label="Permalink to &quot;What does self-hosting cost?&quot;">&ZeroWidthSpace;</a></h3>
<p>OpenDsec is free, open-source software (server AGPL-3.0, SDKs Apache-2.0); you only need your own machines.</p>
<h2 id="further-reading" tabindex="-1">Further reading <a class="header-anchor" href="#further-reading" aria-label="Permalink to &quot;Further reading&quot;">&ZeroWidthSpace;</a></h2>
<ul>
<li><a href="/en/docs/e2b.html">E2B compatibility: what works and what differs</a></li>
<li><a href="/en/docs/quickstart.html">Quickstart</a></li>
<li><a href="/en/docs/cluster.html">Multi-machine cluster deployment</a></li>
<li><a href="/en/blog/posts/dsec-paper.html">DeepSeek's DSec paper</a>: where OpenDsec's design comes from</li>
</ul>
<p><em>OpenDsec is not affiliated with E2B; it implements the API the E2B SDK calls. What this post says about E2B comes from its documentation and repositories, checked on 2026-10-25.</em></p>
]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Acceptance-testing the open-source release on fresh machines: 22 issues]]></title>
            <link>https://kernflux.github.io/en/blog/posts/fresh-machine-acceptance</link>
            <guid>https://kernflux.github.io/en/blog/posts/fresh-machine-acceptance</guid>
            <pubDate>Sat, 24 Oct 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[Before the release we followed the README on fresh Ubuntu 24.04 VMs through every feature a user would touch, and found and fixed 22 issues, two of them serious.]]></description>
            <content:encoded><![CDATA[<h1 id="acceptance-testing-the-open-source-release-on-fresh-machines-22-issues" tabindex="-1">Acceptance-testing the open-source release on fresh machines: 22 issues <a class="header-anchor" href="#acceptance-testing-the-open-source-release-on-fresh-machines-22-issues" aria-label="Permalink to &quot;Acceptance-testing the open-source release on fresh machines: 22 issues&quot;">&ZeroWidthSpace;</a></h1>
<p>OpenDsec's automated tests always ran, and passed, on our development machine and our production cluster. But the development machine has everything installed, and the production cluster was tuned step by step. A new user starts from a fresh machine and a README. So before the release we took a new user's place: fresh Ubuntu 24.04 VMs, the documentation, and every feature a user might touch.</p>
<h2 id="what-we-tested" tabindex="-1">What we tested <a class="header-anchor" href="#what-we-tested" aria-label="Permalink to &quot;What we tested&quot;">&ZeroWidthSpace;</a></h2>
<table tabindex="0">
<thead>
<tr>
<th>Area</th>
<th>How</th>
</tr>
</thead>
<tbody>
<tr>
<td>Single machine</td>
<td>Install per the README; run the examples, the SDK feature test and 54 admin command checks; reboot; uninstall</td>
</tr>
<tr>
<td>No KVM</td>
<td>Containers only</td>
</tr>
<tr>
<td>E2B</td>
<td>E2B's Python SDK (sync and async), JS SDK, code interpreter, snapshots and network options</td>
</tr>
<tr>
<td>Offline</td>
<td>Install from the offline bundle on a VM without internet; use the E2B SDK offline</td>
</tr>
<tr>
<td>Cluster</td>
<td>3 control and 3 compute nodes with NFS and HAProxy: deploy per the docs, hand over to fleet, add a node, rolling upgrade, config change, maintenance, backup, monitoring</td>
</tr>
<tr>
<td>Failures</td>
<td>Hard-reset a control node and kill the IAM leader under load</td>
</tr>
<tr>
<td>Kubernetes</td>
<td>Single-node k3s</td>
</tr>
</tbody>
</table>
<h2 id="two-serious-issues" tabindex="-1">Two serious issues <a class="header-anchor" href="#two-serious-issues" aria-label="Permalink to &quot;Two serious issues&quot;">&ZeroWidthSpace;</a></h2>
<p><strong>On a current Ubuntu kernel, every microVM resume failed.</strong> Ubuntu 24.04's 6.8.0-147 kernel kills a process placed with <code>CLONE_INTO_CGROUP</code> into a cgroup that has ever been through <code>cgroup.kill</code>. OpenDsec ends the VM process on pause and starts a new one in the same cgroup on resume, so every resume failed. Our production machines run an older kernel, which is why we never saw it; a kernel upgrade would have exposed it. Killing now freezes the cgroup and sends SIGKILL to each process instead of using <code>cgroup.kill</code>, and a regression test fails on the affected kernel.</p>
<p><strong>Restarting a control node failed a few calls with <code>unknown edge</code>.</strong> After a restart the watcher only learned about compute nodes when they re-registered, up to 10 s later, and meanwhile the apiserver treated them as gone. A hard reset of one control node failed 3 of 3,911 calls in a load test. The watcher now starts from the nodes its peers know, and the apiserver keeps a node it briefly cannot see for 2 minutes. The same hard reset now fails none.</p>
<h2 id="the-rest" tabindex="-1">The rest <a class="header-anchor" href="#the-rest" aria-label="Permalink to &quot;The rest&quot;">&ZeroWidthSpace;</a></h2>
<p>Most of the other 20 are things a new user hits on day one:</p>
<ul>
<li><strong>Install</strong>: fresh machines lack <code>make</code>, and the script only failed halfway through the build; fresh Ubuntu lacks <code>python3-venv</code>; fresh cloud images have no package lists until <code>apt update</code>.</li>
<li><strong>CLI</strong>: <code>opendsecctl exec</code> dropped argument quoting, breaking <code>python3 -c '...'</code>; <code>--help</code> did not work; batch create printed no IDs; importing a missing local file went to Docker Hub.</li>
<li><strong>E2B</strong>: a single-machine install had no E2B gateway, and on clusters that deny internet by default E2B sandboxes had none, while E2B allows it by default.</li>
<li><strong>Operations</strong>: <code>opendsecops restart</code> skipped the E2B gateway; the disk alert used a fixed threshold and fired on every machine under 200 GB; on Kubernetes the apiserver could not write the image store.</li>
<li><strong>Performance</strong>: the NFS 4.1 we recommended cannot find holes in sparse files, so creating from a memory snapshot on another node read the whole memory file and took 141 s; with 4.2 it takes 62 s. The docs now say 4.2, and <code>store-check</code> warns about 4.1.</li>
</ul>
<p>Each is listed in the <a href="https://github.com/Kernflux/opendsec/blob/main/CHANGELOG.md" target="_blank" rel="noreferrer">CHANGELOG</a>.</p>
<h2 id="not-covered" tabindex="-1">Not covered <a class="header-anchor" href="#not-covered" aria-label="Permalink to &quot;Not covered&quot;">&ZeroWidthSpace;</a></h2>
<p>Real GPUs, ARM machines, multi-node Kubernetes, and distributions other than Ubuntu 24.04. If you run OpenDsec on one of these and something breaks, please <a href="https://github.com/Kernflux/opendsec/issues" target="_blank" rel="noreferrer">open an issue</a>.</p>
<h2 id="takeaway" tabindex="-1">Takeaway <a class="header-anchor" href="#takeaway" aria-label="Permalink to &quot;Takeaway&quot;">&ZeroWidthSpace;</a></h2>
<p>A full test run on a development machine shows the code is right on our machines. It does not show a user can get it working from the docs. Both serious issues came from the environment: a newer kernel, and restart timing that production never produced. Walking through the docs on a fresh machine finds problems a development machine never will.</p>
]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[OpenDsec is open source]]></title>
            <link>https://kernflux.github.io/en/blog/posts/opendsec-open-source</link>
            <guid>https://kernflux.github.io/en/blog/posts/opendsec-open-source</guid>
            <pubDate>Sat, 24 Oct 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[Sandbox infrastructure built from DeepSeek's DSec paper, now open source under AGPL-3.0, with Firecracker microVMs, fork with memory, task-level pause and an E2B-compatible API.]]></description>
            <content:encoded><![CDATA[<h1 id="opendsec-is-open-source" tabindex="-1">OpenDsec is open source <a class="header-anchor" href="#opendsec-is-open-source" aria-label="Permalink to &quot;OpenDsec is open source&quot;">&ZeroWidthSpace;</a></h1>
<p>Today we published the OpenDsec source on <a href="https://github.com/Kernflux/opendsec" target="_blank" rel="noreferrer">GitHub</a>. OpenDsec is sandbox infrastructure for reinforcement-learning training and code-executing agents: it creates, pauses, copies and destroys isolated environments in bulk, on your own machines.</p>
<h2 id="why" tabindex="-1">Why <a class="header-anchor" href="#why" aria-label="Permalink to &quot;Why&quot;">&ZeroWidthSpace;</a></h2>
<p>Agentic RL training runs thousands of rollouts at once, and each needs an isolated environment that can install dependencies, run tests and reach the network. Training gets preempted, so environments must pause as a batch. The same state gets explored several ways, so environments must be copied together with their memory.</p>
<p>DeepSeek described the system they built for this in <a href="https://arxiv.org/abs/2609.22978" target="_blank" rel="noreferrer"><em>DeepSeek Elastic Compute (DSec)</em></a>. OpenDsec follows that paper: a stateless apiserver, IAM with nested projects and quotas, a watcher that aggregates scheduling state, power-of-k placement with final admission on each node, layered images, a default-deny network policy and task-level pause. Where the paper is silent we made our own choices, and the <a href="/docs/spec.html">SPEC</a> in the repository marks which design points come from the paper and which are ours.</p>
<p>OpenDsec is an independent project. It is not affiliated with DeepSeek and contains none of its code.</p>
<h2 id="what-it-does" tabindex="-1">What it does <a class="header-anchor" href="#what-it-does" aria-label="Permalink to &quot;What it does&quot;">&ZeroWidthSpace;</a></h2>
<ul>
<li><strong>Fast</strong>: a Firecracker microVM cold-starts in about 0.1 s, is created from a memory snapshot in about 55 ms and resumes from a pause in about 50 ms.</li>
<li><strong>Fork</strong>: <code>fork(count=N)</code> copies a running VM with its memory while pausing the source for about 10 ms. Copies on one node share memory pages; 64 of them restore concurrently in about 0.2 s.</li>
<li><strong>Scale</strong>: one 384-thread node took a burst of 5120 microVMs in 37 s.</li>
<li><strong>E2B compatible</strong>: code written for the E2B SDK runs on your machines after changing three environment variables. E2B's own Python and JS SDK acceptance checks all pass.</li>
<li><strong>Operable</strong>: mTLS between components and a Raft-replicated IAM; staged fleet rollouts with automatic rollback; offline installs. Upgrading any component leaves running sandboxes alone.</li>
</ul>
<p>These numbers come from our own bare-metal machines; the SPEC says how each was measured.</p>
<h2 id="try-it" tabindex="-1">Try it <a class="header-anchor" href="#try-it" aria-label="Permalink to &quot;Try it&quot;">&ZeroWidthSpace;</a></h2>
<p>One Linux machine with Ubuntu 24.04, about 10 minutes:</p>
<div class="language-sh vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">sh</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">sudo</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> -i</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">apt</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> update</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> &#x26;&#x26; </span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">apt</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> install</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> -y</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> make</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> git</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> python3-venv</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">curl</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> -fsSL</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> https://go.dev/dl/go1.27.3.linux-amd64.tar.gz</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> |</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> tar</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> -C</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> /usr/local</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> -xz</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">git</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> clone</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> https://github.com/kernflux/opendsec</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> &#x26;&#x26; </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">cd</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> opendsec</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">deploy/quickstart.sh</span></span></code></pre>
</div><p>See <a href="/en/docs/quickstart.html">Get started</a> for the details, and <a href="/en/docs/deploy.html">Deployment</a> for clusters and offline installs.</p>
<h2 id="license" tabindex="-1">License <a class="header-anchor" href="#license" aria-label="Permalink to &quot;License&quot;">&ZeroWidthSpace;</a></h2>
<p>The servers are AGPL-3.0. The Python SDK, the Go client and the API definitions are Apache-2.0, so programs that call OpenDsec are not bound by the AGPL. A <a href="/en/docs/license.html">commercial license</a> covers closed modifications, offering OpenDsec as a service without publishing changes, and the enterprise edition's single sign-on and audit export.</p>
<h2 id="what-s-next" tabindex="-1">What's next <a class="header-anchor" href="#what-s-next" aria-label="Permalink to &quot;What's next&quot;">&ZeroWidthSpace;</a></h2>
<p>OpenDsec is at 0.x, and interfaces may change before 1.0. Not yet verified: distributions other than Ubuntu 24.04, real GPUs, and multi-node Kubernetes. Issues and pull requests are welcome on GitHub.</p>
]]></content:encoded>
        </item>
    </channel>
</rss>