Docker deployment and image modes
What it is for
Docker ComposeDocker ComposeThe Docker tool that starts and connects the services listed in one configuration file. runs every installation from one compose.yml. Every component runs in Docker: meshMeshA set of connected Mycelium services that exchange updates and keep the NovusLens log., database, reasoning, projectionProjectionThe regular pass that brings the read model up to date with the canonical log. It runs every 30 seconds by default., web application and optional local models. Operators configure them through an installer that also runs in a containerContainerA running program with its own packaged software and isolated environment, managed by Docker on the host.. The setup guide gives the commands and questions.
NovusLens still maps and advises. Packaging its services does not make it the runtimeRuntimeThe software that runs a model or agent. A model runtime answers model requests; an agent runtime carries out the customer's agent actions. for the customer’s agents.
How it works
| Compose service | Responsibility | Stored data |
|---|---|---|
postgres | PostgresPostgresThe database software that stores the tables read by the NovusLens screens, along with document bodies, access records and protected personnel records. 16 with vector support for the analytical view, evidence, identities and audit. | postgres-data volume. |
mesh-0, mesh-1, mesh-2 | The pinnedPinA recorded choice of one exact dependency version. It prevents an update from changing that dependency without a deliberate decision., patched MyceliumMyceliumThe application infrastructure underneath NovusLens. Its connected services exchange updates and keep the ordered structural log. nodes and canonical commit log. | One volume per node. |
init | Apply migrationsMigrationAn ordered change to a database's tables or rules. Applied migration files are recorded so the same file is not run again., load the GuideGuideThe help pages inside NovusLens. They explain the pages, input formats and tasks in the version you use. They are product instructions, not evidence about the organisation., optionally seed the fictional Retail Lending twinTwinNovusLens’s model of one organisation, built from records about its teams, services and how they rely on one another. It is only as current as those records., and create the first operator. | Writes to Postgres and the mesh; checkpoints on installation-state. |
demo | Optional one-off load of the Meridian reference demo, run by the installer after the application starts. Selected through the demo profile. | Writes to Postgres and the mesh. |
reasoning | Session-scoped tools, answers and service endpointsEndpointA particular address on a service that accepts a defined kind of request.. | Reads the shared stores. |
projector | Materialise log entries and refresh the analytical view. | Writes to Postgres. |
ui | The web application and signed-in service proxy. | Sessions use the configured signing secret. |
ollama | Optional local modelLanguage modelSoftware that produces text from instructions and supplied context. NovusLens checks its answers against the records retrieved for the question. server, selected through the ollama profile. | ollama-data keeps downloaded models. |
installer | Optional guided setup and Docker administration, selected through the installer profile. | Private .novuslens.env and setup journalInstallation journalA saved record of setup steps, their attempts and whether they started, completed or failed. It records progress, not a backup of application data. in the installation directory. |
cli | One-off administration commands using the installation’s configuration, selected through the cli profile. | Uses the configured database and mesh; bind-mounts the installation directory at /installation. |
init waits for Postgres and the three gatewaysGatewayA service that accepts requests and passes them into another system. The Mycelium gateway gives NovusLens access to the mesh. to become healthy. Reasoning and projection wait for successful initialisation. The UI also waits for reasoning health. Services restart with unless-stopped; the initialisation job does not restart itself after failure.
Initialisation does not reset an existing twin. bootstrap.py preserves an existing sign-in passphrase and checks the materialiserMaterialiserThe component that reads recorded changes in order and updates the database. It records its place so it can continue after a restart. cursor when records already exist. A database whose canonical logCanonical logThe permanent history of changes to an organisation’s model. New entries are added at the end. Earlier entries stay when the database is rebuilt. is missing fails rather than being silently reseeded.
The installer records attempts, stage transitions and opaque configuration/build fingerprints in private .novuslens-install-state.json. A lock prevents simultaneous installers. resume checks each image and model, the bootstrap result and current application containers before skipping completed work. Local builds also check source inputs; published modes ignore unrelated host-source edits. In latest mode, resume always pulls images again. Missing or changed outputs rerun the relevant steps.
On a terminal, each long installer step (the image build or pull, init, the Meridian load, up --wait and the Ollama steps) shows its command and its latest output line in one changing status line. The whole output, with secrets removed, goes to .novuslens-install.log in the installation directory. A failed step prints its last 40 lines and names that log.
Bootstrap records each stage and individual fixtureFixtureA prepared set of records used for a test or demonstration. A fixture is sample data, not a live customer's records. record in private /state/bootstrap.json on the installation-state volume. Both journals use atomicAtomic operationAn operation whose changes succeed together or fail together, rather than leaving only some of them applied. writes and synchronise them to disk. They keep no raw credentials. Bootstrap replays the existing log before adding missing records after interruption, preserves later edits once the fixture is complete, and repairs derived analysis stages.
Bootstrap’s --status proof requires a completed current release/configuration fingerprint and actual schema, Guide, operator, canonical anchors and verified derived outcomes. A successful calculation may produce no CPLCPLCritical path length. The total coordination weight of the longest chain of dependencies in the model. NovusLens reports it as index points, not elapsed time. value. Status accepts that recorded outcome only while its inputs match; changed inputs require recalculation. The installer checks that proof when resuming. A journal entry alone does not establish readiness. If the bootstrap journal is missing while existing records remain, initialisation refuses ambiguous reseeding. Keep the surviving stores and diagnose the missing state.
Interfaces and network
Only the UI publishes a host port, on 127.0.0.1, default 3111. Postgres, mesh gateways and gossipGossipA way for connected services to pass updates to one another until the updates spread through the group. traffic stay within Docker’s internal application network. Reasoning, projection and the UI also join a network that permits external model requests.
The gateways still require MYCELIUM_GATEWAY_TOKEN. The UI still sends CONWAY_SERVICE_TOKEN to reasoning and supplies the signed-in viewer. All application services use CONWAY_PROFILE=customer. The Docker network does not replace those application checks.
The model endpoint must end in /v1/chat/completions. The optional local container uses http://ollama:11434/v1/chat/completions. OpenRouter uses https://openrouter.ai/api/v1/chat/completions with the key saved as CONWAY_LLM_API_KEY. An external provider uses its configured endpoint. The installer can also configure no model server for a browsing and import demo.
The installer mounts the host Docker socket and the installation directory at its absolute host path. It therefore controls the host Docker engine. The bootstrap wrapper can start it before .novuslens.env exists; the Compose installer profile is for an already configured installation.
Run an administration command with its verb. ./lens graph --help lists the verbs:
./lens graph create-viewer --help./lens run -- <command> runs any other command through the configured Compose cli service.
The installer passes arguments after -- literally to the Compose command service, without shell evaluation.
It selects --user with the installation directory owner’s user and group identifiers, so generated files retain that ownership.
The service works in /installation, a bind mount of the installation directory for relative input and output paths.
Its application environment sets CONWAY_REASONING_URL=http://reasoning:8500 and uses the configured database and mesh.
Compose runs it with --rm --no-deps -T. It does not start missing dependencies; prepare the installation first.
Images and release selection
| Image | Build input |
|---|---|
novuslens-mycelium | Shared operating system, core utilities and pinned, patched Mycelium. Built first and inherited by the five application images below. |
novuslens-python | Python workspace applications, locked dependencies, vendored SDKSDKSoftware development kit: a package of code that helps another program use a service. NovusLens uses mycelium-py to talk to Mycelium. wheelWheelA packaged Python library ready to install. A vendored wheel is stored in this repository so its exact bytes can be checked., migrations and configuration. Shared by init, reasoning and projector. |
novuslens-sidecar | Mycelium at config/mycelium.pin, with NovusLens’s gateway patch. |
novuslens-ui | The production Next.jsNext.jsThe web framework used to build and run the NovusLens interface. It provides pages, server code and request handlers. application and its required static assets and reference documents. |
novuslens-installer | The standalone Python installer, Rich and the Docker command-line tools. |
novuslens-ops | The operational toolchain and frozen application source used by project commands. |
For published images, copy only lens into a new installation directory and run chmod +x lens.
Run ./lens setup; a new installation pulls the release that lens was built for. The lens file on main is built for latest.
Use --init dev, --init local or --init ghcr:<tag> to select another mode or release.
The dev and local modes require a source checkout.
| Mode | Source and image behaviour |
|---|---|
dev | Bind-mount live source. The installer uses the host’s compose.yml and compose.dev.yml; Python reads live source and the UI runs the Next.js development server. |
local | Build frozen local images from the supplied source. |
latest | Pull the rolling GHCRGitHub Container RegistryGitHub’s service for storing and downloading packaged applications used by Docker. images. |
0.x.y | Pull an immutable GHCR release, for example 0.1.0. |
For local, latest and 0.x.y, the installer uses its own release files at /opt/installer/release.
Host source changes do not replace those packaged Compose files.
Setup uses its default or selected release without an image-release prompt. Flags may precede or follow the command, before the literal -- separator.
The installation records its release in .novuslens.env.
Later ./lens start, ./lens resume, ./lens status and administration commands read that record.
Use ./lens version to inspect the saved selection.
Configuration choices
The first-run setup guide explains Docker setup and model choices. Select the image mode when initialising the installation.
Run ./lens configure --init local for a frozen local installation. Decline reuse to edit saved choices, or run ./lens setup --interview to answer every question again. Keep the existing project name, identity saltIdentity saltA secret value added before employee identifiers are turned into fingerprints. Keep the same value so later imports can still match existing people. and mesh addresses.
- Published release: run
./lens setupfor the release thatlenswas built for, or use--init ghcr:0.1.0for an immutable release. Before pulling the private installer, setup asks for a GitHub login and a hidden personal access token withread:packagespermission. The token is not stored in.novuslens.env. The installer keeps its registry login separately in the private.novuslens-registry.jsonfile for later deploys and resumes. Unattended--defaultsruns requireGHCR_TOKENin the environment. Later commands reuse a cached installer; a separate deployment can request registry access again. - OpenRouter models:
openrouteris the default choice. Its models run on OpenRouter, outside this host. The default model isopenai/gpt-6-luna, and setup asks for the Build and Compass model names. Setup asks for an OpenRouter APIAPIApplication programming interface: a defined way for one program to request data or actions from another. key and refuses to save the choice without one; create a key at https://openrouter.ai/settings/keys . An unattended--defaultsrun reads it fromOPENROUTER_API_KEY. The key is saved in the private.novuslens.env. - External models: choose
externalfor an existing OpenAI-compatible provider. Enter its/v1/chat/completionsendpoint and the Build and Compass model names. Container loopbackLoopbackA network address that refers to the same host, such as 127.0.0.1. Other hosts cannot reach a service through that address. addresses refer to the container itself and are refused. - Local models: choose
localfor the Ollama container. The installer downloads the selected models before starting the application. The defaults aregpt-oss:20bandqwen3:30b. Check the model server’s memory and disk requirements before selecting this option. - Meridian demo: choose
meridianto load the fictional Meridian GridMeridian GridThe fictional demonstration organisation used by the demonstration walk and its sample data. Nothing about Meridian Grid describes a real company. demonstration. It needs a model server, because its build agent interviews through it. The installer loads it after the application starts and skips a load that already finished. - Docker socket: the wrapper uses
/var/run/docker.sock. If the engine uses another local socket, setNOVUSLENS_DOCKER_SOCKETto its path before running the wrapper. - Live development: run
./lens start --init devto use the source-mounted development overlay.
If a model requires a newer Ollama version
If a model download reports HTTPHTTPHypertext Transfer Protocol: the rules a browser or program uses to send a request to a web service and receive its response. 412 and asks for newer Ollama, check the selected mode first.
In dev, open the installation’s compose.yml and find ollama under services.
The current pin is:
image: ollama/ollama:0.40.4For dev, save the installation’s Compose change and run ./lens resume --init dev.
Frozen and published modes use packaged Compose files; select a rebuilt local image or a corrected published release instead of editing host Compose.
The installer recreates the model server when its selected image changes, retaining model storage, saved choices and application records.
Wait for the installer to print the sign-in address. Run ./lens status; it uses the saved mode. Each model:<name> row and the application row must show completed.
Open the printed sign-in address and use your saved operator login and passphrase.
If this image is already selected and the download still reports HTTP 412, stop retries. Keep your configuration, journals and volumes. Send the model name, image version and full error to your demo provider. Ask them for an image version compatible with your selected model before changing the image again.
Build and publication in CI
.github/workflows/containers.yml runs fast pull request checks, nightly builds and published release builds.
.github/workflows/latest.yml runs quick Python and UI checks, builds all six images for Linux amd64 and arm64, and publishes each main push as latest.
Main-push publication does not run a container rehearsal.
Nightly and published-release jobs rehearse sign-in, service boundaries and container recreation with existing records.
Blacksmith supplies native workers for both architectures, with persisted builder and dependency-download caches.
Application services and runtime checks run inside Docker. Build commands, skills and development tools run directly on the worker.
Configured routing and caches do not establish a successful rehearsal or cache reuse.
Pull requests run fast Docker and installer checks without the full image build or rehearsal matrix.
Model tags can change their runtime requirements. The nightly and release workflow checks the default models’ current metadata against the pinned Ollama version before building images. This reads manifests and configuration only; it does not download model weights or test model responses.
The nightly run starts at 01:41 UTCUTCCoordinated Universal Time: a shared time reference that does not change with daylight-saving time.. Successful builds and rehearsals update nightly images and the Git tag nightly.
GitHub release: published builds and rehearses all six images before publishing immutable 0.x.y tags, accepting an optional v prefix.
Release publication leaves latest unchanged. Pull requests do not publish.
Every build stages its images in the registry under temporary architecture tags, then publication writes a verified two-platform index.
The nightly and release builds stage only after their rehearsal, under tags that name their run, so they publish exactly the images they rehearsed.
Cleanup removes only obsolete rolling indexes that no retained release references.
This workflow contract does not establish that CIContinuous integrationAutomated checks run when code changes, usually including building the code and running tests. or the live gates have passed.
The UI image includes the static apps/docs bundle, local search and tutorial media under /docs.
The shared base fetches pinned Mycelium source, applies the gateway patch and compiles the reusable payloadPayloadThe data carried inside a message, apart from the surrounding fields that identify and date it..
Local builds reuse a content key covering its Dockerfile, source pin and patch, avoiding repeated compilation when those inputs are unchanged.
The installer prepares this base instead of packaging a source archive.
Release builds pass the exact base reference to all five application builds; the bootstrap controller inherits it too.
The release bundle remains portable committed source rather than a host-specific binaryBinaryA program file built to run on a particular kind of machine. A program built for one processor may not run on another..
Run development commands directly in the task worktree. Application execution and gates use Docker; working-tree application gates select ./lens ops --version dev.
Persistence and failure behaviour
Named volumesNamed volumeStorage managed by Docker under a fixed name. It survives the removal of a running container until explicitly deleted. survive container removal. Preserve the Compose project name and mesh addresses for an existing installation: they locate its stores and node identities. The initialisation volume retains bootstrap checkpoints. The optional model volume contains downloaded models, rather than twin evidence. Setup journals are not backups of either primary store.
NOVUSLENS_MESH_NETWORK sets the first three address octets, default 10.239.58. The network is 10.239.58.0/24 by default, with mesh addresses ending in .10, .11 and .12. Choose the range before creating records; changing it later changes node identity.
Postgres and the mesh both contain primary records. Keeping only one is insufficient for recovery. The installer backs up and restores both together with ./lens backup and ./lens restore. ./lens forget-org erases one organisation. Agree a recovery plan with the customer before relying on its records.
The installer checks its saved fields, refuses unsafe file permissions and reports Docker failures with a non-zero result. It builds or pulls images before starting services. A failed pull does not proceed to deployment. Docker health is separate from the projection worker’s completed analysis: the product’s readiness and freshness states remain relevant.