This guide covers deploying Cortex to production environments using Docker Compose and Coolify.
Prerequisites
- A server with Docker and Docker Compose installed
- At least 4GB RAM (8GB+ recommended for production)
- Domain name with DNS configured (for HTTPS)
- OpenAI API key (or alternative LLM provider)
Docker Compose Deployment
Production Configuration
Create a docker-compose.prod.yml:
Code
Deploy
Code
Coolify Deployment
Cortex includes Coolify-ready configuration for easy deployment.
Setup Steps
-
Create a New Resource
- Go to your Coolify dashboard
- Click "New Resource" → "Docker Compose"
-
Configure Git Repository
- Repository:
https://github.com/mocaOS/cortex-app - Branch:
main - Compose file:
coolify/docker-compose.coolify.yml
- Repository:
-
Set Environment Variables
In Coolify's environment section, add:
Code -
Configure Domains
- Frontend:
cortex.yourdomain.com - Backend API:
api.cortex.yourdomain.com
- Frontend:
-
Deploy
- Click "Deploy"
- Wait for containers to start
- Coolify handles SSL certificates automatically
Coolify Compose File
The coolify/docker-compose.coolify.yml includes Coolify-specific magic variables:
Code
Nginx Configuration
For manual deployments, configure Nginx as a reverse proxy:
Code
Health Checks
Backend Health
Code
Expected response:
Code
A degraded instance (Neo4j unreachable or schema not yet confirmed) answers
HTTP 503 with "status": "degraded" — so curl -f, the compose
healthchecks, depends_on: service_healthy, and health-aware proxies all
gate on it. The compose healthchecks carry a 60s start_period to cover
schema initialization at startup.
Neo4j Health
Code
Redeploy Safety
Before restarting or upgrading an instance (e.g. rolling out a new version), confirm it isn't mid-flight on destructible work:
Code
Code
safe_to_redeploy is false while documents are processing/extracting, background tasks are running (a restart interrupts them — their records survive in Neo4j and are marked failed so pollers get a real answer, but the work must be re-run), or an AskAI/research query is in flight (a restart kills the stream). Pending documents persist in Neo4j and resume after a restart, so they never block. Poll this endpoint in deploy automation and proceed only once safe_to_redeploy is true. Requires manage permission.
Scaling
Backend Scaling
Scale the backend vertically (more CPU/RAM for the single backend container):
Code
Do not run multiple backend replicas behind one Neo4j instance: task progress, SSE streams, and rate-limit buckets are tracked in process memory, so replicas would see inconsistent state. For more capacity per deployment, scale vertically; for more isolation, run one full Cortex stack per workload (the container-per-tenant model Cortex Cloud uses).
Neo4j Scaling
For large knowledge bases, increase Neo4j resources:
Code
Backup & Recovery
The backup sidecar (recommended)
The Dokploy and Coolify composes include the backup sidecar; on the standalone prod stack add the overlay:
Code
Nightly (BACKUP_INTERVAL_SECONDS), online and verified: a server-side APOC
logical export of the graph (graph.cypher.gz) plus a tar of the file
volumes, with checksums and a .complete marker. Retention
(BACKUP_RETENTION_DAYS, default 7) only rotates after a verified success and
never removes the newest complete backup. The sidecar's healthcheck goes
unhealthy when the newest verified backup is older than twice the interval, so
a silently failing backup shows up in docker ps.
Code
Restore
Code
File volumes are mounted read-only in the sidecar — restore them from the host
with a throwaway container (full runbook in ops/backup/restore.sh). Backups
live in the backups volume on the same host; ship them off-host
(restic/rclone) for disaster recovery.
Manual physical dump (alternative, requires downtime)
Code
The two formats are not interchangeable: a .dump restores only via
neo4j-admin database load, a sidecar export only via restore.sh.
Monitoring
Logs
Code
Resource Usage
Code
Security Checklist
- Strong
NEO4J_PASSWORDset -
ADMIN_API_KEYrotated from default -
SESSION_SECRETis 32+ characters - HTTPS enabled with valid certificates
- API key authentication enabled (
REQUIRE_API_KEY=true) - CORS configured for your domains only
- Firewall blocks direct access to Neo4j (7687)
- Regular backups scheduled