name: onyx services: api_server: 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" depends_on: - relational_db - opensearch - cache - minio - inference_model_server restart: unless-stopped environment: - AUTH_TYPE=${AUTH_TYPE:-oidc} - POSTGRES_HOST=relational_db - OPENSEARCH_HOST=${OPENSEARCH_HOST:-opensearch} - OPENSEARCH_ADMIN_PASSWORD=${OPENSEARCH_ADMIN_PASSWORD:-StrongPassword123!} - REDIS_HOST=cache - MODEL_SERVER_HOST=${MODEL_SERVER_HOST:-inference_model_server} - CODE_INTERPRETER_BASE_URL=${CODE_INTERPRETER_BASE_URL:-http://code-interpreter:8000} - USE_IAM_AUTH=${USE_IAM_AUTH} - AWS_REGION_NAME=${AWS_REGION_NAME-} - AWS_ACCESS_KEY_ID=${AWS_ACCESS_KEY_ID-} - AWS_SECRET_ACCESS_KEY=${AWS_SECRET_ACCESS_KEY-} # MinIO configuration - S3_ENDPOINT_URL=${S3_ENDPOINT_URL:-http://minio:9000} - S3_AWS_ACCESS_KEY_ID=${S3_AWS_ACCESS_KEY_ID:?set a strong value in .env, not minioadmin} - S3_AWS_SECRET_ACCESS_KEY=${S3_AWS_SECRET_ACCESS_KEY:?set a strong value in .env, not minioadmin} env_file: - path: .env required: false # 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" volumes: - api_server_logs:/var/log/onyx # Shared volume for persistent document storage (Craft file-system mode) - file-system:/app/file-system background: 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" depends_on: - relational_db - opensearch - cache - inference_model_server - indexing_model_server restart: unless-stopped environment: - AUTH_TYPE=${AUTH_TYPE:-oidc} - POSTGRES_HOST=relational_db - OPENSEARCH_HOST=${OPENSEARCH_HOST:-opensearch} - OPENSEARCH_ADMIN_PASSWORD=${OPENSEARCH_ADMIN_PASSWORD:-StrongPassword123!} - REDIS_HOST=cache - MODEL_SERVER_HOST=${MODEL_SERVER_HOST:-inference_model_server} - INDEXING_MODEL_SERVER_HOST=${INDEXING_MODEL_SERVER_HOST:-indexing_model_server} - USE_IAM_AUTH=${USE_IAM_AUTH} - AWS_REGION_NAME=${AWS_REGION_NAME-} - AWS_ACCESS_KEY_ID=${AWS_ACCESS_KEY_ID-} - AWS_SECRET_ACCESS_KEY=${AWS_SECRET_ACCESS_KEY-} # MinIO configuration - S3_ENDPOINT_URL=${S3_ENDPOINT_URL:-http://minio:9000} - S3_AWS_ACCESS_KEY_ID=${S3_AWS_ACCESS_KEY_ID:?set a strong value in .env, not minioadmin} - S3_AWS_SECRET_ACCESS_KEY=${S3_AWS_SECRET_ACCESS_KEY:?set a strong value in .env, not minioadmin} - DISCORD_BOT_TOKEN=${DISCORD_BOT_TOKEN:-} - DISCORD_BOT_INVOKE_CHAR=${DISCORD_BOT_INVOKE_CHAR:-!} # API Server connection for Discord bot message processing - API_SERVER_PROTOCOL=${API_SERVER_PROTOCOL:-http} - API_SERVER_HOST=${API_SERVER_HOST:-api_server} env_file: - path: .env required: false # 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" volumes: - background_logs:/var/log/onyx # Shared volume for persistent document storage (Craft file-system mode) - file-system:/app/file-system logging: driver: json-file options: max-size: "50m" max-file: "6" # 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 # 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: onyxdotapp/onyx-web-server:${IMAGE_TAG:-latest} build: context: ../../web dockerfile: Dockerfile args: - NEXT_PUBLIC_DISABLE_LOGOUT=${NEXT_PUBLIC_DISABLE_LOGOUT:-} - NEXT_PUBLIC_THEME=${NEXT_PUBLIC_THEME:-} - NEXT_PUBLIC_FORGOT_PASSWORD_ENABLED=${NEXT_PUBLIC_FORGOT_PASSWORD_ENABLED:-} depends_on: - api_server restart: unless-stopped environment: - INTERNAL_URL=http://api_server:8080 env_file: - path: .env required: false logging: driver: json-file options: max-size: "50m" max-file: "6" # Uncomment the block below to enable the MCP server for Onyx. # mcp_server: # 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=relational_db # - 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} # extra_hosts: # - "host.docker.internal:host-gateway" # logging: # driver: json-file # options: # max-size: "50m" # max-file: "6" # volumes: # - mcp_server_logs:/var/log/onyx relational_db: image: postgres:15.2-alpine shm_size: 1g command: -c 'max_connections=250' restart: unless-stopped # POSTGRES_USER and POSTGRES_PASSWORD should be set in .env file env_file: - path: .env required: false volumes: - db_volume:/var/lib/postgresql/data logging: driver: json-file options: max-size: "50m" max-file: "6" inference_model_server: image: onyxdotapp/onyx-model-server:${IMAGE_TAG:-latest} build: context: ../../backend dockerfile: Dockerfile.model_server restart: unless-stopped environment: - MIN_THREADS_ML_MODELS=${MIN_THREADS_ML_MODELS:-} # Set to debug to get more fine-grained logs - LOG_LEVEL=${LOG_LEVEL:-info} 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: onyxdotapp/onyx-model-server:${IMAGE_TAG:-latest} build: context: ../../backend dockerfile: Dockerfile.model_server restart: unless-stopped environment: - MIN_THREADS_ML_MODELS=${MIN_THREADS_ML_MODELS:-} - INDEXING_ONLY=True # Set to debug to get more fine-grained logs - LOG_LEVEL=${LOG_LEVEL:-info} - VESPA_SEARCHER_THREADS=${VESPA_SEARCHER_THREADS:-1} 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" opensearch: image: opensearchproject/opensearch:3.6.0 restart: unless-stopped # OpenSearch is the search backend and is enabled by default. To run against # an external OpenSearch instance, set OPENSEARCH_HOST in your env and # remove this service from the compose file (or skip it via the service list # when running `docker compose up`). environment: # We need discovery.type=single-node so that OpenSearch doesn't try # forming a cluster and waiting for other nodes to become live. - discovery.type=single-node - OPENSEARCH_INITIAL_ADMIN_PASSWORD=${OPENSEARCH_ADMIN_PASSWORD:-StrongPassword123!} # This and the JVM config below come from the example in https://docs.opensearch.org/latest/install-and-configure/install-opensearch/docker/ # We do this to avoid unstable performance from page swaps. - bootstrap.memory_lock=true # Disable JVM heap memory swapping. # Java heap should be ~50% of memory limit. For now we assume a limit of # 4g although in practice the container can request more than this. # See https://opster.com/guides/opensearch/opensearch-basics/opensearch-heap-size-usage-and-jvm-garbage-collection/ # Xms is the starting size, Xmx is the maximum size. These should be the # same. - "OPENSEARCH_JAVA_OPTS=-Xms2g -Xmx2g" volumes: - opensearch-data:/usr/share/opensearch/data # These come from the example in https://docs.opensearch.org/latest/install-and-configure/install-opensearch/docker/ ulimits: # Similarly to bootstrap.memory_lock, we don't want to impose limits on # how much memory a process can lock from being swapped. memlock: soft: -1 # Set memlock to unlimited (no soft or hard limit). hard: -1 nofile: soft: 65536 # Maximum number of open files for the opensearch user - set to at least 65536. hard: 65536 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 ports: - "80:80" - "443:443" volumes: - ../data/nginx:/nginx-templates:ro - ../data/certbot/conf:/etc/letsencrypt - ../data/certbot/www:/var/www/certbot logging: driver: json-file options: max-size: "50m" max-file: "6" command: > /bin/sh -c "rm -f /etc/nginx/conf.d/default.conf && cp -a /nginx-templates/. /etc/nginx/conf.d/ && sed 's/\r$//' /etc/nginx/conf.d/run-nginx.sh > /tmp/run-nginx.sh && chmod +x /tmp/run-nginx.sh && /tmp/run-nginx.sh app.conf.template.prod" env_file: - .env.nginx environment: # 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} healthcheck: test: [ "CMD", "wget", "--quiet", "--tries=1", "--spider", "http://127.0.0.1/nginx-health", ] interval: 30s timeout: 10s retries: 5 start_period: 30s # follows https://pentacent.medium.com/nginx-and-lets-encrypt-with-docker-in-less-than-5-minutes-b4b8a60d3a71 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;'" minio: image: minio/minio:RELEASE.2025-07-23T15-54-02Z-cpuv1 restart: unless-stopped environment: MINIO_ROOT_USER: ${MINIO_ROOT_USER:?set a strong value in .env, not minioadmin} MINIO_ROOT_PASSWORD: ${MINIO_ROOT_PASSWORD:?set a strong value in .env, not minioadmin} 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", "mc", "ready", "local"] interval: 30s timeout: 20s retries: 3 cache: image: redis:7.4-alpine restart: unless-stopped # 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 # Use tmpfs to prevent creation of anonymous volumes for /data tmpfs: - /data code-interpreter: image: onyxdotapp/code-interpreter:${CODE_INTERPRETER_IMAGE_TAG:-latest} command: ["bash", "./entrypoint.sh", "code-interpreter-api"] 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 volumes: db_volume: minio_data: model_cache_huggingface: indexing_huggingface_model_cache: # for logs that we don't want to lose on container restarts api_server_logs: background_logs: inference_model_server_logs: indexing_model_server_logs: # mcp_server_logs: # Shared volume for persistent document storage (Craft file-system mode) file-system: # Persistent data for OpenSearch. opensearch-data: