12. Deployment
Deployment shapes covered in this chapter:
- Docker Compose — local or single-host. The fastest way to get the full stack running outside a developer environment. Used regularly; treated as the production-quality shape today.
- Azure ContainerApps via Bicep — preliminary. Templates exist in the repository as a starting point but have not been deployed end-to-end yet. See §12.2 for the caveat in detail.
- Embedding the kernel as a library — for advanced cases where the kernel runs inside another Rust process rather than as a separate gRPC service.
12.1. Docker Compose
The provided docker-compose.yml brings up both services with one command.
Quick start
# Mock LLM (no API key needed)EIGENIUS_MOCK_LLM=true docker compose up --build -d
# Real LLMANTHROPIC_API_KEY=sk-ant-... docker compose up --build -d
# Stopdocker compose downThe just recipes wrap the same:
just up-mock # mockjust up # realjust down # stopService definitions
The compose file declares two services:
- kernel — built from
deploy/Dockerfile.kernel. Exposes port 50051. Depends onorchestratorbeing healthy first. - orchestrator — built from
deploy/Dockerfile.orchestration. Exposes port 8080. ReadsEIGENIUS_MOCK_LLMandANTHROPIC_API_KEYfrom the host environment.
Both have health checks:
| Service | Health check |
|---|---|
| kernel | eigenius --endpoint http://localhost:50051 inspect "urn:eigenius:core:Class" |
| orchestrator | HTTP GET on http://localhost:8080/health |
The kernel waits for the orchestrator’s health check to pass before its own command runs (depends_on.condition: service_healthy).
Adding persistence
By default, the compose file runs the kernel in-memory. To persist across container restarts, mount a volume and add --db to the kernel’s command:
services: kernel: # ... volumes: - ./data:/var/lib/eigenius command: - "serve" - "--port" - "50051" - "--orchestrator" - "http://orchestrator:8080" - "--db" - "/var/lib/eigenius"The ./data host directory is created on first start. On subsequent docker compose up, the kernel rehydrates layers, traces, and capabilities from the persisted RocksDB.
For backups and restoration, use docker compose exec kernel eigenius db export /var/lib/eigenius /tmp/export followed by a docker cp. See chapter 6.
Rebuilding after code changes
Compose caches build layers aggressively. After changes:
# Rebuild the changed servicedocker compose up --build kernel
# Or force a clean build of everythingdocker compose build --no-cachedocker compose up -dBuild time is significant (full Rust workspace + WASM examples). For iterative development, prefer the three-terminal model from chapter 5.
12.2. Azure ContainerApps via Bicep
Status: preliminary, not yet exercised end-to-end. The Bicep templates are committed to the repository as a starting point and are believed to be syntactically correct, but no Eigenius deployment has been validated against a live Azure subscription. Treat the section below as a structural reference, not a runbook. Expect to iterate on sizing, identity, and storage configuration when you stand it up the first time.
The repository ships with Azure Bicep templates for a managed cloud deployment. Files under deploy/bicep/:
deploy/bicep/├── main.bicep orchestrating template├── modules/│ ├── acr.bicep Azure Container Registry│ ├── environment.bicep ContainerApps managed environment│ ├── kernel.bicep kernel ContainerApp│ ├── orchestration.bicep orchestrator ContainerApp│ └── keyvault.bicep Key Vault for secrets└── parameters/ ├── staging.bicepparam staging environment overrides └── production.bicepparam production environment overridesWhat gets provisioned by main.bicep:
| Resource | Purpose |
|---|---|
| Container Registry | Holds the kernel and orchestrator images |
| Key Vault | Stores ANTHROPIC_API_KEY and other secrets |
| ContainerApps managed environment | The host environment for both services |
| Kernel ContainerApp | Runs eigenius serve |
| Orchestration ContainerApp | Runs the Deno orchestrator |
| Managed identities | For ACR pull and Key Vault read access |
Deploying
Prerequisites: an Azure subscription, the az CLI logged in, a target resource group.
# Build and push images to ACRdocker build -t <acr>.azurecr.io/eigenius-kernel:<tag> -f deploy/Dockerfile.kernel .docker build -t <acr>.azurecr.io/eigenius-orchestration:<tag> -f deploy/Dockerfile.orchestration .az acr login --name <acr>docker push <acr>.azurecr.io/eigenius-kernel:<tag>docker push <acr>.azurecr.io/eigenius-orchestration:<tag>
# Deployaz deployment group create \ --resource-group <rg> \ --template-file deploy/bicep/main.bicep \ --parameters @deploy/bicep/parameters/staging.bicepparam \ --parameters imageTag=<tag>The staging.bicepparam and production.bicepparam files in parameters/ carry environment-specific defaults (region, tier sizing, etc.). Customise these for your subscription before the first deploy.
Updating
For container image updates:
# Build + push new imagedocker build -t <acr>.azurecr.io/eigenius-kernel:<new-tag> -f deploy/Dockerfile.kernel .docker push <acr>.azurecr.io/eigenius-kernel:<new-tag>
# Re-run deployment with new tagaz deployment group create \ --resource-group <rg> \ --template-file deploy/bicep/main.bicep \ --parameters @deploy/bicep/parameters/staging.bicepparam \ --parameters imageTag=<new-tag>ContainerApps performs a rolling update — old replicas drain while new ones come up.
Persistent storage in Azure
ContainerApps doesn’t ship native persistent volumes for the ContainerApp workload type. Two options:
- Azure Files volume mount — declare a
Microsoft.App/managedEnvironments/storagesresource backed by an Azure Files share, then mount it into the kernel container at/var/lib/eigenius. Thekernel.bicepmodule includes a hook for this; configure the storage account in your bicepparam. - External managed RocksDB — out of scope for the shipped templates. Run the kernel in stateless mode and persist to a side-car service.
For staging/dev environments, option (1) is the simplest path.
Secret handling
ANTHROPIC_API_KEY (and any other secrets) is stored in Key Vault and exposed to the orchestrator container via a secret reference:
secrets: [ { name: 'anthropic-api-key' keyVaultUrl: '${keyvault.outputs.vaultUri}secrets/anthropic-api-key' identity: 'system' }]env: [ { name: 'ANTHROPIC_API_KEY' secretRef: 'anthropic-api-key' }]The container app’s managed identity must be granted Key Vault Secrets User on the vault — wired up in keyvault.bicep.
Cost considerations
- ContainerApps default-scales to zero when idle if you set
minReplicas: 0. The first request after a quiet period pays a cold-start penalty (10–30 seconds). For most usage, setminReplicas: 1to keep one warm replica. - Persistent storage (Azure Files) is billed separately.
- ACR has tier-based pricing; Standard is sufficient for typical image sizes.
12.3. Embedding the kernel as a library
For consumers that want to embed the kernel directly in another Rust application — without running it as a separate gRPC service — the kernel is published as a Cargo crate.
Add to your Cargo.toml:
[dependencies]eigenius-kernel = { path = "<repo>/kernel" } # or git/version depUse the API directly:
use eigenius_kernel::bootstrap;use eigenius_kernel::layer::LayerBuilder;use eigenius_kernel::ontology::{eigon_json, Iri};use eigenius_kernel::query;use std::sync::Arc;
// Bootstrap the four embedded ontology layerslet bootstrap_chain = bootstrap::bootstrap_layers()?;
// Add a custom layerlet mut builder = LayerBuilder::new("my-layer", Some(bootstrap_chain.clone()));let resources = eigon_json::parse_document(my_json_str)?;for r in resources { builder.add_resource(r)?;}let layer = Arc::new(builder.build()?);
// Run a querylet result = query::execute( "USING \"urn:eigenius:core:Class\" MATCH Class(?c) { short_name: ?n } RETURN [] { name: ?n }", &layer)?;The CLI binary itself is a thin user of this API (cli/src/main.rs). Embedding gives you direct in-process access at the cost of accepting Rust as your application language.
12.4. Running without an orchestrator
For deployments where you only need read-only operations (queries, file inspection, type-check) and no IO components, the orchestrator is unnecessary. Run the kernel without --orchestrator:
eigenius serve --port 50051 --db /var/lib/eigeniusCLI commands load, query, inspect, program-validate continue to work. run works for programs whose bodies don’t dispatch IO components — for example, programs that only manipulate resources structurally without calling CompleteText or CompleteJson.
This deployment shape is suitable for read-heavy workloads (pure-data services), embedded analytics, or institutions that don’t depend on LLM dispatch.
12.5. gRPC clients beyond the CLI
The kernel’s gRPC service (defined in proto/) is consumable by any tonic-compatible Rust client or any standard gRPC client (Python, Go, TypeScript, etc.) generated from the protobuf definitions.
For ad-hoc exploration:
grpcurl -plaintext localhost:50051 listgrpcurl -plaintext -d '{"iri":"urn:eigenius:core:Class"}' \ localhost:50051 eigenius.kernel.EigeniusKernel/InspectFor production clients, generate stubs from the .proto files and call them via your language’s standard gRPC client library.
12.6. Deployment checklist
If you’re deploying to Azure ContainerApps, also see the §12.2 caveat — the templates are a starting point that hasn’t been validated end-to-end. Plan for iteration on the first deploy.
Before going live with a deployment:
- Set
--db <path>and verify backup/restore works (export, restore, query) - Configure the orchestrator with a real
ANTHROPIC_API_KEY(or alternative LLM provider) - Set CPU/memory limits sized for your workload (defaults are dev-sized)
- Set
minReplicasif you don’t want cold starts (Azure) - Configure logging — both kernel and orchestrator log to stdout
- Verify the demo scripts run successfully against the deployed endpoints
- Set up monitoring on
http://<orchestrator>/health - Document the
ANTHROPIC_API_KEYrotation procedure for your environment