Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

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 provideVersionWithout it
PostgreSQL18 or laterThe runtime will not start. There is no other database option.
An OCI engineanyYou build from source instead; the image is the supported path.
An OIDC providerany, by discoveryEvery request is anonymous and every bearer token is rejected.
An OTLP endpointany collectorNo 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

PortWhatExpose it?
8080End-user traffic. Authenticated.Yes
9090Health, 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