Docker Compose Setup

Single-host Docker Compose deployment of GENIE.AI: configuration, profiles, and operations.

This guide covers local single-node deployment using docker compose up. For multi-node production deployments with Docker Swarm, see Docker Swarm Deployment Guide.

GENIE.AI uses a single dual-mode docker-compose.yaml at the project root that works with both docker compose up and docker stack deploy.

Prerequisites

  • Docker Engine 23+ with Compose v2 (docker compose subcommand)
  • NVIDIA Container Toolkit (only if using OPEA/GPU services)
  • Hugging Face API token (only if using OPEA/GPU services)

Architecture Overview

For the full system architecture with diagrams (C4 context/container, authentication flows, service auth matrix, token lifecycle, RAG pipeline), see Architecture Overview.

All services run on a single host. Two deployment profiles are available:

ProfileCommandServices
Coredocker compose up -dFrontend, Backend, ArangoDB, Redis, Document Repository, ClamAV, Kong, NGINX
Full (OPEA)docker compose --profile opea --profile gpu-models up -dCore + vLLM, TEI, Retriever, Dataprep, ChatQnA, Translation
Observabilitydocker compose --profile observability up -dCore + OTel Collector, VictoriaMetrics, VictoriaLogs, Grafana

Step 1: Clone Repository

git clone https://github.com/your-org/GENIE.AI.git
cd GENIE.AI

Step 2: Prepare Directories

mkdir -p data/logs/kong data/logs/backend data/logs/doc-repo data/database_backups secrets/ssl
DirectoryPurpose
data/logs/kongKong gateway logs
data/logs/backendBackend API logs
data/logs/doc-repoDocument Repository logs
data/database_backupsBackend DB dumps
secrets/sslSSL certificates (optional — auto-generated if missing)

Step 3: Configure Environment

cp env .env

Edit .env and set the required secrets:

ARANGO_PASSWORD=<strong-password>
TRANSLATION_CACHE_PASSWORD=<strong-password>
POSTGRES_PASSWORD=<strong-password>
KONG_DB_PASSWORD=<strong-password>
KEYCLOAK_ADMIN_PASSWORD=<strong-password>
GENIE_ADMIN_PASSWORD=<strong-password>
KEYCLOAK_DB_PASSWORD=<strong-password>
KEYCLOAK_CLIENT_SECRET=<strong-random-string>
KEYCLOAK_PROXY_CLIENT_SECRET=<strong-random-string>

Generate strong passwords with: python3 -c "import secrets; print(secrets.token_urlsafe(32))"

Important: KONG_DB_PASSWORD and KEYCLOAK_DB_PASSWORD must differ from POSTGRES_PASSWORD — they protect dedicated PostgreSQL users.

Set network/domain variables. Use localhost for local development, or your server IP/domain for remote access:

# Local development
NGINX_PUBLIC_DOMAIN=localhost
VUE_APP_API_URL=https://localhost/api
CSP_CONNECT_SRC='self' https://localhost wss://localhost
CORS_ALLOWED_ORIGINS=https://localhost
# Remote access (e.g., 10.0.0.110)
NGINX_PUBLIC_DOMAIN=10.0.0.110
VUE_APP_API_URL=https://10.0.0.110/api
CSP_CONNECT_SRC='self' https://10.0.0.110 wss://10.0.0.110
CORS_ALLOWED_ORIGINS=https://10.0.0.110

For OPEA/GPU services, also set:

HUGGING_FACE_HUB_TOKEN=<hf-token>

For observability (optional), also set:

ENABLE_OBSERVABILITY=1
GRAFANA_ADMIN_USER=admin
GRAFANA_ADMIN_PASSWORD=<strong-password>
KC_GRAFANA_CLIENT_ID=grafana
KC_GRAFANA_CLIENT_SECRET=<strong-secret>

Note: ENABLE_OBSERVABILITY must be 0 or 1, not true/false.

See the env template for the full list of variables and their descriptions.

Step 4: SSL Certificates

SSL certificates are optional. The nginx container automatically generates self-signed certificates on startup if none are found at secrets/ssl/.

Option A: Self-signed (default)

No action needed. Self-signed certificates are auto-generated. Browser warnings are expected.

Option B: Manual certificates

Place your own certificates:

cp /path/to/server.crt secrets/ssl/
cp /path/to/server.key secrets/ssl/

Option C: Let’s Encrypt (automatic)

For automatic SSL certificate provisioning and renewal:

  1. Set CERTBOT_EMAIL in .env:
    CERTBOT_EMAIL=your-email@example.com
    
  2. Ensure NGINX_PUBLIC_DOMAIN is set to your public FQDN (not localhost).
  3. Deploy with the letsencrypt profile:
    docker compose --profile letsencrypt up -d
    

Certificates are automatically obtained, written to secrets/ssl/, and renewed every 12 hours. Nginx reloads automatically after renewal.

For testing, add CERTBOT_STAGING=true to .env to use Let’s Encrypt’s staging server (avoids rate limits).

Prerequisites: Port 80 must be accessible from the internet, and your domain must have a DNS A/AAAA record pointing to your server.

Step 5: Build Images

The compose file includes build: directives alongside image: for all custom services. Build them with:

# Build all custom images
docker compose build

# Or build a specific service
docker compose build backend

For OPEA services (only if using --profile opea):

docker compose --profile opea --profile gpu-models build

Step 6: Deploy

6a. Validate configuration

docker compose config > /dev/null

Fix any errors before proceeding.

6b. Start core services

docker compose up -d

This starts: Frontend, Backend, ArangoDB, Redis, Document Repository, ClamAV, Kong, NGINX, and Keycloak.

6c. Start full stack with OPEA

If you have a GPU and want the full RAG pipeline:

docker compose --profile opea --profile gpu-models up -d

6d. With GPU-specific settings

For NVIDIA T4 (16GB VRAM):

docker compose --env-file .env --env-file env.t4 --profile opea --profile gpu-models up -d

For RTX 6000 ADA (24GB VRAM):

docker compose --env-file .env --env-file env.rtx6000 --profile opea --profile gpu-models up -d

6e. Remote GPU Node (Optional)

GENIE.AI supports connecting to a dedicated GPU node for AI services, deployed separately from the app stack. When configured, the app node routes AI requests to the GPU node via HTTPS (default port 443, configurable via gpu_https_port) with API key authentication and skips local GPU-heavy containers.

To connect to a remote GPU node, set in .env:

GPU_NODE_HOST=<gpu-node-host>       # GPU node IP or hostname
VLLM_API_KEY=<your-api-key>         # API key from the GPU node administrator (Authorization: Bearer)
OPEA_SSL_SKIP_VERIFY=1              # If GPU node uses self-signed certs

Then deploy orchestrators only (GPU models are skipped):

docker compose --profile opea up -d

Note: OPEA_SSL_SKIP_VERIFY=1 disables SSL certificate verification in OPEA services via a runtime patch (configs/ssl/genie_ssl_patch.py). Only use with self-signed certs. Omit this variable if the GPU node uses Let’s Encrypt or a public CA. VLLM_API_KEY authenticates with the GPU node nginx via standard Authorization: Bearer header. All clients send this header: OpenAI-compatible clients (ChatOpenAI, AsyncOpenAI) via api_key param; OPEA TEI wrapper services (embedding, reranker) via HF_TOKEN env var (automatically set from VLLM_API_KEY in docker-compose.yaml). If Keycloak also uses a self-signed cert, set KEYCLOAK_SSL_SKIP_VERIFY=1 independently.

Self-Signed Certificates — Decision Matrix

Three environment variables control TLS verification for self-signed certificates. Each covers a different layer:

VariableServicesMechanismWhen to set
NODE_TLS_REJECT_UNAUTHORIZED=0backend, document-repositoryNode.js built-in (all HTTPS connections)NGINX uses self-signed cert (local dev)
OPEA_SSL_SKIP_VERIFY=1embedding, reranker, textgen, dataprep, retriever, chatqna (7 Python services)configs/ssl/genie_ssl_patch.py (patches Python ssl module)Remote GPU node uses self-signed cert
KEYCLOAK_SSL_SKIP_VERIFY=1dataprep-arango-service onlyaiohttp connector in keycloak_service_account.pyKeycloak behind NGINX with self-signed cert

Quick reference — which variables to set by scenario:

ScenarioNODE_TLS_REJECT_...OPEA_SSL_...KEYCLOAK_SSL_...
Local dev, self-signed NGINX, no remote GPU0
Local dev, remote GPU with self-signed cert01
Production, real CA certificates on all hosts
Production/air-gapped, self-signed NGINX01*1

= use default (verify certs). * Only if remote GPU node also uses self-signed cert.

6f. Start with observability stack

To add the OTel Collector, VictoriaMetrics, VictoriaLogs, and Grafana monitoring stack:

docker compose --profile observability up -d

Combine with OPEA for the full stack plus observability:

docker compose --profile opea --profile gpu-models --profile observability up -d

Log collection: All services use the fluentd logging driver (driver: fluentd) to forward container logs to the OTel Collector’s fluent_forward receiver on port 24224 (localhost only). Docker dual logging (20.10+) keeps docker logs functional.

Access Grafana via Kong at https://<domain>/grafana/ (requires Keycloak OIDC login). 9 pre-built dashboards are auto-provisioned (service health, application metrics, service logs, trace explorer, RAG pipeline waterfall, plus observability-stack health and the three Victoria single-node views).

See configs/otel/README.md for Collector configuration details.

Step 7: Post-Deploy — Kong Configuration

The kong-config one-shot service automatically configures Kong routes, services, and plugins after Kong starts. It runs once and completes in ~10-30 seconds.

Monitor its progress:

docker compose logs kong-config --tail 20

Once you see “Configuration restored successfully”, Kong is ready to route traffic.

Note: Kong routes are unavailable until kong-config completes. Services calling Kong during this window will get 404s.

Step 8: Post-Deploy — Keycloak Identity Provider

Keycloak is started automatically with the core stack and proxied by NGINX at /auth/*. The keycloak-config one-shot service applies realm configuration (clients, roles, mappers) after Keycloak is healthy.

Verify Keycloak health:

docker compose ps keycloak
docker compose logs keycloak-config --tail 10

Admin console:

  • URL: https://<NGINX_PUBLIC_DOMAIN>/auth/admin/
  • Username: admin
  • Password: <KEYCLOAK_ADMIN_PASSWORD> from .env

GENIE realm admin user (separate from master admin, used for frontend login):

  • Username: genie-admin (default, configurable via GENIE_ADMIN_USERNAME)
  • Password: <GENIE_ADMIN_PASSWORD> from .env
  • Has admin realm role — grants admin access in the GENIE.AI frontend

Keycloak environment variables:

See Section 9 of the env template for all available variables. Key ones:

VariableDefaultDescription
KEYCLOAK_REALMgenieRealm name
KEYCLOAK_CLIENT_IDgenie-appOIDC client ID
KEYCLOAK_URLhttps://<domain>/authPublic URL (auto-set from NGINX_PUBLIC_DOMAIN)
NGINX_HTTPS_PORT443HTTPS port — included in Keycloak redirect URIs and web origins via KC_PUBLIC_ORIGIN
KEYCLOAK_ADDITIONAL_REALMSAdditional realms (JSON array of realm names, optional)

Redirect URIs and web origins are auto-derived from NGINX_PUBLIC_DOMAIN + NGINX_HTTPS_PORT into KC_PUBLIC_ORIGIN (e.g., https://localhost:8443). If you use a non-standard HTTPS port, set NGINX_HTTPS_PORT in .env — no manual Keycloak configuration needed.

For external IdP integration (Google, Microsoft, etc.), see External IdP Integration Guide.

Keycloak behind the reverse proxy

Keycloak runs behind the NGINX → Kong proxy chain with the /auth path prefix. The proxy headers are handled automatically:

  • NGINX sets X-Forwarded-Proto, X-Forwarded-Host, and X-Forwarded-Port on /auth/ requests
  • Kong adds X-Forwarded-Prefix: /auth via the request-transformer plugin and strips the /auth prefix before forwarding to Keycloak
  • Kong must trust nginx to preserve these headers — configured via KONG_TRUSTED_IPS (default: 172.16.0.0/12, covers Docker bridge subnets)
  • Keycloak resolves its public URL dynamically from these headers (KC_PROXY_HEADERS=xforwarded, KC_HOSTNAME=<hostname>)

This means Keycloak’s OIDC discovery endpoint (/auth/realms/genie/.well-known/openid-configuration) returns the correct public URLs (scheme, host, port, path prefix) without hardcoding a full URL in KC_HOSTNAME.

Step 9: Verify Deployment

Check service status:

# List all running services
docker compose ps

# Check logs for a specific service
docker compose logs backend --tail 20

Smoke tests:

# Backend health through Kong
curl -sk https://localhost/api/health

# Frontend (should return HTML)
curl -sk https://localhost/

Access the application:

  • Web UI: https://localhost/ (self-signed cert warning is expected)
  • API Docs: https://localhost/api-docs

Step 10: Useful Commands

# View logs (follow mode)
docker compose logs -f <service-name>

# Restart a specific service
docker compose restart <service-name>

# Rebuild after code changes
docker compose build <service-name>
docker compose up -d <service-name>

# Stop all services
docker compose down

# Stop and remove volumes (data loss)
docker compose down -v

Step 11: Debugging

Execute commands inside a running container:

docker compose exec backend bash
docker compose exec arango-vector-db arangosh

Test connectivity between services:

docker compose exec backend curl -s http://arango-vector-db:8529/_api/version

Run a temporary container on the same network:

docker compose run --rm curlimages/curl curl -s http://backend:3000/api/health

Teardown

# Stop all services (volumes and data preserved)
docker compose down

# Remove everything including named volumes (data loss)
docker compose down -v

Troubleshooting

Service fails to start

# Check logs
docker compose logs <service-name> --tail 50

# Check if a dependency is unhealthy
docker compose ps

Common causes:

  • Environment variable not set in .env
  • Port already in use (check with ss -tlnp | grep <port>)
  • SSL certificate warnings in browser (expected with self-signed certs — see Step 4)

GPU services not starting

# Verify NVIDIA runtime
docker run --rm --gpus all nvidia/cuda:12.0-base nvidia-smi

# Check GPU service logs
docker compose logs vllm --tail 50

Kong routes returning 404

Wait for the kong-config service to complete (Step 7). Also check that keycloak-config completed (Step 8). If either failed:

docker compose logs kong-config
docker compose restart kong-config

DNS resolution delays

Unlike Swarm, docker compose up handles startup ordering via depends_on. Services wait for their dependencies to be healthy before starting. If a service still fails, check its healthcheck status:

docker compose ps  # shows health status for each service

Next Steps

  • Production deployment: See Docker Swarm Deployment Guide for multi-node, production-grade deployments with Ansible.
  • Ansible automation: See deploy/ansible/README.md for automated Swarm deployment.
  • GPU configuration: See env.t4 and env.rtx6000 for GPU-specific tuning.