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 |
Local Function sync command and upload-policy mount |
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. The Open WebUI
integration uses the official Open WebUI image and keeps its Function sync
command local. Compose also mounts its small raw-upload policy into that image;
it does not build a project-owned Open WebUI 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¶
Docker Compose 5.3.0 or newer
Chainlit uses pre_start for its private schema migrations. The two API
services instead share one dedicated lgos-demo-api-setup job and wait for
its successful completion. This avoids running the same LangGraph
checkpoint migration concurrently in both API workers.
Prepare the demo environment:
Set PUID and PGID in .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.
.env.example owns the configurable demo defaults. Compose reads the
corresponding values from .env without supplying fallbacks. Keep an
existing .env in sync with new template settings without overwriting secrets.
Before using either OTEL mode, configure the OpenTelemetry settings.
docker/compose/demo.yml contains no local builds:
Set DEMO_IMAGE_TAG in .env to select one
release tag for all project-owned demo images. To add the published OTEL
overlay, use make 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. Only the API image installs the parent LGOS checkout as an editable package:
To add the OTEL overlay while building the current checkout, use
make compose-otel-dev.
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 command 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¶
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
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, and
raw pass-through only for provider-specific catalog detail. See Bifrost
Gateway for endpoints, routing, and the shared SDK verification
command.
LiteLLM is one of the two first-class UI entry points. The UIs split catalog detail from normal inference and Files routing:
- API A pass-through:
http://localhost:3007/v1/lgos-a - API B pass-through:
http://localhost:3007/v1/lgos-b - managed Files:
http://localhost:3007/v1 - managed routing:
http://localhost:3007/v1 - LiteLLM Admin UI:
http://localhost:3007/ui/
Chainlit and Open WebUI send Responses and Files to managed routing and
merge both authenticated catalog pass-throughs. Each graph keeps its
lgos-a/ or lgos-b/ prefix before inference. The proxy therefore 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.
DEMO_LITELLM_MASTER_KEY protects all four routes; 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 DEMO_LITELLM_MASTER_KEY from .env.
The managed-routing surface uses one wildcard route per graph API
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.
Graph discovery reads each API's /v1/models; no graph list is maintained
in gateway configuration. Both wildcard routes set
model_info.supports_native_streaming: true. The default public
homeserver-litellm image
preserves native Responses streaming for these wildcard routes. Compose
reads its tag and digest from DEMO_LITELLM_IMAGE in .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 from demo/:
Keep the override set for subsequent Compose commands. To restore the
default, copy the image value from .env.example. An alternative image must preserve
native wildcard Responses streaming, authenticated catalog pass-through,
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 the complete pass-through contract:
Chainlit: http://localhost:3002
Configure its signing secret as described in the Chainlit client.
docker compose --env-file .env -f docker/compose/demo.yml up --wait lgos-openwebui
make sync-openwebui
Open WebUI: http://localhost:3003
Compose runs the official Open WebUI image. The local sync command updates the bundled Functions and generates 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 setup service initializes the
LangGraph persistence schemas before both API workers, while Chainlit's
pre_start hook applies its independent UI migrations.
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.