Deployment assets are at mixed maturity. The database Compose file supports local adapter smoke tests, the Temporal Compose file provides a runnable PostgreSQL-backed development server, and the development helper composes those services with the control-plane API. The checked-in API, CLI, worker, and MCP Dockerfiles are runnable local images. Production topology and remote MCP exposure still require operator-owned composition and security policy.
deploy/compose/docker-compose.database.yml defines:
Note that host port 6379 is FalkorDB, not Redis - Redis is published on 6380 to avoid
the collision. HARBORRAG_REDIS_URL=redis://redis:6379/0 is still correct, because that
URL is resolved inside the Docker network rather than on the host. FalkorDB’s browser also
defaults to host port 3000, which collides with Grafana in the monitoring stack.
The service image tags are pinned directly in the Compose file. The environment template
controls ports and credentials. Its DATABASE_PULL, DATABASE_STARTUP_TIMEOUT, and
DATABASE_COMPOSE_FILE entries are vestigial and read by nothing.
FalkorDB persists RDB snapshots in its named volume. Do not enable AOF for this
service: FalkorDB 4.20.1 can create and restore the knowledge-graph uniqueness
constraint from a snapshot, but crashes when Redis replays the asynchronous
GRAPH.CONSTRAINT command from AOF. The separate Redis service continues to use
AOF persistence.
Prepare a protected environment file:
scripts/deployment/dev.sh bootstrap # creates all seven env/ files, mode 0600
bootstrap is preferred over copying templates by hand: it creates every file the
entrypoints require - including env/.env.api, which mcp.sh hard-requires - and mints
the MCP bearer token.
HARBORRAG_SECRETS_ENCRYPTION_KEY in env/.env.database ships empty and blocks
startup. It encrypts stored connector credentials in the control database, and both the
API and the Temporal worker must read the same value. Compose guards it, and treats an
empty value as unset:
openssl rand -hex 32 # paste into HARBORRAG_SECRETS_ENCRYPTION_KEY
Rotating it after secrets have been stored requires re-encrypting or re-entering them.
When upgrading an existing checkout, add HARBORRAG_SECRETS_ENCRYPTION_KEY,
POSTGRES_PASSWORD, and MINIO_ROOT_PASSWORD to the existing protected
env/.env.database before the next start. Monitoring now uses a separate env/.env.monitoring; create it
from env-example/.env.monitoring.example and set GRAFANA_ADMIN_PASSWORD.
Compose intentionally refuses to start an affected service when one of these
required passwords is absent.
The unified deployment helper accepts DATABASE_ENV_FILE overrides and passes
the selected file to Docker Compose. Change the example password before using
the stack outside an isolated developer machine.
The stack owns the stable harborrag-data-network, and it is the only compose file that
creates it - every other stack declares it external: true. Application containers and the
Temporal worker join it. Temporal control-plane services additionally sit on their own
Compose project network (harborrag-temporal_default). Start the database stack before
anything else, or the other stacks fail on a missing network.
Use the repository smoke runner in Testing to verify adapters through their public APIs.
docker-compose.monitoring.yml defines version-pinned Prometheus, Grafana, and
Loki services using files under deploy/prometheus, deploy/grafana, and
deploy/loki. Prometheus and Grafana publish loopback-only ports by default;
Loki is reachable only inside the private monitoring network.
Prometheus joins harborrag-data-network; API metrics at /api/v1/metrics
require an admin bearer token, while the default configuration scrapes
the ingestion worker on port 9464. The API exports request count,
latency, in-flight, Python runtime, and process metrics using bounded route
template labels. The worker exports bounded stage, document, artifact,
chunk, retry, cleanup, verification, stale-candidate, and connector
rate-limiting metrics. Dashboard and alert policy remain
deployment-specific.
scripts/deployment/dev.sh bootstrap does not create env/.env.monitoring, and this
stack declares harborrag-data-network as external, so dev.sh data must have run first:
scripts/deployment/dev.sh data # creates harborrag-data-network
cp env-example/.env.monitoring.example env/.env.monitoring
chmod 600 env/.env.monitoring
# GRAFANA_ADMIN_PASSWORD ships empty and is guarded, so Compose refuses to start
# until you set it. Grafana also defaults to host port 3000, which the FalkorDB
# browser already uses -- change GRAFANA_PORT or FALKORDB_BROWSER_PORT.
docker compose --env-file env/.env.monitoring \
--file deploy/compose/docker-compose.monitoring.yml up --detach
Set GRAFANA_ADMIN_PASSWORD in the protected monitoring environment file
before starting the stack; Compose has no checked-in password fallback.
Monitoring remains separate from scripts/deployment/dev.sh bootstrap. Keep
its environment file protected and monitoring bound to loopback unless an
authenticated TLS reverse proxy and firewall protect it. The hardened stack
initializes a new grafana_secure_data volume so an older Grafana volume
created with the retired admin/admin default is never reused; the old volume
remains available for manual recovery or removal.
scripts/deployment/dev.sh is the only deployment entrypoint. Run bootstrap
once to create protected environment files from env-example/, including the
API-only env/.env.api, then review the placeholders. The up command starts
data services, Temporal, a Temporal worker, and the API, returning only after
the API process health check succeeds:
scripts/deployment/dev.sh bootstrap
scripts/deployment/dev.sh up
When the database and Temporal stacks are already running, start only the API with:
scripts/deployment/dev.sh api
The API and worker commands reuse Compose’s existing local
harborrag-api-api and harborrag-temporal-temporal-worker images by default.
A missing image is built on the first start. Use api --build, worker
--build, or up --build after changing application source, dependency
metadata, or worker configuration. Docker then reuses the dependency-install
layer while uv.lock and package metadata are unchanged, so source-only
rebuilds do not reinstall libraries.
The API binds to 127.0.0.1:8000 by default. Override
HARBORRAG_API_BIND_ADDRESS or HARBORRAG_API_PORT when another local binding
is required. The local container’s internal wildcard listener is explicitly
acknowledged by HARBORRAG_ALLOW_INSECURE_DEV=true; this is safe only while the
published address remains loopback. Configure HMAC authentication before using
a non-loopback published address. Authentication and CORS values belong in the ignored
env/.env.api. The API receives chat and embedding credentials from
env/.env.models for chat completions and synchronous vector retrieval, while
connector credentials remain isolated in ingestion workers. Use
dev.sh up --no-worker to omit the worker. Stop the
development topology in reverse order with:
scripts/deployment/dev.sh down
Individual ownership is explicit: temporal never starts a worker, worker
only starts the worker, and api starts the API with --no-deps.
deploy/compose/docker-compose.yml is the single API composition. It joins the
external data network created by docker-compose.database.yml and connects to
the Temporal service created by docker-compose.temporal.yml. Environment
policy, rather than duplicated development and production Compose files,
controls the API mode. Production deployments must supply authentication and
their own secret/TLS/network policy.
Build the standalone operator images from the repository root:
docker build -f deploy/docker/Dockerfile.cli -t harborrag-cli .
docker build -f deploy/docker/Dockerfile.mcp -t harborrag-mcp .
docker run --rm harborrag-cli --help
docker run --rm harborrag-mcp --check
Both images include the shared runtime, model and retrieval dependencies,
checked-in configuration, and packaged chat prompts. They run as the non-root
harborrag user. Configuration paths inside the images point to /app/config;
mount a replacement directory there read-only when deploying a different
catalog.
The CLI image uses harborrag as its entry point. Provide
env/.env.models for chat and the relevant service settings for retrieval or
ingestion commands:
docker run --rm \
--env-file env/.env.models \
harborrag-cli chat "Explain HarborRAG." --json
The MCP image uses python -m harborrag_mcp_server as its entry point and
defaults to stdio. An MCP client must keep stdin attached with -i; real tool
calls also require protected model/database settings and network-reachable data
services. Its writable JSONL audit path is
/var/lib/harborrag/.harborrag/mcp-audit.jsonl.
Use scripts/deployment/mcp.sh --http for the authenticated loopback HTTP
transport and local status/configuration UI. Remote MCP needs TLS and a
production JWT/JWKS verifier and is intentionally not provided by the local
container command.
scripts/models/ contains helpers for Docling/FastEmbed downloads, warmup, and local-model smoke checks. Inspect each script before use: provider/model downloads may require network access, substantial disk space, and platform-specific runtimes. Keep caches outside container layers when they need independent lifecycle management.
deploy/temporal/ contains PostgreSQL schema setup, dynamic configuration, and
namespace initialization for local development. It is not a production
topology; see its README for the Temporal Cloud/Helm boundary. deploy/aws/
reserves cloud deployment directions and does not provide complete
infrastructure-as-code.
Before an internet-facing deployment can be considered production-ready, operators still need deployment-specific authorization policy, TLS/network policy, secret delivery, backup/restore, resource limits, alert thresholds, and end-to-end tests against their chosen source systems. Review the runtime reliability boundary before choosing worker, retry, timeout, retention, and recovery policy.