Docker Compose¶
Self-Contained Demo Layout¶
The demo uses four independent uv projects rather than a uv workspace:
| Project | Lockfile | Deployment |
|---|---|---|
demo/api |
demo/api/uv.lock |
ghcr.io/ilkersigirci/lgos-demo-api |
demo/files_api |
demo/files_api/uv.lock |
ghcr.io/ilkersigirci/lgos-files-api |
demo/ui/chainlit_ui |
demo/ui/chainlit_ui/uv.lock |
ghcr.io/ilkersigirci/lgos-chainlit |
demo/ui/openwebui |
demo/ui/openwebui/uv.lock |
Host-run sync tool; unchanged pinned official Open WebUI image |
Published project-owned images use only their project directories as build
contexts. The Compose entrypoint is docker/compose/demo.yml; service
definitions live in docker/apps/, while docker/compose/development.yml and
docker/compose/otel.yml provide development and OpenTelemetry overlays.
Shared runtime assets remain under demo/docker/. The development overlay
additionally supplies the parent LGOS checkout as a named context for the API's
editable install. Open WebUI runs its pinned official image unchanged. Its
locked OpenAI v3 and HTTPX2 synchronization project runs on the host, while
Compose mounts only the Function sources and small raw-upload policy into the
upstream image. The two gateway fragments use pinned public images: upstream
Bifrost and homeserver-litellm. There is no demo-wide pyproject.toml, uv
workspace, shared Python environment, or shared lockfile.
The API package includes the compact Markdown corpus used by lgos-rag.
Compose Modes¶
Commands use Bash and Just 1.58.0 or newer and run from the repository root.
In a standalone copy of demo/, use just <recipe> in place of
just demo/<recipe>.
Docker Compose 5.3.0 or newer
The demo is validated with Compose 5.3.0. Chainlit and the two API services use dedicated one-shot schema jobs and wait for successful completion. This avoids running the same persistence migrations concurrently in application workers and ensures the MCP views are created only after their source tables exist.
Prepare the demo environment:
Set PUID and PGID in demo/.env to the numeric host identity that owns the
bind directories. The checkout includes each empty service directory with a
tracked .gitkeep; service-created contents remain ignored.
demo/.env.example owns the configurable demo defaults. Compose reads the
corresponding values from demo/.env without supplying fallbacks. Keep an
existing demo/.env in sync with new template settings without overwriting
secrets.
Before using either OTEL mode, configure the OpenTelemetry settings.
demo/docker/compose/demo.yml contains no local builds:
The command waits for the gateway and its dependencies, syncs LiteLLM when
selected, starts both UIs without recreating the healthy backends, and syncs
Open WebUI. Services remain running in the background. Compose owns dependency order and
readiness; just only
sequences the repeatable sync jobs.
Set DEMO_IMAGE_TAG in demo/.env to select one
release tag for all project-owned demo images. To add the published OTEL
overlay, use just demo/compose --otel.
Apply the explicit development model from the LGOS repository checkout. The API, Files API, and Chainlit services build locally from their Dockerfiles and lockfiles; Open WebUI remains the pinned upstream image. Only the API image installs the parent LGOS checkout as an editable package:
To add the OTEL overlay while building the current checkout, use
just demo/compose --dev --otel.
The development overlay bind-mounts the Python application sources and the parent LGOS package read-only. Restart or recreate the affected service after source edits. Dependency metadata and lockfile changes require an image rebuild.
For immediate local feedback without containers, use uv's temporary editable overlay:
This tests all four projects and does not rewrite api/pyproject.toml or
api/uv.lock. Chainlit and Open WebUI remain standalone clients and exercise
whichever API their OpenAI base URL targets.
Demo Services¶
One bundled gateway at a time
Bifrost and LiteLLM both publish host port 3000. Enable only the selected
gateway's profile, and stop the running gateway before switching. Changing
OPENAI_GATEWAY_TYPE does not stop the previous gateway container.
The API and Chainlit migration jobs initialize their persistence schemas.
A following one-shot job creates or updates the dedicated lgos_mcp login
and four curated views over live Chainlit users/conversations and pending
LGOS interrupts. The role can read only those views. The pinned DBHub
service then exposes six fixed reports only inside the Compose network;
it publishes no host port. Starting either bundled gateway starts these
dependencies automatically. Replace LGOS_MCP_DB_PASSWORD and
LGOS_MCP_AUTH_TOKEN before startup. See
PostgreSQL Through Native MCP for the complete
client and security flow.
Run each attached service in a separate terminal. Compose starts the shared
PostgreSQL dependency automatically. Before either graph API starts,
lgos-demo-api-setup waits for PostgreSQL health and initializes the
LangGraph checkpoint and store schemas once. Both APIs use
service_completed_successfully
as their readiness dependency.
lgos-a:http://localhost:3004/v1lgos-b:http://localhost:3005/v1
For an independently deployed LiteLLM API, run the shared one-shot model-sync job after the deployment's health check.
The independently packaged service connects directly to its configured S3-compatible store. It neither starts PostgreSQL nor imports LGOS.
- central Files API:
http://localhost:3006/v1
The UIs use native /openai/v1/responses, normal /v1 Files routing, the
aggregate /mcp endpoint, and raw pass-through only for provider-specific
catalog detail. The named PostgreSQL Virtual MCP remains available at
http://localhost:3000/mcp/lgos-postgres. See Bifrost Gateway
for endpoints, routing, and the shared SDK verification command.
Set these values in demo/.env to reuse an existing LiteLLM gateway:
OPENAI_GATEWAY_TYPE=litellm
COMPOSE_PROFILES=
OPENAI_GATEWAY_BASE_URL=https://litellm.example.com
DEMO_GATEWAY_HOST_URL=https://litellm.example.com
OPENAI_GATEWAY_API_KEY=TO_BE_FILLED
DEMO_CHAINLIT_ENABLE_OAUTH_TOKEN_FORWARDING=false
Use the gateway root without /v1. Both UIs share the gateway type, base
URL, and OPENAI_GATEWAY_API_KEY, or Chainlit uses delegated OAuth when
explicitly enabled. An external LiteLLM uses the same
Responses, Files, and catalog routes as bundled LiteLLM.
Both UIs derive their MCP endpoint from the same gateway root and use the
same credential as Responses and Files. To use mcp-postgres, also adapt
the checked-in
mcp_servers.lgos_postgres entry and LGOS_MCP_AUTH_TOKEN for the external
gateway, just as the Files configuration is adapted below. The external
gateway URL must be reachable from both UI containers. On another host,
expose DBHub only on a private address reachable by that gateway.
just demo/compose (or the same command with --dev) starts
the demo APIs, Files service, DBHub, and its PostgreSQL setup, syncs both
catalogs to the external LiteLLM, then starts and syncs the UIs. An empty
COMPOSE_PROFILES disables both bundled gateways;
the template normally selects one through
COMPOSE_PROFILES=${OPENAI_GATEWAY_TYPE}. The external gateway and its
administrator credentials must be reachable by the
model-sync job.
Enable LiteLLM's native database model storage. For Files, adapt the
files_settings in docker/configs/litellm/config.yaml
to the shared Files service. No LGOS catalog pass-through
is required. Retain the bundled image's
LITELLM_ENABLE_RESPONSES_STREAMING_FIX=true opt-in when using that image.
On the same Docker host, attach the existing LiteLLM service to the demo's network through its own Compose deployment, alongside its current networks:
services:
litellm:
networks:
# Retain the service's existing networks here too.
- lgos
networks:
lgos:
external: true
name: lgos-network
Create the network by starting the demo backends first, for example with
just demo/up lgos-demo-api-a,
just demo/up lgos-demo-api-b, and
just demo/up lgos-files-api in separate terminals, then
just demo/up lgos-postgres-mcp --wait.
The existing gateway can then resolve lgos-demo-api-a,
lgos-demo-api-b, lgos-files-api, and lgos-postgres-mcp using the
bundled sync, Files, and MCP configuration examples. For another host,
replace those upstream URLs with addresses reachable from that gateway.
The external deployment continues to own its database, TLS, credentials,
and Admin UI SSO. The selected credentials must allow the LGOS models, Files
operations, the configured MCP tools, and native /model/info. Chainlit can
enable delegated OAuth; it then ignores
the shared static key and disables its native MCP connection.
If the gateway already configures litellm_proxy Files,
reconcile that provider with the demo's shared Files namespace. Then run
just demo/sync-openwebui and the
LiteLLM SDK checks against the external URL.
LiteLLM is one of the two first-class UI entry points. After startup, sync both demo catalogs. The UIs use:
- model metadata:
http://localhost:3000/model/info - managed Files:
http://localhost:3000/v1 - managed routing:
http://localhost:3000/v1 - native MCP:
http://localhost:3000/mcp/ - LiteLLM Admin UI:
http://localhost:3000/ui/
The bundled configuration uses API-key authentication. Chainlit can still use OAuth for browser login with that key. OAuth token forwarding additionally requires a gateway configured to validate delegated access tokens through its trusted SSO ingress; enabling forwarding in Chainlit alone does not configure LiteLLM. See Chainlit OAuth.
Chainlit and Open WebUI send Responses and Files to managed routing and
read descriptions, capabilities, and settings from model_info.lgos.
Each graph uses LiteLLM's model_name unchanged for inference. The proxy retains
normal model routing while each API remains the source of its descriptions
and LGOS capability metadata and the Files service remains the owner of
file bytes.
Neither UI connects to an upstream service directly.
OPENAI_GATEWAY_API_KEY protects these routes in the default setup; replace its demo-only
default in any shared deployment. For the local Admin UI, sign in as
admin; unless UI_PASSWORD is set separately, the password is the value
of OPENAI_GATEWAY_API_KEY from demo/.env.
The MCP configuration reaches DBHub with the separate internal
LGOS_MCP_AUTH_TOKEN, allowlists the six fixed PostgreSQL reports, and
permits authenticated gateway keys to use them. Native clients still opt
in by connecting to /mcp/; the graph API receives only ordinary OpenAI
function definitions and results.
The managed-routing surface uses concrete database-backed models
and LiteLLM's native
Responses endpoint. Select an
API with a provider-qualified model, such as
lgos-a/custom-input-output-context or
lgos-b/custom-input-output-context. The shared demo PostgreSQL service
keeps LiteLLM's Admin UI and gateway-management records in its own
litellm schema; graph execution state remains owned by LGOS. LiteLLM's
standard files_settings route uses provider=litellm_proxy to isolate
upload, retrieval, content, and deletion from the graph deployments.
The LGOS-owned sync reads each API's model list and detail, then registers
routing and model_info.lgos through LiteLLM's native management API.
New deployments set model_info.supports_native_streaming: true.
The default public
homeserver-litellm image
preserves native Responses streaming. Compose
reads its tag and digest from DEMO_LITELLM_IMAGE in demo/.env and enables
LITELLM_ENABLE_RESPONSES_STREAMING_FIX=true so it honors the deployment
capability. Normal demo commands use this image without a local build or
an image override.
To use another compatible image, set DEMO_LITELLM_IMAGE in demo/.env
or supply it on the command line:
Keep the override set for subsequent Compose commands. To restore the
default, copy the image value from demo/.env.example. An alternative image
must preserve native Responses streaming, authenticated /model/info with
custom metadata, managed Files routing, and the Admin UI migration runtime.
Managed routing also passes the tested Files lifecycle, file-ID input, and function continuation, while its rewritten standard error metadata remains a strict expected failure. The bundled image records successful managed Responses requests in LiteLLM's spend logs, including streaming requests. Token and spend values reflect usage and pricing supplied for the selected graph model.
With the service healthy, run the focused OpenAI SDK check from the repository root. It tests managed routing, the catalog-to-inference flow, and native streaming fidelity against the direct LGOS test endpoints. LiteLLM exposes no demo pass-through routes:
With the gateway and its backends running:
Chainlit: http://localhost:3002
This command starts Chainlit and PostgreSQL. Use
just demo/compose to start the complete stack with the gateway
selected by COMPOSE_PROFILES.
Configure its signing secret as described in the Chainlit client.
Open WebUI: http://localhost:3003
Compose runs the pinned official Open WebUI image unchanged. Follow the Open WebUI setup to synchronize the bundled Functions and generate Workspace Models from LGOS metadata. The Compose service also mounts the temporary raw-upload policy described under Open WebUI file input.
PostgreSQL is published on localhost:3001. LangGraph persistence, Bifrost
state, and Open WebUI state—including its native raw file copies—use host bind
mounts under demo/docker/volumes/; the Compose model declares no named
volumes. Every service runs as PUID:PGID with a read-only root filesystem,
dropped capabilities, and explicit resource limits. Narrow tmpfs mounts hold
required ephemeral writes. The one-shot API and Chainlit setup services
initialize their respective persistence schemas. A following idempotent setup
job owns the MCP reporting views, role, and grants before DBHub starts.
Chainlit stores thread and element metadata in PostgreSQL, while its native S3
client uploads generated file elements to the configured BUCKET_NAME.
Resuming a thread obtains a fresh signed object URL from that client. The
central Files API uses only its separate DEMO_API_FILES_BUCKET,
DEMO_API_FILES_S3_ENDPOINT, and DEMO_API_FILES_AWS_* settings. The two S3
configurations are independent.
The API workers share PostgreSQL for thread-scoped application data, durable checkpoints, and fail-fast interrupt coordination. Session-level advisory locks prevent two workers from advancing the same interrupt run at once; a contended request fails instead of waiting. No Redis service is required. The lock is held only while an API request executes the graph, never while a human is deciding. A per-process capacity gate preserves a pool connection for persistence I/O.
Compose also forces LANGGRAPH_STRICT_MSGPACK=true for the APIs. Strict
deserialization narrows which checkpoint object types LangGraph may
reconstruct, following its
security guidance.
Protect the PostgreSQL credentials and storage as integrity-sensitive data as
well.
PostgreSQL is sufficient state infrastructure, not a complete operations plan
The Compose database is a single demo container. A production deployment still owns tested backup and restore, monitoring, upgrades, and its chosen replication and failover guarantees. LangGraph's exit durability writes the resumable state when an invocation pauses or finishes; LGOS drains that invocation before exposing interrupt tool calls. Whether the resulting commit survives loss of the primary depends on the PostgreSQL replication policy.
Budget connections across every API replica. Each demo API process has a five-connection pool and permits at most four simultaneous interrupt leases, preserving one connection for checkpoint I/O. Psycopg recommends monitoring pool statistics and sizing from observed workload; see its pool guidance.
The coordinator uses session-level advisory locks and must retain one database session for the whole lease. If a proxy such as PgBouncer sits in front of PostgreSQL, use session pooling or a direct coordinator connection; PgBouncer documents session advisory locks as unsupported in transaction-pooling mode.
Demo images are examples
The published images run the demo applications and graphs. They are not generic LGOS server images and should not be used as the base contract for an application that owns different graphs or dependencies.
Applications outside demo/ own their container images and deployment model;
LGOS does not prescribe either. For exact demo commands and environment
variables, see Demo Settings and Commands.