Files
dify/docker/docker-compose.yaml
T

1414 lines
45 KiB
YAML

# ==================================================================
# 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: