Skip to content
LogoLogo

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:

SurfaceWhat moves into a microVMHow you turn it on
Sandbox backendThe Python Interpreter node's codeTwo environment variables
CreateOS Sandbox componentWhatever you put in the nodeDrag it onto the canvas
Whole-flow executorAn entire flow graphOne 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/kvm and 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 play

Its 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.

A Python Interpreter node runs its code on a microVM, and the output reports the guest it landed on

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 runsThe Langflow processA microVM on the Langflow hostA microVM on CreateOS
Needs a local hypervisorNoYesNo
IsolationNoneHardware-virtualizedHardware-virtualized
Cold startNot applicableSeconds, longer on first runAbout 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-createos

Install 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=createos

The 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:

SettingDefaultWhat it does here
LANGFLOW_SANDBOX_TIMEOUT_SECONDS30Wall clock for the guest program, enforced inside the VM
LANGFLOW_SANDBOX_MEMORY_MB192A floor, not the VM size. See below
LANGFLOW_SANDBOX_ALLOW_NETWORKfalsefalse blocks egress; true is completely unrestricted
LANGFLOW_SANDBOX_ALLOWED_DOMAINSemptyRefused. Setting it makes every run fail

Plus the plugin's own:

VariableDefaultWhat it does
CREATEOS_SANDBOX_API_KEYnoneRequired. Falls back to CREATEOS_API_KEY
CREATEOS_SANDBOX_BASE_URLhttps://api.sb.createos.shControl-plane endpoint
CREATEOS_SANDBOX_ROOTFSdevbox:1Guest image
CREATEOS_SANDBOX_SHAPEautoPin an exact shape instead of letting it choose
LANGFLOW_SANDBOX_CREATEOS_ACCEPT_EGRESS_EXCEPTIONSfalseNeeded 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.

InputDefaultWhat it does
Python Codeprint('hello from a microVM')Python to run. Ignored when Command is set
CommandemptyA shell command to run instead of the Python
Guest Reuseoffflow keeps one guest across runs of this component
Return FilesoffRead /workspace/artifacts back after the run
Allow NetworkoffOff blocks egress. On is completely unrestricted
Timeout (seconds)60Wall clock, enforced inside the VM
Shapes-2vcpu-4gbVM size. Falls back to CREATEOS_SANDBOX_SHAPE

It has two outputs. Results carries the outcome:

FieldMeaning
resultThe command's stdout, trimmed
stderr, exit_codeThe rest of the outcome
sandbox_idThe guest it ran on
reused_guestWhether this run adopted an existing guest
artifact_countHow 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=createos

It 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.

VariableDefaultWhat it does
CREATEOS_EXECUTOR_ROOTFSimage defaultThe supported path. A template with Langflow and your components already installed
CREATEOS_EXECUTOR_SHAPEs-4vcpu-8gbGuest size
CREATEOS_EXECUTOR_TIMEOUT_SECONDS600Wall clock, enforced inside the VM
CREATEOS_EXECUTOR_EGRESS*Comma-separated address rules. Open by default, because flows call model providers
CREATEOS_EXECUTOR_INSTALL_LFXfalseInstall dependencies per run instead. For a first experiment, not for production
CREATEOS_EXECUTOR_PIPemptyExtra 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=true

Someone 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