Deployment
You run the runtime; somebody else writes the applications. This section is about the process, not the model.
What you need
Four things, and only the first is required to start:
| You provide | Version | Without it |
|---|---|---|
| PostgreSQL | 18 or later | The runtime will not start. There is no other database option. |
| An OCI engine | any | You build from source instead; the image is the supported path. |
| An OIDC provider | any, by discovery | Every request is anonymous and every bearer token is rejected. |
| An OTLP endpoint | any collector | No traces, metrics or logs leave the process. Not a start-up failure. |
PostgreSQL 18 is a real floor, not a recommendation — the runtime provisions and migrates schemas itself and reads the catalogue back to check its work, and it uses what 18 provides to do it. Check before you plan a deployment; it is newer than what many organisations run by default.
Your identity provider is yours. The runtime speaks OIDC discovery and holds
no vendor’s SDK, so Keycloak, Entra ID, Auth0, Okta and anything else that
publishes a JWKS all work the same way. It never issues a token and has no user
database. Until RUNTIME_OIDC_ISSUER is set, every caller is the model’s
anonymous role — which is fine for a first look and is not a way to run
anything.
Your telemetry backend is yours too. The runtime emits OTLP and nothing else: no Prometheus endpoint to scrape, no vendor agent, no exporter to choose. Point it at a collector and fan out from there. A collector it cannot reach is deliberately not a start-up error — see Observability.
What it is built on
Rust, compiled to one static-ish binary in a distroless image. That matters to you in three ways and no others: there is no runtime to install and no interpreter to patch separately, the container needs no shell and runs as non-root with a read-only filesystem, and memory use is flat and predictable enough that you can size a replica from one load test.
Application logic — flows — is Lua 5.4, sandboxed, with an instruction budget and a memory cap. That is a thing your developers will ask about; the short answer is that it is the only language today, a WebAssembly engine is designed for and not built, and a flow runs with the permissions of whoever called it and cannot escalate.
Beyond that, the library list is not something this documentation publishes. Naming them would imply a compatibility promise the project does not make, and none of them change what you have to run or configure.
What you are running
One stateless binary in one container image, plus PostgreSQL. The image contains no application — the runtime is an interpreter, so the model is mounted or fetched at start-up. One image serves every application, which makes a CVE in a dependency one rebuild rather than one per customer.
It writes nothing to its filesystem. Run it read-only:
podman run --read-only --cap-drop=ALL --security-opt=no-new-privileges \
-v ./myapp:/model:ro \
-e RUNTIME_DATABASE_URL=postgres://... \
-e RUNTIME_ADMIN_ADDR=0.0.0.0:9090 \
runtime:dev serve /model
There is no CMD in the image: serve needs a model and there is no sensible
default for which one. A container started with no arguments says so rather than
failing somewhere less obvious.
With compose
The deployment repository has compose files that run the whole thing, on any
OCI engine:
make serve # build the image, start Postgres, the runtime, and observability
make apply # provision the database from the model
make logs # follow the runtime
make down # stop
Two compose files rather than one with profiles, because profiles are not supported identically across engines and “works with any container engine” is the point.
Two ports, and only one is yours to route
| Port | What | Expose it? |
|---|---|---|
8080 | End-user traffic. Authenticated. | Yes |
9090 | Health, introspection, reload. No auth. | No |
The admin port carries no authentication, and the only thing making that acceptable is that nothing routes to it. Do not put it behind an Ingress. It defaults to loopback for exactly that reason, and you have to widen it deliberately to run in a container at all.
What is not here yet
A Helm chart, and an operator with CRDs. Both are planned. What exists is deliberately the simplest thing that genuinely runs, so the Kubernetes story is designed against something working rather than guessed at.
Next
- Configuration — every variable, and the two that matter.
- Securing a deployment — what is yours to handle.
- Health and shutdown — probes, and the drain.
- Observability — what comes out.
- Upgrading — models, schemas, and rolling deploys.