Langflow
Langflow builds AI workflows on a canvas, and some of those nodes run Python. The CreateOS plugin moves that code onto a disposable Linux microVM.
One pip install ships three separate surfaces. They are complementary rather than alternatives, and each is switched on by itself:
| Surface | What moves into a microVM | How you turn it on |
|---|---|---|
| Sandbox backend | The Python Interpreter node's code | Two environment variables |
| CreateOS Sandbox component | Whatever you put in the node | Drag it onto the canvas |
| Whole-flow executor | An entire flow graph | One environment variable |
The backend hardens a flow you already have, without changing it. The component makes the sandbox something you build with. The executor moves the whole graph off the host.
At a glance
- Package:
langflow-sandbox-createos(PyPI), Python 3.10 or newer - Auth: a CreateOS API key, sent as
X-Api-Key - Isolation: hardware-virtualized, one throwaway VM per execution
- No local hypervisor needed: the VM runs on CreateOS, not your Langflow host
- Usable on managed platforms: yes, including containers with no
/dev/kvmand Apple Silicon CI
Set it up in six lines. This is the whole setup shown in the video below. Langflow has no prompt-driven builder, so this is a terminal recipe rather than something you paste into the canvas.
pip install langflow-sandbox-createos
export LANGFLOW_SANDBOX_BACKEND_PLUGINS=createos
export LANGFLOW_SANDBOX_BACKEND=createos
export CREATEOS_SANDBOX_API_KEY='your-key-here'
langflow run
# then drop a Python Interpreter node on the canvas and press playIts code now runs on a microVM instead of in the Langflow process, with nothing on the canvas changed. The rest of this page explains the other two surfaces and the settings.
Why not just use the built-in sandbox
Langflow ships exec-sandbox, which boots a QEMU microVM on the Langflow host. That works, but it needs a hypervisor device on that host. Managed platforms, most containers, and Apple Silicon CI do not have one.
none (default) | exec-sandbox (built in) | createos | |
|---|---|---|---|
| Where code runs | The Langflow process | A microVM on the Langflow host | A microVM on CreateOS |
| Needs a local hypervisor | No | Yes | No |
| Isolation | None | Hardware-virtualized | Hardware-virtualized |
| Cold start | Not applicable | Seconds, longer on first run | About 200 ms to first command |
The trade runs the other way on network rules: exec-sandbox filters DNS inside the guest and can enforce a domain allowlist. CreateOS cannot. See Network rules before you rely on one.
Install
pip install langflow-sandbox-createosInstall it into the same environment Langflow runs in. Then set your API key, which every surface uses:
export CREATEOS_SANDBOX_API_KEY='...'Get one at createos.sh/app/profile. If that variable is unset, the plugin falls back to CREATEOS_API_KEY.
The sandbox backend
This is the quiet one. Switch it on and the Python Interpreter node's code stops running in the Langflow process and starts running on a microVM. Nothing on the canvas changes.
It takes two variables, not one:
export LANGFLOW_SANDBOX_BACKEND_PLUGINS=createos
export LANGFLOW_SANDBOX_BACKEND=createosThe first one is Langflow's trust gate. Loading a sandbox plugin imports its code into the Langflow process, on the very path that decides whether user code is isolated, so discovery is never automatic. The second selects it.
A backend that cannot do its job refuses to run. It never quietly falls back to running the code in the Langflow process. If you see runs failing, that is the design working, not the plugin breaking.
What it reads
Langflow's own sandbox settings apply, with two caveats:
| Setting | Default | What it does here |
|---|---|---|
LANGFLOW_SANDBOX_TIMEOUT_SECONDS | 30 | Wall clock for the guest program, enforced inside the VM |
LANGFLOW_SANDBOX_MEMORY_MB | 192 | A floor, not the VM size. See below |
LANGFLOW_SANDBOX_ALLOW_NETWORK | false | false blocks egress; true is completely unrestricted |
LANGFLOW_SANDBOX_ALLOWED_DOMAINS | empty | Refused. Setting it makes every run fail |
Plus the plugin's own:
| Variable | Default | What it does |
|---|---|---|
CREATEOS_SANDBOX_API_KEY | none | Required. Falls back to CREATEOS_API_KEY |
CREATEOS_SANDBOX_BASE_URL | https://api.sb.createos.sh | Control-plane endpoint |
CREATEOS_SANDBOX_ROOTFS | devbox:1 | Guest image |
CREATEOS_SANDBOX_SHAPE | auto | Pin an exact shape instead of letting it choose |
LANGFLOW_SANDBOX_CREATEOS_ACCEPT_EGRESS_EXCEPTIONS | false | Needed to run with egress off. Read Network rules first |
About memory and shapes
A CreateOS shape fixes vCPU and memory together, so LANGFLOW_SANDBOX_MEMORY_MB cannot express one on its own. Langflow's default of 192 MB was sized for the local QEMU guest and is far too small for a fresh CreateOS guest importing numpy or pandas.
So the setting acts as a floor. The plugin picks the smallest shape in the catalog with at least max(LANGFLOW_SANDBOX_MEMORY_MB, 4096) MiB. It never rounds down, because a shape below the configured memory would produce out-of-memory kills that read like bugs in your own code. Pin an exact shape with CREATEOS_SANDBOX_SHAPE if you would rather choose yourself; a pinned shape smaller than the floor is refused rather than accepted.
What the backend cannot do
The backend protocol in Langflow carries only "run this code, give me stdout, stderr and an exit code". There is nowhere in it to return a reused guest or the files that guest wrote, so the backend does neither. Both of those live in the component below.
The CreateOS Sandbox component
A microVM as a node you wire up yourself. Search the component list for CreateOS Sandbox and drag it on.
| Input | Default | What it does |
|---|---|---|
| Python Code | print('hello from a microVM') | Python to run. Ignored when Command is set |
| Command | empty | A shell command to run instead of the Python |
| Guest Reuse | off | flow keeps one guest across runs of this component |
| Return Files | off | Read /workspace/artifacts back after the run |
| Allow Network | off | Off blocks egress. On is completely unrestricted |
| Timeout (seconds) | 60 | Wall clock, enforced inside the VM |
| Shape | s-2vcpu-4gb | VM size. Falls back to CREATEOS_SANDBOX_SHAPE |
It has two outputs. Results carries the outcome:
| Field | Meaning |
|---|---|
result | The command's stdout, trimmed |
stderr, exit_code | The rest of the outcome |
sandbox_id | The guest it ran on |
reused_guest | Whether this run adopted an existing guest |
artifact_count | How many files came back |
Files carries anything the guest wrote to /workspace/artifacts, as a table with one row per file: the path, the size, and either text or, for anything that is not text, base64. At most 5 MiB and 256 files come back, and the limit applies during the transfer rather than after it, so a runaway program cannot pull an unbounded payload into Langflow first and get stopped afterwards.
Before you turn Guest Reuse on
Reuse keeps one guest alive across runs, so installed packages and files carry over and repeat runs start faster. Four things follow from that, and none of them are bugs:
- The guest outlives the flow run. It is not deleted at the end. Auto-pause bounds the cost but does not remove it, so reap leftover
lf-c-*sandboxes on whatever schedule suits you. - A guest that idled out is resumed first. Any gap between runs longer than the auto-pause window leaves it paused, which is the normal case rather than an edge case. The component resumes it and waits before running.
- Two workers running the same flow share one guest, and therefore one filesystem. That is inherent to reusing a machine, and it is why reuse is off by default.
- Changing Allow Network gets you a different guest, deliberately. The network policy is part of the guest's identity, so a run with egress off can never adopt the guest created while it was on. Without that, turning the setting off would look like it applied and silently do nothing.
The whole-flow executor
This one serializes the entire graph, rebuilds it inside a guest, runs it there, and sends per-vertex events back.
export LANGFLOW_EXECUTOR_KIND=createosIt does not cover the canvas. This is the thing to understand before switching it on. It covers the /api/v1/run endpoint, the lfx command line, and Loop subgraphs. Langflow's UI build endpoint walks the vertices itself and never enters this path, so the playground still runs in the server process. Testing on the canvas and concluding the executor is broken is the easy mistake here.
| Variable | Default | What it does |
|---|---|---|
CREATEOS_EXECUTOR_ROOTFS | image default | The supported path. A template with Langflow and your components already installed |
CREATEOS_EXECUTOR_SHAPE | s-4vcpu-8gb | Guest size |
CREATEOS_EXECUTOR_TIMEOUT_SECONDS | 600 | Wall clock, enforced inside the VM |
CREATEOS_EXECUTOR_EGRESS | * | Comma-separated address rules. Open by default, because flows call model providers |
CREATEOS_EXECUTOR_INSTALL_LFX | false | Install dependencies per run instead. For a first experiment, not for production |
CREATEOS_EXECUTOR_PIP | empty | Extra packages for that fallback |
The guest needs every dependency your components use. A guest with bare lfx and nothing else will rebuild a flow correctly and then fail on every node with a missing-module error. Build a template image; the per-run install is a convenience for trying it out, and it cannot reliably tell you when the install itself failed.
Two more things worth knowing. Results are not live: events arrive only once the guest command finishes, so a long flow shows nothing until it completes. And when a run fails inside the guest, the stream raises instead of ending normally, so anything expecting a completion event on every run will not get one.
Network rules
This is the part that differs most from the built-in sandbox, and all three differences matter.
Domain allowlists are refused, not honoured. CreateOS does not enforce hostname rules. Its API accepts them and echoes them back, so an allowlist looks applied while restricting nothing. Rather than let that stand, the plugin reports that it cannot do domain allowlists and refuses any run that asks for one. Only address and CIDR rules reach the real policy.
Resolving your domains to addresses yourself is not a fix either. DNS answers rotate, the guest resolves independently, and CDN addresses are shared, so allowlisting one hostname's address would admit every other tenant sitting behind it.
Turning the network on means completely on. There is no package-registry-only default the way exec-sandbox has one.
Turning it off still leaves one range reachable. The 169.254.0.0/16 link-local range, which carries the VM metadata service, is accepted before the deny-all rule applies. Because of that, the backend refuses to run with egress off until you acknowledge it:
export LANGFLOW_SANDBOX_CREATEOS_ACCEPT_EGRESS_EXCEPTIONS=trueSomeone who turned the network off did not ask for "off except one range", so the refusal is the honest default rather than a silent compromise. If you need a real domain allowlist today, use exec-sandbox, which filters DNS inside the guest.
Note that the executor defaults the opposite way to the backend: open, with no refusal and no warning. Do not assume the plugin locks the network down everywhere.
How one execution works
POST /v1/sandboxes shape, image, environment, egress, auto-pause
PUT /v1/sandboxes/{id}/files?path=... the program
POST /v1/sandboxes/{id}/exec run it under a timeout
DELETE /v1/sandboxes/{id} always, even when the run failed
The guest's own timeout is the authoritative clock, so a run that times out still returns whatever output it produced. Deleting the VM is what actually stops runaway code. A VM the process fails to delete is bounded by auto-pause, which is always set longer than a single execution.
There are no retries anywhere. A rate-limit or server error from the control plane surfaces as a failed run rather than being retried for you.
See also
- Integrations overview: how this compares to the agent integrations
- REST API: the endpoints the plugin calls
- Concepts: sandboxes, networks, ingress, snapshots
- Limits and defaults: shapes, disk sizes, and quotas