Docker Compose Setup
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 composesubcommand) - 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:
| Profile | Command | Services |
|---|---|---|
| Core | docker compose up -d | Frontend, Backend, ArangoDB, Redis, Document Repository, ClamAV, Kong, NGINX |
| Full (OPEA) | docker compose --profile opea --profile gpu-models up -d | Core + vLLM, TEI, Retriever, Dataprep, ChatQnA, Translation |
| Observability | docker compose --profile observability up -d | Core + 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
| Directory | Purpose |
|---|---|
data/logs/kong | Kong gateway logs |
data/logs/backend | Backend API logs |
data/logs/doc-repo | Document Repository logs |
data/database_backups | Backend DB dumps |
secrets/ssl | SSL 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:
- Set
CERTBOT_EMAILin.env:CERTBOT_EMAIL=your-email@example.com - Ensure
NGINX_PUBLIC_DOMAINis set to your public FQDN (notlocalhost). - Deploy with the
letsencryptprofile: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=1disables 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_KEYauthenticates with the GPU node nginx via standardAuthorization: Bearerheader. All clients send this header: OpenAI-compatible clients (ChatOpenAI, AsyncOpenAI) viaapi_keyparam; OPEA TEI wrapper services (embedding, reranker) viaHF_TOKENenv var (automatically set fromVLLM_API_KEYin docker-compose.yaml). If Keycloak also uses a self-signed cert, setKEYCLOAK_SSL_SKIP_VERIFY=1independently.
Self-Signed Certificates — Decision Matrix
Three environment variables control TLS verification for self-signed certificates. Each covers a different layer:
| Variable | Services | Mechanism | When to set |
|---|---|---|---|
NODE_TLS_REJECT_UNAUTHORIZED=0 | backend, document-repository | Node.js built-in (all HTTPS connections) | NGINX uses self-signed cert (local dev) |
OPEA_SSL_SKIP_VERIFY=1 | embedding, 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=1 | dataprep-arango-service only | aiohttp connector in keycloak_service_account.py | Keycloak behind NGINX with self-signed cert |
Quick reference — which variables to set by scenario:
| Scenario | NODE_TLS_REJECT_... | OPEA_SSL_... | KEYCLOAK_SSL_... |
|---|---|---|---|
| Local dev, self-signed NGINX, no remote GPU | 0 | — | — |
| Local dev, remote GPU with self-signed cert | 0 | 1 | — |
| Production, real CA certificates on all hosts | — | — | — |
| Production/air-gapped, self-signed NGINX | 0 | 1* | 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 viaGENIE_ADMIN_USERNAME) - Password:
<GENIE_ADMIN_PASSWORD>from.env - Has
adminrealm 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:
| Variable | Default | Description |
|---|---|---|
KEYCLOAK_REALM | genie | Realm name |
KEYCLOAK_CLIENT_ID | genie-app | OIDC client ID |
KEYCLOAK_URL | https://<domain>/auth | Public URL (auto-set from NGINX_PUBLIC_DOMAIN) |
NGINX_HTTPS_PORT | 443 | HTTPS port — included in Keycloak redirect URIs and web origins via KC_PUBLIC_ORIGIN |
KEYCLOAK_ADDITIONAL_REALMS | — | Additional 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, andX-Forwarded-Porton/auth/requests - Kong adds
X-Forwarded-Prefix: /authvia therequest-transformerplugin and strips the/authprefix 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.mdfor automated Swarm deployment. - GPU configuration: See
env.t4andenv.rtx6000for GPU-specific tuning.