# ============================================================================= # ONYX DOCKER COMPOSE # ============================================================================= # This is the default configuration for Onyx. This file is fairly configurable, # also see env.template for possible settings. # # PRODUCTION DEPLOYMENT CHECKLIST: # To convert this setup to a production deployment following best practices, # follow the checklist below. Note that there are other ways to secure the Onyx # deployment so these are not strictly necessary for all teams. # # 1. SECURITY HARDENING: # - Remove all port exposures except nginx (80/443) # - Comment out ports for: api_server, relational_db, index, cache, minio # # 2. SSL/TLS SETUP: # - Uncomment the certbot service (see below) # - Add SSL certificate volumes to nginx service # - Change nginx command from app.conf.template to app.conf.template.prod # # 3. ENVIRONMENT CONFIGURATION: # - Replace env_file with explicit environment variables # # 4. AUTHENTICATION: # - Select an authentication method like Basic, Google OAuth, OIDC, or SAML # # 5. CA CERTIFICATES: # - Uncomment custom CA certificate volumes if needed # # 6. DOMAIN CONFIGURATION: # - Set proper DOMAIN environment variable for nginx # - Configure DNS and SSL certificates # # For a complete production setup, refer to docker-compose.prod.yml # ============================================================================= name: onyx services: api_server: image: ${ONYX_BACKEND_IMAGE:-onyxdotapp/onyx-backend:${IMAGE_TAG:-latest}} build: context: ../../backend dockerfile: Dockerfile command: > /bin/sh -c "alembic upgrade head && echo \"Starting Onyx Api Server\" && uvicorn onyx.main:app --host 0.0.0.0 --port 8080" # Check env.template and copy to .env for env vars env_file: - path: .env required: false depends_on: - relational_db - index - cache - inference_model_server - minio restart: unless-stopped # DEV: To expose ports, either: # 1. Use docker-compose.dev.yml: docker compose -f docker-compose.yml -f docker-compose.dev.yml up -d # 2. Uncomment the ports below # ports: # - "8080:8080" environment: # Auth Settings - AUTH_TYPE=${AUTH_TYPE:-basic} - POSTGRES_HOST=${POSTGRES_HOST:-relational_db} - VESPA_HOST=${VESPA_HOST:-index} - REDIS_HOST=${REDIS_HOST:-cache} - MODEL_SERVER_HOST=${MODEL_SERVER_HOST:-inference_model_server} - S3_ENDPOINT_URL=${S3_ENDPOINT_URL:-http://minio:9000} - S3_AWS_ACCESS_KEY_ID=${S3_AWS_ACCESS_KEY_ID:-minioadmin} - S3_AWS_SECRET_ACCESS_KEY=${S3_AWS_SECRET_ACCESS_KEY:-minioadmin} # PRODUCTION: Uncomment the line below to use if IAM_AUTH is true and you are using iam auth for postgres # volumes: # - ./bundle.pem:/app/bundle.pem:ro extra_hosts: - "host.docker.internal:host-gateway" logging: driver: json-file options: max-size: "50m" max-file: "6" # Optional, only for debugging purposes volumes: - api_server_logs:/var/log/onyx background: image: ${ONYX_BACKEND_IMAGE:-onyxdotapp/onyx-backend:${IMAGE_TAG:-latest}} build: context: ../../backend dockerfile: Dockerfile command: > /bin/sh -c " if [ -f /etc/ssl/certs/custom-ca.crt ]; then update-ca-certificates; fi && /app/scripts/supervisord_entrypoint.sh" env_file: - path: .env required: false depends_on: - relational_db - index - cache - inference_model_server - indexing_model_server restart: unless-stopped environment: - USE_LIGHTWEIGHT_BACKGROUND_WORKER=${USE_LIGHTWEIGHT_BACKGROUND_WORKER:-true} - POSTGRES_HOST=${POSTGRES_HOST:-relational_db} - VESPA_HOST=${VESPA_HOST:-index} - REDIS_HOST=${REDIS_HOST:-cache} - MODEL_SERVER_HOST=${MODEL_SERVER_HOST:-inference_model_server} - INDEXING_MODEL_SERVER_HOST=${INDEXING_MODEL_SERVER_HOST:-indexing_model_server} - S3_ENDPOINT_URL=${S3_ENDPOINT_URL:-http://minio:9000} - S3_AWS_ACCESS_KEY_ID=${S3_AWS_ACCESS_KEY_ID:-minioadmin} - S3_AWS_SECRET_ACCESS_KEY=${S3_AWS_SECRET_ACCESS_KEY:-minioadmin} # PRODUCTION: Uncomment the line below to use if IAM_AUTH is true and you are using iam auth for postgres # volumes: # - ./bundle.pem:/app/bundle.pem:ro extra_hosts: - "host.docker.internal:host-gateway" # Optional, only for debugging purposes volumes: - background_logs:/var/log/onyx logging: driver: json-file options: max-size: "50m" max-file: "6" # PRODUCTION: Uncomment the following lines if you need to include a custom CA certificate # This section enables the use of a custom CA certificate # If present, the custom CA certificate is mounted as a volume # The container checks for its existence and updates the system's CA certificates # This allows for secure communication with services using custom SSL certificates # Optional volume mount for CA certificate # volumes: # # Maps to the CA_CERT_PATH environment variable in the Dockerfile # - ${CA_CERT_PATH:-./custom-ca.crt}:/etc/ssl/certs/custom-ca.crt:ro web_server: image: ${ONYX_WEB_SERVER_IMAGE:-onyxdotapp/onyx-web-server:${IMAGE_TAG:-latest}} build: context: ../../web dockerfile: Dockerfile args: - NEXT_PUBLIC_POSITIVE_PREDEFINED_FEEDBACK_OPTIONS=${NEXT_PUBLIC_POSITIVE_PREDEFINED_FEEDBACK_OPTIONS:-} - NEXT_PUBLIC_NEGATIVE_PREDEFINED_FEEDBACK_OPTIONS=${NEXT_PUBLIC_NEGATIVE_PREDEFINED_FEEDBACK_OPTIONS:-} - NEXT_PUBLIC_DISABLE_LOGOUT=${NEXT_PUBLIC_DISABLE_LOGOUT:-} - NEXT_PUBLIC_DEFAULT_SIDEBAR_OPEN=${NEXT_PUBLIC_DEFAULT_SIDEBAR_OPEN:-} - NEXT_PUBLIC_FORGOT_PASSWORD_ENABLED=${NEXT_PUBLIC_FORGOT_PASSWORD_ENABLED:-} # Enterprise Edition only - NEXT_PUBLIC_THEME=${NEXT_PUBLIC_THEME:-} # DO NOT TURN ON unless you have EXPLICIT PERMISSION from Onyx. - NEXT_PUBLIC_DO_NOT_USE_TOGGLE_OFF_DANSWER_POWERED=${NEXT_PUBLIC_DO_NOT_USE_TOGGLE_OFF_DANSWER_POWERED:-false} - NODE_OPTIONS=${NODE_OPTIONS:-"--max-old-space-size=4096"} env_file: - path: .env required: false depends_on: - api_server restart: unless-stopped environment: - INTERNAL_URL=${INTERNAL_URL:-http://api_server:8080} mcp_server: image: ${ONYX_BACKEND_IMAGE:-onyxdotapp/onyx-backend:${IMAGE_TAG:-latest}} build: context: ../../backend dockerfile: Dockerfile command: > /bin/sh -c "if [ \"${MCP_SERVER_ENABLED:-}\" != \"True\" ] && [ \"${MCP_SERVER_ENABLED:-}\" != \"true\" ]; then echo 'MCP server is disabled (MCP_SERVER_ENABLED=false), skipping...'; exit 0; else exec python -m onyx.mcp_server_main; fi" env_file: - path: .env required: false depends_on: - relational_db - cache restart: "no" environment: - POSTGRES_HOST=${POSTGRES_HOST:-relational_db} - REDIS_HOST=${REDIS_HOST:-cache} # MCP Server Configuration - MCP_SERVER_ENABLED=${MCP_SERVER_ENABLED:-false} - MCP_SERVER_PORT=${MCP_SERVER_PORT:-8090} - MCP_SERVER_CORS_ORIGINS=${MCP_SERVER_CORS_ORIGINS:-} - API_SERVER_PROTOCOL=${API_SERVER_PROTOCOL:-http} - API_SERVER_HOST=${API_SERVER_HOST:-api_server} - API_SERVER_PORT=${API_SERVER_PORT:-8080} extra_hosts: - "host.docker.internal:host-gateway" logging: driver: json-file options: max-size: "50m" max-file: "6" # Optional, only for debugging purposes volumes: - mcp_server_logs:/var/log/onyx inference_model_server: image: ${ONYX_MODEL_SERVER_IMAGE:-onyxdotapp/onyx-model-server:${IMAGE_TAG:-latest}} build: context: ../../backend dockerfile: Dockerfile.model_server # GPU Support: Uncomment the following lines to enable GPU support # Requires nvidia-container-toolkit to be installed on the host # deploy: # resources: # reservations: # devices: # - driver: nvidia # count: all # capabilities: [gpu] command: > /bin/sh -c "if [ \"${DISABLE_MODEL_SERVER:-}\" = \"True\" ] || [ \"${DISABLE_MODEL_SERVER:-}\" = \"true\" ]; then echo 'Skipping service...'; exit 0; else exec uvicorn model_server.main:app --host 0.0.0.0 --port 9000; fi" env_file: - path: .env required: false restart: unless-stopped volumes: # Not necessary, this is just to reduce download time during startup - model_cache_huggingface:/app/.cache/huggingface/ # Optional, only for debugging purposes - inference_model_server_logs:/var/log/onyx logging: driver: json-file options: max-size: "50m" max-file: "6" indexing_model_server: image: ${ONYX_MODEL_SERVER_IMAGE:-onyxdotapp/onyx-model-server:${IMAGE_TAG:-latest}} build: context: ../../backend dockerfile: Dockerfile.model_server # GPU Support: Uncomment the following lines to enable GPU support # Requires nvidia-container-toolkit to be installed on the host # deploy: # resources: # reservations: # devices: # - driver: nvidia # count: all # capabilities: [gpu] command: > /bin/sh -c "if [ \"${DISABLE_MODEL_SERVER:-}\" = \"True\" ] || [ \"${DISABLE_MODEL_SERVER:-}\" = \"true\" ]; then echo 'Skipping service...'; exit 0; else exec uvicorn model_server.main:app --host 0.0.0.0 --port 9000; fi" env_file: - path: .env required: false restart: unless-stopped environment: - INDEXING_ONLY=True volumes: # Not necessary, this is just to reduce download time during startup - indexing_huggingface_model_cache:/app/.cache/huggingface/ # Optional, only for debugging purposes - indexing_model_server_logs:/var/log/onyx logging: driver: json-file options: max-size: "50m" max-file: "6" relational_db: image: postgres:15.2-alpine shm_size: 1g command: -c 'max_connections=250' env_file: - path: .env required: false restart: unless-stopped # PRODUCTION: Override the defaults by passing in the environment variables environment: - POSTGRES_USER=${POSTGRES_USER:-postgres} - POSTGRES_PASSWORD=${POSTGRES_PASSWORD:-password} # DEV: To expose ports, either: # 1. Use docker-compose.dev.yml: docker compose -f docker-compose.yml -f docker-compose.dev.yml up -d # 2. Uncomment the ports below # ports: # - "5432:5432" volumes: - db_volume:/var/lib/postgresql/data # This container name cannot have an underscore in it due to Vespa expectations of the URL index: image: vespaengine/vespa:8.609.39 restart: unless-stopped env_file: - path: .env required: false environment: - VESPA_SKIP_UPGRADE_CHECK=${VESPA_SKIP_UPGRADE_CHECK:-true} # DEV: To expose ports, either: # 1. Use docker-compose.dev.yml: docker compose -f docker-compose.yml -f docker-compose.dev.yml up -d # 2. Uncomment the ports below # ports: # - "19071:19071" # - "8081:8081" volumes: - vespa_volume:/opt/vespa/var logging: driver: json-file options: max-size: "50m" max-file: "6" nginx: image: nginx:1.25.5-alpine restart: unless-stopped # nginx will immediately crash with `nginx: [emerg] host not found in upstream` # if api_server / web_server are not up depends_on: - api_server - web_server env_file: - path: .env required: false environment: - DOMAIN=localhost # Nginx proxy timeout settings (in seconds) - NGINX_PROXY_CONNECT_TIMEOUT=${NGINX_PROXY_CONNECT_TIMEOUT:-300} - NGINX_PROXY_SEND_TIMEOUT=${NGINX_PROXY_SEND_TIMEOUT:-300} - NGINX_PROXY_READ_TIMEOUT=${NGINX_PROXY_READ_TIMEOUT:-300} ports: - "${HOST_PORT_80:-80}:80" - "${HOST_PORT:-3000}:80" # allow for localhost:3000 usage, since that is the norm volumes: - ../data/nginx:/etc/nginx/conf.d # PRODUCTION: Add SSL certificate volumes for HTTPS support: # - ../data/certbot/conf:/etc/letsencrypt # - ../data/certbot/www:/var/www/certbot logging: driver: json-file options: max-size: "50m" max-file: "6" # The specified script waits for the api_server to start up. # Without this we've seen issues where nginx shows no error logs but # does not receive any traffic # NOTE: we have to use dos2unix to remove Carriage Return chars from the file # in order to make this work on both Unix-like systems and windows # PRODUCTION: Change to app.conf.template.prod for production nginx config command: > /bin/sh -c "dos2unix /etc/nginx/conf.d/run-nginx.sh && /etc/nginx/conf.d/run-nginx.sh app.conf.template" cache: image: redis:7.4-alpine restart: unless-stopped # DEV: To expose ports, either: # 1. Use docker-compose.dev.yml: docker compose -f docker-compose.yml -f docker-compose.dev.yml up -d # 2. Uncomment the ports below # ports: # - "6379:6379" # docker silently mounts /data even without an explicit volume mount, which enables # persistence. explicitly setting save and appendonly forces ephemeral behavior. command: redis-server --save "" --appendonly no env_file: - path: .env required: false # Use tmpfs to prevent creation of anonymous volumes for /data tmpfs: - /data minio: image: minio/minio:RELEASE.2025-07-23T15-54-02Z-cpuv1 restart: unless-stopped # DEV: To expose ports, either: # 1. Use docker-compose.dev.yml: docker compose -f docker-compose.yml -f docker-compose.dev.yml up -d # 2. Uncomment the ports below # ports: # - "9004:9000" # - "9005:9001" env_file: - path: .env required: false environment: MINIO_ROOT_USER: ${MINIO_ROOT_USER:-minioadmin} MINIO_ROOT_PASSWORD: ${MINIO_ROOT_PASSWORD:-minioadmin} # Note: we've seen the default bucket creation logic not work in some cases MINIO_DEFAULT_BUCKETS: ${S3_FILE_STORE_BUCKET_NAME:-onyx-file-store-bucket} volumes: - minio_data:/data command: server /data --console-address ":9001" healthcheck: test: ["CMD", "curl", "-f", "http://localhost:9000/minio/health/live"] interval: 30s timeout: 20s retries: 3 code-interpreter: image: onyxdotapp/code-interpreter:${CODE_INTERPRETER_IMAGE_TAG:-latest} entrypoint: ["/bin/bash", "-c"] command: > " if [ \"$${CODE_INTERPRETER_BETA_ENABLED}\" = \"True\" ] || [ \"$${CODE_INTERPRETER_BETA_ENABLED}\" = \"true\" ]; then exec bash ./entrypoint.sh code-interpreter-api; else echo 'Skipping code interpreter'; exec tail -f /dev/null; fi " restart: unless-stopped env_file: - path: .env required: false # Below is needed for the `docker-out-of-docker` execution mode user: root volumes: - /var/run/docker.sock:/var/run/docker.sock # uncomment below + comment out the above to use the `docker-in-docker` execution mode # privileged: true # PRODUCTION: Uncomment the following certbot service for SSL certificate management # certbot: # image: certbot/certbot # restart: unless-stopped # volumes: # - ../data/certbot/conf:/etc/letsencrypt # - ../data/certbot/www:/var/www/certbot # logging: # driver: json-file # options: # max-size: "50m" # max-file: "6" # entrypoint: "/bin/sh -c 'trap exit TERM; while :; do certbot renew; sleep 12h & wait $${!}; done;'" volumes: # Necessary for persisting data for use db_volume: vespa_volume: # Created by the container itself minio_data: # Caches to prevent re-downloading models, not strictly necessary model_cache_huggingface: indexing_huggingface_model_cache: # Logs preserved across container restarts api_server_logs: background_logs: mcp_server_logs: inference_model_server_logs: indexing_model_server_logs: