# ================================================================== # WARNING: This file is auto-generated by generate_docker_compose # Do not modify this file directly. Instead, update the .env.example # or docker-compose-template.yaml and regenerate this file. # ================================================================== # Shared configuration using YAML anchors and env_file x-shared-api-config: &shared-api-config env_file: - path: ./envs/core-services/shared.env required: false - path: ./envs/core-services/api-worker.env required: false - path: ./envs/security.env required: false - path: ./envs/databases/db-postgres.env required: false - path: ./envs/databases/db-mysql.env required: false - path: ./envs/databases/redis.env required: false - path: ./envs/vectorstores/weaviate.env required: false - path: ./envs/vectorstores/qdrant.env required: false - path: ./envs/vectorstores/oceanbase.env required: false - path: ./envs/vectorstores/seekdb.env required: false - path: ./envs/vectorstores/couchbase.env required: false - path: ./envs/vectorstores/pgvector.env required: false - path: ./envs/vectorstores/vastbase.env required: false - path: ./envs/vectorstores/pgvecto-rs.env required: false - path: ./envs/vectorstores/chroma.env required: false - path: ./envs/vectorstores/iris.env required: false - path: ./envs/vectorstores/oracle.env required: false - path: ./envs/vectorstores/opengauss.env required: false - path: ./envs/vectorstores/myscale.env required: false - path: ./envs/vectorstores/matrixone.env required: false - path: ./envs/vectorstores/elasticsearch.env required: false - path: ./envs/vectorstores/opensearch.env required: false - path: ./envs/vectorstores/milvus.env required: false - path: ./envs/infrastructure/nginx.env required: false - path: ./envs/infrastructure/certbot.env required: false - path: ./envs/infrastructure/ssrf-proxy.env required: false - path: ./envs/infrastructure/etcd.env required: false - path: ./envs/infrastructure/minio.env required: false - path: ./envs/infrastructure/milvus-standalone.env required: false - path: ./envs/core-services/api.env required: false - ./.env - path: ./envs/vectorstores/${VECTOR_STORE:-weaviate}.defaults required: false - ./envs/core-services/api-worker.defaults - ./envs/core-services/api.defaults networks: - ssrf_proxy_network - default restart: always x-shared-worker-config: &shared-worker-config env_file: - path: ./envs/core-services/shared.env required: false - path: ./envs/core-services/api-worker.env required: false - path: ./envs/security.env required: false - path: ./envs/databases/db-postgres.env required: false - path: ./envs/databases/db-mysql.env required: false - path: ./envs/databases/redis.env required: false - path: ./envs/vectorstores/weaviate.env required: false - path: ./envs/vectorstores/qdrant.env required: false - path: ./envs/vectorstores/oceanbase.env required: false - path: ./envs/vectorstores/seekdb.env required: false - path: ./envs/vectorstores/couchbase.env required: false - path: ./envs/vectorstores/pgvector.env required: false - path: ./envs/vectorstores/vastbase.env required: false - path: ./envs/vectorstores/pgvecto-rs.env required: false - path: ./envs/vectorstores/chroma.env required: false - path: ./envs/vectorstores/iris.env required: false - path: ./envs/vectorstores/oracle.env required: false - path: ./envs/vectorstores/opengauss.env required: false - path: ./envs/vectorstores/myscale.env required: false - path: ./envs/vectorstores/matrixone.env required: false - path: ./envs/vectorstores/elasticsearch.env required: false - path: ./envs/vectorstores/opensearch.env required: false - path: ./envs/vectorstores/milvus.env required: false - path: ./envs/infrastructure/nginx.env required: false - path: ./envs/infrastructure/certbot.env required: false - path: ./envs/infrastructure/ssrf-proxy.env required: false - path: ./envs/infrastructure/etcd.env required: false - path: ./envs/infrastructure/minio.env required: false - path: ./envs/infrastructure/milvus-standalone.env required: false - path: ./envs/core-services/worker.env required: false - ./.env - path: ./envs/vectorstores/${VECTOR_STORE:-weaviate}.defaults required: false - ./envs/core-services/api-worker.defaults - ./envs/core-services/worker.defaults networks: - ssrf_proxy_network - default restart: always # KNOWLEDGE_FS_WORKER_IMAGE must bundle the KnowledgeFS engine at KNOWLEDGE_FS_BACKGROUND_ENGINE_PATH; # standard Dify API images do not, and the worker entrypoint exits without it. x-knowledge-fs-celery-worker: &knowledge-fs-celery-worker image: ${KNOWLEDGE_FS_WORKER_IMAGE:-langgenius/dify-api:1.19.0} profiles: - knowledge-fs-celery restart: always # Match the two-hour default task deadline plus shutdown margin for long document jobs. stop_grace_period: 125m env_file: - path: ./envs/core-services/shared.env required: false - path: ./envs/core-services/api-worker.env required: false - path: ./envs/security.env required: false - path: ./envs/databases/db-postgres.env required: false - path: ./envs/databases/db-mysql.env required: false - path: ./envs/databases/redis.env required: false - path: ./envs/core-services/knowledge-fs.env required: false - path: ./envs/core-services/knowledge-fs-worker.env required: false - path: ./envs/core-services/worker.env required: false - ./.env - ./envs/core-services/api-worker.defaults - ./envs/core-services/worker.defaults environment: &knowledge-fs-celery-environment MODE: worker MIGRATION_ENABLED: "false" CELERY_WORKER_POOL: prefork CELERY_PREFETCH_MULTIPLIER: "1" # Periodic database sweeps are cheap Celery tasks; the standard 50-task recycling would # repeatedly discard a warm engine even while idle. MAX_TASKS_PER_CHILD: ${KNOWLEDGE_FS_WORKER_MAX_TASKS_PER_CHILD:-1000} DIFY_INNER_API_URL: ${PLUGIN_DIFY_INNER_API_URL:-http://api:5001} DIFY_INNER_API_KEY: ${PLUGIN_DIFY_INNER_API_KEY:-QaHbTe77CtuXmsfyhR7+vRjI/+XbV1AaFy691iy+kGDv2Jvy0/eAh8Y1} KNOWLEDGE_INTEGRATED_MODE_ENABLED: ${KNOWLEDGE_INTEGRATED_MODE_ENABLED:-true} KNOWLEDGE_DIRECT_UPLOAD_ENABLED: ${KNOWLEDGE_DIRECT_UPLOAD_ENABLED:-true} depends_on: api: condition: service_healthy networks: - default x-shared-worker-beat-config: &shared-worker-beat-config env_file: - path: ./envs/core-services/shared.env required: false - path: ./envs/core-services/api-worker.env required: false - path: ./envs/security.env required: false - path: ./envs/databases/db-postgres.env required: false - path: ./envs/databases/db-mysql.env required: false - path: ./envs/databases/redis.env required: false - path: ./envs/vectorstores/weaviate.env required: false - path: ./envs/vectorstores/qdrant.env required: false - path: ./envs/vectorstores/oceanbase.env required: false - path: ./envs/vectorstores/seekdb.env required: false - path: ./envs/vectorstores/couchbase.env required: false - path: ./envs/vectorstores/pgvector.env required: false - path: ./envs/vectorstores/vastbase.env required: false - path: ./envs/vectorstores/pgvecto-rs.env required: false - path: ./envs/vectorstores/chroma.env required: false - path: ./envs/vectorstores/iris.env required: false - path: ./envs/vectorstores/oracle.env required: false - path: ./envs/vectorstores/opengauss.env required: false - path: ./envs/vectorstores/myscale.env required: false - path: ./envs/vectorstores/matrixone.env required: false - path: ./envs/vectorstores/elasticsearch.env required: false - path: ./envs/vectorstores/opensearch.env required: false - path: ./envs/vectorstores/milvus.env required: false - path: ./envs/infrastructure/nginx.env required: false - path: ./envs/infrastructure/certbot.env required: false - path: ./envs/infrastructure/ssrf-proxy.env required: false - path: ./envs/infrastructure/etcd.env required: false - path: ./envs/infrastructure/minio.env required: false - path: ./envs/infrastructure/milvus-standalone.env required: false - path: ./envs/core-services/worker-beat.env required: false - ./.env - path: ./envs/vectorstores/${VECTOR_STORE:-weaviate}.defaults required: false - ./envs/core-services/api-worker.defaults - ./envs/core-services/worker-beat.defaults networks: - ssrf_proxy_network - default restart: always services: # Init container to fix permissions init_permissions: image: busybox:latest command: - sh - -c - | for DIR in /app/api/storage /app/plugin_daemon/storage; do FLAG_FILE="$${DIR}/.init_permissions" if [ -f "$${FLAG_FILE}" ]; then echo "Permissions already initialized for $${DIR}." continue fi echo "Initializing permissions for $${DIR}" chown -R 1001:1001 "$${DIR}" && touch "$${FLAG_FILE}" || exit 1 done echo "Permissions initialized. Exiting." volumes: - ./volumes/app/storage:/app/api/storage - ./volumes/plugin_daemon:/app/plugin_daemon/storage restart: "no" # API service api: <<: *shared-api-config image: langgenius/dify-api:1.19.0 environment: MODE: api depends_on: init_permissions: condition: service_completed_successfully db_postgres: condition: service_healthy required: false db_mysql: condition: service_healthy required: false oceanbase: condition: service_healthy required: false seekdb: condition: service_healthy required: false redis: condition: service_started agent_backend: condition: service_started volumes: # Mount the storage directory to the container, for storing user files. - ./volumes/app/storage:/app/api/storage healthcheck: test: ["CMD", "curl", "-f", "http://localhost:5001/health"] interval: 30s timeout: 5s retries: 3 start_period: 30s networks: - ssrf_proxy_network - default # WebSocket service for workflow collaboration. api_websocket: <<: *shared-api-config image: langgenius/dify-api:1.19.0 profiles: - collaboration environment: MODE: api # Resolve websocket settings after loading the shared/API service files. entrypoint: - /bin/bash - -c - | export SERVER_WORKER_AMOUNT="$${API_WEBSOCKET_WORKER_AMOUNT:-1}" export SERVER_WORKER_CLASS="$${API_WEBSOCKET_WORKER_CLASS:-geventwebsocket.gunicorn.workers.GeventWebSocketWorker}" export SERVER_WORKER_CONNECTIONS="$${API_WEBSOCKET_WORKER_CONNECTIONS:-1000}" export GUNICORN_TIMEOUT="$${API_WEBSOCKET_GUNICORN_TIMEOUT:-360}" exec /bin/bash /entrypoint.sh depends_on: init_permissions: condition: service_completed_successfully db_postgres: condition: service_healthy required: false db_mysql: condition: service_healthy required: false redis: condition: service_started volumes: # Mount the storage directory to the container, for storing user files. - ./volumes/app/storage:/app/api/storage networks: - ssrf_proxy_network - default # The Celery worker for processing all queues (dataset, workflow, mail, etc.) worker: <<: *shared-worker-config image: langgenius/dify-api:1.19.0 environment: MODE: worker depends_on: init_permissions: condition: service_completed_successfully db_postgres: condition: service_healthy required: false db_mysql: condition: service_healthy required: false oceanbase: condition: service_healthy required: false seekdb: condition: service_healthy required: false redis: condition: service_started agent_backend: condition: service_started volumes: # Mount the storage directory to the container, for storing user files. - ./volumes/app/storage:/app/api/storage healthcheck: test: ["CMD-SHELL", "celery -A celery_healthcheck.celery inspect ping"] interval: ${COMPOSE_WORKER_HEALTHCHECK_INTERVAL:-30s} timeout: ${COMPOSE_WORKER_HEALTHCHECK_TIMEOUT:-30s} retries: 3 start_period: 60s disable: ${COMPOSE_WORKER_HEALTHCHECK_DISABLED:-true} networks: - ssrf_proxy_network - default # Dedicated worker for click-time Dataset snapshots and KnowledgeFS upgrade orchestration. # Keep this queue out of the generic worker so large file copies cannot starve existing jobs. worker_knowledge_fs_upgrade: <<: *shared-worker-config image: langgenius/dify-api:1.19.0 profiles: - knowledge-fs environment: MODE: worker CELERY_WORKER_QUEUES: knowledge_fs_upgrade CELERY_WORKER_POOL: prefork CELERY_WORKER_CONCURRENCY: ${KNOWLEDGE_FS_UPGRADE_WORKER_AMOUNT:-2} CELERY_PREFETCH_MULTIPLIER: 1 depends_on: init_permissions: condition: service_completed_successfully db_postgres: condition: service_healthy required: false db_mysql: condition: service_healthy required: false redis: condition: service_started volumes: - ./volumes/app/storage:/app/api/storage networks: - ssrf_proxy_network - default # worker_beat service # Celery beat for scheduling periodic tasks. worker_beat: <<: *shared-worker-beat-config image: langgenius/dify-api:1.19.0 environment: MODE: beat depends_on: init_permissions: condition: service_completed_successfully db_postgres: condition: service_healthy required: false db_mysql: condition: service_healthy required: false oceanbase: condition: service_healthy required: false seekdb: condition: service_healthy required: false redis: condition: service_started healthcheck: test: ["CMD-SHELL", "celery -A celery_healthcheck.celery inspect ping"] interval: ${COMPOSE_WORKER_HEALTHCHECK_INTERVAL:-30s} timeout: ${COMPOSE_WORKER_HEALTHCHECK_TIMEOUT:-30s} retries: 3 start_period: 60s disable: ${COMPOSE_WORKER_HEALTHCHECK_DISABLED:-true} networks: - ssrf_proxy_network - default # Frontend web application. web: image: langgenius/dify-web:1.19.0 restart: always env_file: - path: ./envs/core-services/web.env required: false - path: ./envs/security.env required: false - ./.env - ./envs/core-services/web.defaults # The PostgreSQL database. db_postgres: env_file: - path: ./envs/databases/db-postgres.env required: false - ./envs/databases/db-postgres.defaults image: postgres:15-alpine profiles: - postgresql restart: always # Expand service-file settings inside the container, preserving the image's initialization. command: - sh - -c - >- exec docker-entrypoint.sh postgres -c "max_connections=$$POSTGRES_MAX_CONNECTIONS" -c "shared_buffers=$$POSTGRES_SHARED_BUFFERS" -c "work_mem=$$POSTGRES_WORK_MEM" -c "maintenance_work_mem=$$POSTGRES_MAINTENANCE_WORK_MEM" -c "effective_cache_size=$$POSTGRES_EFFECTIVE_CACHE_SIZE" -c "statement_timeout=$$POSTGRES_STATEMENT_TIMEOUT" -c "idle_in_transaction_session_timeout=$$POSTGRES_IDLE_IN_TRANSACTION_SESSION_TIMEOUT" volumes: - ./volumes/db/data:/var/lib/postgresql/data healthcheck: test: ["CMD-SHELL", 'pg_isready -h db_postgres -U "$$POSTGRES_USER" -d "$$POSTGRES_DB"'] interval: 1s timeout: 3s retries: 60 # The mysql database. db_mysql: env_file: - path: ./envs/databases/db-postgres.env required: false - path: ./envs/databases/db-mysql.env required: false - ./envs/databases/db-mysql.defaults image: mysql:8.0 profiles: - mysql restart: always command: - sh - -c - >- exec docker-entrypoint.sh mysqld "--max_connections=$$MYSQL_MAX_CONNECTIONS" "--innodb_buffer_pool_size=$$MYSQL_INNODB_BUFFER_POOL_SIZE" "--innodb_log_file_size=$$MYSQL_INNODB_LOG_FILE_SIZE" "--innodb_flush_log_at_trx_commit=$$MYSQL_INNODB_FLUSH_LOG_AT_TRX_COMMIT" volumes: - ${MYSQL_HOST_VOLUME:-./volumes/mysql/data}:/var/lib/mysql healthcheck: test: ["CMD-SHELL", 'mysqladmin ping -u root -p"$$MYSQL_ROOT_PASSWORD"'] interval: 1s timeout: 3s retries: 30 # The redis cache. redis: env_file: - path: ./envs/databases/redis.env required: false - ./envs/databases/redis.defaults image: redis:6-alpine restart: always volumes: # Mount the redis data directory to the container. - ./volumes/redis/data:/data # Set the redis password when startup redis server. command: ["sh", "-c", 'exec docker-entrypoint.sh redis-server --requirepass "$$REDIS_PASSWORD"'] healthcheck: test: [ "CMD-SHELL", "redis-cli ping | grep -q PONG", ] # The DifySandbox sandbox: image: langgenius/dify-sandbox:0.2.15 restart: always env_file: - path: ./envs/core-services/sandbox.env required: false - path: ./envs/security.env required: false - ./.env - ./envs/core-services/sandbox.defaults volumes: - ./volumes/sandbox/dependencies:/dependencies - ./volumes/sandbox/conf:/conf healthcheck: test: ["CMD-SHELL", 'curl -f "http://localhost:$$SANDBOX_PORT/health"'] networks: - ssrf_proxy_network # Local sandbox for Dify Agent shell workspaces. # Network isolation: local_sandbox has NO direct route to `api`. Its only # networks are `agent_sandbox_network` (so agent_backend can reach it on 5004 # for shellctl, and it can reach agent_backend directly) and # `local_sandbox_proxy_network` (so its egress is forced through # agent_ssrf_proxy). # All non-agent_backend/localhost traffic goes through the Squid forward proxy # on port 3128, which only allows agent_backend /agent-stub/ and the Dify API # /files/* endpoints (see ssrf_proxy/squid-agent.conf.template). local_sandbox: image: langgenius/dify-agent-local-sandbox:1.19.0 command: ["shellctl", "serve", "--listen", "0.0.0.0:5004", "--require-auth-token"] restart: always env_file: - path: ./envs/core-services/local-sandbox.env required: false environment: - SHELLCTL_AUTH_TOKEN=${DIFY_AGENT_LOCAL_SANDBOX_AUTH_TOKEN:-${DIFY_AGENT_SHELLCTL_AUTH_TOKEN:-}} - HTTP_PROXY=http://agent_ssrf_proxy:3128 - HTTPS_PROXY=http://agent_ssrf_proxy:3128 - NO_PROXY=localhost,127.0.0.1 volumes: - dify_agent_local_sandbox_home:/home/dify - dify_agent_local_sandbox_workspace:/workspace healthcheck: test: ["CMD", "curl", "-f", "http://localhost:5004/healthz"] interval: 30s timeout: 5s retries: 3 start_period: 10s networks: - agent_sandbox_network - local_sandbox_proxy_network # plugin daemon plugin_daemon: image: langgenius/dify-plugin-daemon:0.6.14-local restart: always env_file: - path: ./envs/core-services/shared.env required: false - path: ./envs/core-services/api-worker.env required: false - path: ./envs/core-services/plugin-daemon.env required: false - path: ./envs/security.env required: false - path: ./envs/databases/db-postgres.env required: false - path: ./envs/databases/db-mysql.env required: false - path: ./envs/databases/redis.env required: false - ./.env - ./envs/core-services/api-worker.defaults - ./envs/core-services/plugin-daemon.defaults networks: - ssrf_proxy_network - default environment: PLUGIN_REMOTE_INSTALLING_PORT: ${PLUGIN_DEBUGGING_PORT:-5003} ports: - "${EXPOSE_PLUGIN_DEBUGGING_PORT:-5003}:${PLUGIN_DEBUGGING_PORT:-5003}" volumes: - ./volumes/plugin_daemon:/app/storage depends_on: init_permissions: condition: service_completed_successfully db_postgres: condition: service_healthy required: false db_mysql: condition: service_healthy required: false oceanbase: condition: service_healthy required: false seekdb: condition: service_healthy required: false # KnowledgeFS integration service. It is opt-in: add `knowledge-fs` to COMPOSE_PROFILES only after # configuring envs/core-services/knowledge-fs.env (durable DATABASE_URL, migrations, verifier). # It has no public port or nginx route. The published image is linux/amd64 only. knowledge_fs: image: ${KNOWLEDGE_FS_API_IMAGE:-langgenius/dify-knowledge-fs-api:deploy-konwledge} profiles: - knowledge-fs restart: always env_file: - path: ./envs/core-services/knowledge-fs.env required: false environment: DIFY_ROOT_KNOWLEDGE_BACKGROUND_EXECUTION_OVERRIDE: ${KNOWLEDGE_BACKGROUND_EXECUTION-} DIFY_ROOT_KNOWLEDGE_DOCUMENT_MATERIALIZATION_MAX_CONCURRENCY_OVERRIDE: ${KNOWLEDGE_DOCUMENT_MATERIALIZATION_MAX_CONCURRENCY-} # Canonical values stay in the service env/image. These whitelisted proxies let an explicitly # set docker/.env value override them without injecting the complete root environment. DIFY_ROOT_KNOWLEDGE_PDF_RASTERIZER_OVERRIDE: ${KNOWLEDGE_PDF_RASTERIZER-} DIFY_ROOT_KNOWLEDGE_PDF_RASTERIZER_DPI_OVERRIDE: ${KNOWLEDGE_PDF_RASTERIZER_DPI-} DIFY_ROOT_KNOWLEDGE_PDF_RASTERIZER_THUMBNAIL_DPI_OVERRIDE: ${KNOWLEDGE_PDF_RASTERIZER_THUMBNAIL_DPI-} DIFY_ROOT_KNOWLEDGE_PDF_RASTERIZER_TIMEOUT_MS_OVERRIDE: ${KNOWLEDGE_PDF_RASTERIZER_TIMEOUT_MS-} DIFY_ROOT_KNOWLEDGE_PDF_RASTERIZER_MAX_ASSETS_OVERRIDE: ${KNOWLEDGE_PDF_RASTERIZER_MAX_ASSETS-} DIFY_ROOT_KNOWLEDGE_PDF_RASTERIZER_MAX_CONCURRENCY_OVERRIDE: ${KNOWLEDGE_PDF_RASTERIZER_MAX_CONCURRENCY-} DIFY_INNER_API_URL: ${PLUGIN_DIFY_INNER_API_URL:-http://api:5001} DIFY_INNER_API_KEY: ${PLUGIN_DIFY_INNER_API_KEY:-QaHbTe77CtuXmsfyhR7+vRjI/+XbV1AaFy691iy+kGDv2Jvy0/eAh8Y1} KNOWLEDGE_INTEGRATED_MODE_ENABLED: ${KNOWLEDGE_INTEGRATED_MODE_ENABLED:-true} # Workspace staging uses KFS upload sessions for quota reservation and durable admission, # while the document bytes remain in Dify's unified object storage. KNOWLEDGE_DIRECT_UPLOAD_ENABLED: ${KNOWLEDGE_DIRECT_UPLOAD_ENABLED:-true} NODE_ENV: production PORT: 8787 depends_on: api: condition: service_healthy expose: - "8787" healthcheck: test: [ "CMD", "node", "-e", "fetch('http://127.0.0.1:8787/ready').then(r=>{if(!r.ok)process.exit(1)}).catch(()=>process.exit(1))", ] interval: 10s timeout: 5s retries: 3 start_period: 30s networks: - default # All five worker roles share the Dify Celery broker and need a worker image with the bundled # KnowledgeFS engine. Only the optional knowledge-fs-celery profile starts them; set # KNOWLEDGE_BACKGROUND_EXECUTION=celery in the root env first. knowledge_fs_document_worker: <<: *knowledge-fs-celery-worker environment: <<: *knowledge-fs-celery-environment # Compose shares document capacity; Cloud deployments can use separate consumer pools. CELERY_WORKER_QUEUES: knowledge_fs_document,knowledge_fs_priority_document CELERY_WORKER_CONCURRENCY: ${KNOWLEDGE_FS_DOCUMENT_WORKER_CONCURRENCY:-2} knowledge_fs_source_worker: <<: *knowledge-fs-celery-worker environment: <<: *knowledge-fs-celery-environment CELERY_WORKER_QUEUES: knowledge_fs_source CELERY_WORKER_CONCURRENCY: ${KNOWLEDGE_FS_SOURCE_WORKER_CONCURRENCY:-1} knowledge_fs_research_worker: <<: *knowledge-fs-celery-worker environment: <<: *knowledge-fs-celery-environment CELERY_WORKER_QUEUES: knowledge_fs_research CELERY_WORKER_CONCURRENCY: ${KNOWLEDGE_FS_RESEARCH_WORKER_CONCURRENCY:-2} knowledge_fs_maintenance_worker: <<: *knowledge-fs-celery-worker environment: <<: *knowledge-fs-celery-environment CELERY_WORKER_QUEUES: knowledge_fs_maintenance CELERY_WORKER_CONCURRENCY: ${KNOWLEDGE_FS_MAINTENANCE_WORKER_CONCURRENCY:-1} knowledge_fs_dispatch_worker: <<: *knowledge-fs-celery-worker environment: <<: *knowledge-fs-celery-environment CELERY_WORKER_QUEUES: knowledge_fs_dispatch CELERY_WORKER_CONCURRENCY: ${KNOWLEDGE_FS_DISPATCH_WORKER_CONCURRENCY:-1} # Dify Agent backend service. agent_backend: image: langgenius/dify-agent-backend:1.19.0 restart: always env_file: - path: ./envs/security.env required: false - path: ./envs/databases/redis.env required: false - path: ./envs/core-services/dify-agent.env required: false - ./.env - ./envs/core-services/dify-agent.defaults environment: DIFY_AGENT_LOCAL_SANDBOX_AUTH_TOKEN: ${DIFY_AGENT_LOCAL_SANDBOX_AUTH_TOKEN:-${DIFY_AGENT_SHELLCTL_AUTH_TOKEN:-}} depends_on: redis: condition: service_started plugin_daemon: condition: service_started networks: - default # Shared internal network with local_sandbox so agent_backend can reach it # on port 5004 (shellctl entrypoint) while local_sandbox stays off `default`. - agent_sandbox_network # Dedicated SSRF proxy for the dify-agent local_sandbox. agent_ssrf_proxy: image: ubuntu/squid:latest restart: always volumes: - ./ssrf_proxy/squid-agent.conf.template:/etc/squid/squid.conf.template - ./ssrf_proxy/squid-common.conf.template:/etc/squid/dify_common.conf.template - ./ssrf_proxy/docker-agent-entrypoint.sh:/docker-entrypoint-mount.sh entrypoint: [ "sh", "-c", "cp /docker-entrypoint-mount.sh /docker-entrypoint.sh && sed -i 's/\r$$//' /docker-entrypoint.sh && chmod +x /docker-entrypoint.sh && /docker-entrypoint.sh", ] env_file: - path: ./envs/infrastructure/ssrf-proxy.env required: false - ./envs/infrastructure/ssrf-proxy.defaults networks: # Needs to reach api and agent_backend as forward-proxy destinations. - default # Only agent_ssrf_proxy and local_sandbox share this internal network, so # the local_sandbox can reach Squid without gaining a direct route to `api`. - local_sandbox_proxy_network # ssrf_proxy server # for more information, please refer to # https://docs.dify.ai/en/self-host/deploy/troubleshooting/docker-issues#why-is-ssrf-proxy-needed ssrf_proxy: env_file: - path: ./envs/infrastructure/ssrf-proxy.env required: false - ./envs/infrastructure/ssrf-proxy.defaults image: ubuntu/squid:latest restart: always volumes: - ./ssrf_proxy/squid.conf.template:/etc/squid/squid.conf.template - ./ssrf_proxy/squid-common.conf.template:/etc/squid/dify_common.conf.template - ./ssrf_proxy/docker-entrypoint.sh:/docker-entrypoint-mount.sh entrypoint: [ "sh", "-c", "cp /docker-entrypoint-mount.sh /docker-entrypoint.sh && sed -i 's/\r$$//' /docker-entrypoint.sh && chmod +x /docker-entrypoint.sh && /docker-entrypoint.sh", ] networks: - ssrf_proxy_network - default # Certbot service # use `docker-compose --profile certbot up` to start the certbot service. certbot: env_file: - path: ./envs/infrastructure/certbot.env required: false - ./envs/infrastructure/certbot.defaults image: certbot/certbot profiles: - certbot volumes: - ./volumes/certbot/conf:/etc/letsencrypt - ./volumes/certbot/www:/var/www/html - ./volumes/certbot/logs:/var/log/letsencrypt - ./volumes/certbot/conf/live:/etc/letsencrypt/live - ./certbot/update-cert.template.txt:/update-cert.template.txt - ./certbot/docker-entrypoint.sh:/docker-entrypoint.sh entrypoint: ["/docker-entrypoint.sh"] command: ["tail", "-f", "/dev/null"] # The nginx reverse proxy. # used for reverse proxying the API service and Web service. nginx: env_file: - path: ./envs/infrastructure/nginx.env required: false - ./envs/infrastructure/nginx.defaults image: nginx:latest restart: always volumes: - ./nginx/nginx.conf.template:/etc/nginx/nginx.conf.template - ./nginx/proxy.conf.template:/etc/nginx/proxy.conf.template - ./nginx/https.conf.template:/etc/nginx/https.conf.template - ./nginx/conf.d:/etc/nginx/conf.d - ./nginx/docker-entrypoint.sh:/docker-entrypoint-mount.sh - ./nginx/ssl:/etc/ssl # cert dir (legacy) - ./volumes/certbot/conf/live:/etc/letsencrypt/live # cert dir (with certbot container) - ./volumes/certbot/conf:/etc/letsencrypt - ./volumes/certbot/www:/var/www/html entrypoint: [ "sh", "-c", "cp /docker-entrypoint-mount.sh /docker-entrypoint.sh && sed -i 's/\r$$//' /docker-entrypoint.sh && chmod +x /docker-entrypoint.sh && /docker-entrypoint.sh", ] environment: NGINX_SSL_PORT: ${NGINX_SSL_PORT:-443} NGINX_PORT: ${NGINX_PORT:-80} depends_on: - api - web ports: - "${EXPOSE_NGINX_PORT:-80}:${NGINX_PORT:-80}" - "${EXPOSE_NGINX_SSL_PORT:-443}:${NGINX_SSL_PORT:-443}" # The Weaviate vector store. weaviate: env_file: - path: ./envs/vectorstores/weaviate.env required: false - ./envs/vectorstores/weaviate.defaults image: cr.weaviate.io/semitechnologies/weaviate:1.39.13 profiles: - weaviate restart: always volumes: # Mount the Weaviate data directory to the con tainer. - ./volumes/weaviate:/var/lib/weaviate # OceanBase vector database oceanbase: env_file: - path: ./envs/vectorstores/oceanbase.env required: false - ./envs/vectorstores/oceanbase.defaults image: oceanbase/oceanbase-ce:4.3.5-lts container_name: oceanbase profiles: - oceanbase restart: always volumes: - ./volumes/oceanbase/data:/root/ob - ./volumes/oceanbase/conf:/root/.obd/cluster - ./volumes/oceanbase/init.d:/root/boot/init.d environment: OB_SERVER_IP: 127.0.0.1 MODE: mini LANG: C.UTF-8 LC_ALL: C.UTF-8 ports: - "${OCEANBASE_VECTOR_PORT:-2881}:2881" healthcheck: test: [ "CMD-SHELL", 'obclient -h127.0.0.1 -P2881 -uroot@test -p"$$OB_TENANT_PASSWORD" -e "SELECT 1;"', ] interval: 10s retries: 30 start_period: 30s timeout: 10s # seekdb vector database seekdb: env_file: - path: ./envs/vectorstores/seekdb.env required: false - ./envs/vectorstores/seekdb.defaults image: oceanbase/seekdb:latest container_name: seekdb profiles: - seekdb restart: always volumes: - ./volumes/seekdb:/var/lib/oceanbase environment: REPORTER: dify-ai-seekdb ports: - "${OCEANBASE_VECTOR_PORT:-2881}:2881" healthcheck: test: [ "CMD-SHELL", 'mysql -h127.0.0.1 -P2881 -uroot -p"$$ROOT_PASSWORD" -e "SELECT 1;"', ] interval: 5s retries: 60 timeout: 5s # Qdrant vector store. # (if used, you need to set VECTOR_STORE to qdrant in the api & worker service.) qdrant: env_file: - path: ./envs/vectorstores/qdrant.env required: false - ./envs/vectorstores/qdrant.defaults image: langgenius/qdrant:v1.8.3 profiles: - qdrant restart: always volumes: - ./volumes/qdrant:/qdrant/storage ulimits: nofile: soft: ${QDRANT_NOFILE_SOFT:-524288} hard: ${QDRANT_NOFILE_HARD:-524288} # The Couchbase vector store. couchbase-server: env_file: - path: ./envs/vectorstores/couchbase.env required: false - ./envs/vectorstores/couchbase.defaults build: ./couchbase-server profiles: - couchbase restart: always environment: - CLUSTER_NAME=dify_search - COUCHBASE_BUCKET_RAMSIZE=512 - COUCHBASE_RAM_SIZE=2048 - COUCHBASE_EVENTING_RAM_SIZE=512 - COUCHBASE_INDEX_RAM_SIZE=512 - COUCHBASE_FTS_RAM_SIZE=1024 hostname: couchbase-server container_name: couchbase-server working_dir: /opt/couchbase stdin_open: true tty: true entrypoint: [""] command: sh -c "/opt/couchbase/init/init-cbserver.sh" volumes: - ./volumes/couchbase/data:/opt/couchbase/var/lib/couchbase/data healthcheck: # ensure bucket was created before proceeding test: [ "CMD-SHELL", "curl -s -f -u \"$$COUCHBASE_ADMINISTRATOR_USERNAME:$$COUCHBASE_ADMINISTRATOR_PASSWORD\" http://localhost:8091/pools/default/buckets | grep -q '\\[{' || exit 1", ] interval: 10s retries: 10 start_period: 30s timeout: 10s # The pgvector vector database. pgvector: env_file: - path: ./envs/vectorstores/pgvector.env required: false - ./envs/vectorstores/pgvector.defaults image: pgvector/pgvector:pg16 profiles: - pgvector restart: always volumes: - ./volumes/pgvector/data:/var/lib/postgresql/data - ./pgvector/docker-entrypoint.sh:/docker-entrypoint.sh entrypoint: ["/docker-entrypoint.sh"] healthcheck: test: ["CMD", "pg_isready"] interval: 1s timeout: 3s retries: 30 # get image from https://www.vastdata.com.cn/ vastbase: image: vastdata/vastbase-vector profiles: - vastbase restart: always environment: - VB_DBCOMPATIBILITY=PG - VB_DB=dify - VB_USERNAME=dify - VB_PASSWORD=Difyai123456 ports: - "5434:5432" volumes: - ./vastbase/lic:/home/vastbase/vastbase/lic - ./vastbase/data:/home/vastbase/data - ./vastbase/backup:/home/vastbase/backup - ./vastbase/backup_log:/home/vastbase/backup_log healthcheck: test: ["CMD", "pg_isready"] interval: 1s timeout: 3s retries: 30 # pgvecto-rs vector store pgvecto-rs: env_file: - path: ./envs/vectorstores/pgvecto-rs.env required: false - ./envs/vectorstores/pgvecto-rs.defaults image: tensorchord/pgvecto-rs:pg16-v0.3.0 profiles: - pgvecto-rs restart: always volumes: - ./volumes/pgvecto_rs/data:/var/lib/postgresql/data healthcheck: test: ["CMD", "pg_isready"] interval: 1s timeout: 3s retries: 30 # Chroma vector database chroma: env_file: - path: ./envs/vectorstores/chroma.env required: false - ./envs/vectorstores/chroma.defaults image: ghcr.io/chroma-core/chroma:0.5.20 profiles: - chroma restart: always volumes: - ./volumes/chroma:/chroma/chroma # InterSystems IRIS vector database iris: env_file: - path: ./envs/vectorstores/iris.env required: false - ./envs/vectorstores/iris.defaults image: containers.intersystems.com/intersystems/iris-community:2025.3 profiles: - iris container_name: iris restart: always init: true ports: - "${IRIS_SUPER_SERVER_PORT:-1972}:1972" - "${IRIS_WEB_SERVER_PORT:-52773}:52773" volumes: - ./volumes/iris:/durable - ./iris/iris-init.script:/iris-init.script - ./iris/docker-entrypoint.sh:/custom-entrypoint.sh entrypoint: ["/custom-entrypoint.sh"] tty: true environment: ISC_DATA_DIRECTORY: /durable/iris # Oracle vector database oracle: env_file: - path: ./envs/vectorstores/oracle.env required: false - ./envs/vectorstores/oracle.defaults image: container-registry.oracle.com/database/free:latest profiles: - oracle restart: always volumes: - source: oradata type: volume target: /opt/oracle/oradata - ./startupscripts:/opt/oracle/scripts/startup # Milvus vector database services etcd: env_file: - path: ./envs/vectorstores/milvus.env required: false - path: ./envs/infrastructure/etcd.env required: false - ./envs/infrastructure/etcd.defaults container_name: milvus-etcd image: quay.io/coreos/etcd:v3.5.5 profiles: - milvus volumes: - ./volumes/milvus/etcd:/etcd command: etcd -advertise-client-urls=http://127.0.0.1:2379 -listen-client-urls http://0.0.0.0:2379 --data-dir /etcd healthcheck: test: ["CMD", "etcdctl", "endpoint", "health"] interval: 30s timeout: 20s retries: 3 networks: - milvus minio: env_file: - path: ./envs/vectorstores/milvus.env required: false - path: ./envs/infrastructure/minio.env required: false - ./envs/infrastructure/minio.defaults container_name: milvus-minio image: minio/minio:RELEASE.2023-03-20T20-16-18Z profiles: - milvus volumes: - ./volumes/milvus/minio:/minio_data command: minio server /minio_data --console-address ":9001" healthcheck: test: ["CMD", "curl", "-f", "http://localhost:9000/minio/health/live"] interval: 30s timeout: 20s retries: 3 networks: - milvus milvus-standalone: env_file: - path: ./envs/vectorstores/milvus.env required: false - path: ./envs/infrastructure/milvus-standalone.env required: false - ./envs/infrastructure/milvus-standalone.defaults container_name: milvus-standalone image: milvusdb/milvus:v2.6.3 profiles: - milvus command: ["milvus", "run", "standalone"] volumes: - ./volumes/milvus/milvus:/var/lib/milvus healthcheck: test: ["CMD", "curl", "-f", "http://localhost:9091/healthz"] interval: 30s start_period: 90s timeout: 20s retries: 3 depends_on: - etcd - minio ports: - 19530:19530 - 9091:9091 networks: - milvus - default # Opensearch vector database opensearch: env_file: - path: ./envs/vectorstores/opensearch.env required: false - ./envs/vectorstores/opensearch.defaults container_name: opensearch image: opensearchproject/opensearch:latest profiles: - opensearch ulimits: memlock: soft: ${OPENSEARCH_MEMLOCK_SOFT:--1} hard: ${OPENSEARCH_MEMLOCK_HARD:--1} nofile: soft: ${OPENSEARCH_NOFILE_SOFT:-65536} hard: ${OPENSEARCH_NOFILE_HARD:-65536} volumes: - ./volumes/opensearch/data:/usr/share/opensearch/data networks: - opensearch-net - default opensearch-dashboards: container_name: opensearch-dashboards image: opensearchproject/opensearch-dashboards:latest profiles: - opensearch environment: OPENSEARCH_HOSTS: '["https://opensearch:9200"]' volumes: - ./volumes/opensearch/opensearch_dashboards.yml:/usr/share/opensearch-dashboards/config/opensearch_dashboards.yml networks: - opensearch-net depends_on: - opensearch # opengauss vector database. opengauss: env_file: - path: ./envs/vectorstores/opengauss.env required: false - ./envs/vectorstores/opengauss.defaults image: opengauss/opengauss:7.0.0-RC1 profiles: - opengauss privileged: true restart: always environment: GS_PORT: ${OPENGAUSS_PORT:-6600} volumes: - ./volumes/opengauss/data:/var/lib/opengauss/data healthcheck: test: ["CMD-SHELL", "netstat -lntp | grep tcp6 > /dev/null 2>&1"] interval: 10s timeout: 10s retries: 10 ports: - ${OPENGAUSS_PORT:-6600}:${OPENGAUSS_PORT:-6600} # MyScale vector database myscale: container_name: myscale image: myscale/myscaledb:1.6.4 profiles: - myscale restart: always tty: true volumes: - ./volumes/myscale/data:/var/lib/clickhouse - ./volumes/myscale/log:/var/log/clickhouse-server - ./volumes/myscale/config/users.d/custom_users_config.xml:/etc/clickhouse-server/users.d/custom_users_config.xml ports: - ${MYSCALE_PORT:-8123}:${MYSCALE_PORT:-8123} # Matrixone vector store. matrixone: hostname: matrixone image: matrixorigin/matrixone:2.1.1 profiles: - matrixone restart: always volumes: - ./volumes/matrixone/data:/mo-data ports: - ${MATRIXONE_PORT:-6001}:${MATRIXONE_PORT:-6001} # https://www.elastic.co/guide/en/elasticsearch/reference/current/settings.html # https://www.elastic.co/guide/en/elasticsearch/reference/current/docker.html#docker-prod-prerequisites elasticsearch: env_file: - path: ./envs/vectorstores/elasticsearch.env required: false - ./envs/vectorstores/elasticsearch.defaults image: docker.elastic.co/elasticsearch/elasticsearch:8.14.3 container_name: elasticsearch profiles: - elasticsearch - elasticsearch-ja restart: always environment: cluster.name: dify-es-cluster node.name: dify-es0 discovery.type: single-node xpack.license.self_generated.type: basic xpack.security.enabled: "true" xpack.security.enrollment.enabled: "false" xpack.security.http.ssl.enabled: "false" # Backend selection also controls the Japanese analyzer installed by the entrypoint. VECTOR_STORE: ${VECTOR_STORE:-} volumes: - ./elasticsearch/docker-entrypoint.sh:/docker-entrypoint-mount.sh - dify_es01_data:/usr/share/elasticsearch/data ports: - ${ELASTICSEARCH_PORT:-9200}:9200 deploy: resources: limits: memory: 2g entrypoint: ["sh", "-c", "sh /docker-entrypoint-mount.sh"] healthcheck: test: ["CMD", "curl", "-s", "http://localhost:9200/_cluster/health?pretty"] interval: 30s timeout: 10s retries: 50 # https://www.elastic.co/guide/en/kibana/current/docker.html # https://www.elastic.co/guide/en/kibana/current/settings.html kibana: image: docker.elastic.co/kibana/kibana:8.14.3 container_name: kibana profiles: - elasticsearch depends_on: - elasticsearch restart: always environment: XPACK_ENCRYPTEDSAVEDOBJECTS_ENCRYPTIONKEY: d1a66dfd-c4d3-4a0a-8290-2abcb83ab3aa NO_PROXY: localhost,127.0.0.1,elasticsearch,kibana XPACK_SECURITY_ENABLED: "true" XPACK_SECURITY_ENROLLMENT_ENABLED: "false" XPACK_SECURITY_HTTP_SSL_ENABLED: "false" XPACK_FLEET_ISAIRGAPPED: "true" I18N_LOCALE: zh-CN SERVER_PORT: "5601" ELASTICSEARCH_HOSTS: http://elasticsearch:9200 ports: - ${KIBANA_PORT:-5601}:5601 healthcheck: test: ["CMD-SHELL", "curl -s http://localhost:5601 >/dev/null || exit 1"] interval: 30s timeout: 10s retries: 3 # unstructured . # (if used, you need to set ETL_TYPE to Unstructured in the api & worker service.) unstructured: image: downloads.unstructured.io/unstructured-io/unstructured-api:latest profiles: - unstructured restart: always volumes: - ./volumes/unstructured:/app/data # Dedicated KnowledgeFS parser for all remote document formats. Its page-parallel PDF policy # and heavy-workload resource budget cannot change legacy Dify Unstructured ETL traffic. knowledge_fs_unstructured: image: downloads.unstructured.io/unstructured-io/unstructured-api@sha256:0df934a22e4e893cf15e7aeaf35c463ecc75937758a83099aefdc13041619a1d profiles: - knowledge-fs-unstructured restart: always env_file: - path: ./envs/core-services/knowledge-fs-unstructured-service.defaults required: true - path: ./envs/core-services/knowledge-fs-unstructured.env required: false volumes: - ./volumes/knowledge-fs-unstructured:/app/data deploy: resources: limits: cpus: "4.0" memory: 6G networks: # create a network between sandbox, api and ssrf_proxy, and can not access outside. ssrf_proxy_network: driver: bridge internal: true # Internal network shared only by agent_ssrf_proxy and local_sandbox. local_sandbox_proxy_network: driver: bridge internal: true # shellctl control channel (agent_backend -> local_sandbox:5004). # sandbox can access agent backend through this network, this is # a known limitation. # # The agent runtime respects HTTP(S)_PROXY, but arbitrary code execution # is still possible through the shellctl channel. agent_sandbox_network: driver: bridge internal: true milvus: driver: bridge opensearch-net: driver: bridge internal: true volumes: oradata: dify_es01_data: dify_agent_local_sandbox_home: dify_agent_local_sandbox_workspace: