From 53ea596fc7d23669c1daf6014875f65d358d176d Mon Sep 17 00:00:00 2001 From: -LAN- Date: Tue, 3 Nov 2026 01:58:43 +0000 Subject: [PATCH] fix(docker): honor categorized service environment overrides (#45359) Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com> --- .github/workflows/autofix.yml | 7 + .github/workflows/knowledge-fs-ci.yml | 2 +- .../contracts/configs/test_env_consistency.py | 53 +- .../extra/test_agent_backend_config.py | 6 +- .../configs/extra/test_knowledge_fs_config.py | 2 +- dify-agent/.example.env | 2 +- dify-agent/docs/dify-agent/guide/openshell.md | 2 +- docker/.env.example | 7 +- docker/README.md | 533 +++++++++++----- docker/docker-compose-template.yaml | 478 +++++--------- docker/docker-compose.e2b.yaml | 2 - docker/docker-compose.yaml | 478 +++++--------- docker/envs/core-services/api.defaults | 101 +++ docker/envs/core-services/api.env.example | 59 -- docker/envs/core-services/dify-agent.defaults | 132 ++++ .../envs/core-services/dify-agent.env.example | 132 ---- .../envs/core-services/plugin-daemon.defaults | 89 +++ .../core-services/plugin-daemon.env.example | 25 - docker/envs/core-services/sandbox.defaults | 26 + docker/envs/core-services/sandbox.env.example | 17 - docker/envs/core-services/shared.defaults | 595 +++++++++++++++++- docker/envs/core-services/shared.env.example | 574 ----------------- docker/envs/core-services/web.defaults | 60 ++ docker/envs/core-services/web.env.example | 56 -- .../envs/core-services/worker-beat.defaults | 9 + .../core-services/worker-beat.env.example | 8 - docker/envs/core-services/worker.defaults | 29 + docker/envs/core-services/worker.env.example | 13 - docker/envs/databases/db-mysql.defaults | 15 + docker/envs/databases/db-mysql.env.example | 9 - docker/envs/databases/db-postgres.defaults | 31 + docker/envs/databases/db-postgres.env.example | 26 - docker/envs/databases/redis.defaults | 42 ++ docker/envs/databases/redis.env.example | 39 -- docker/envs/infrastructure/certbot.defaults | 7 + .../envs/infrastructure/certbot.env.example | 7 - docker/envs/infrastructure/etcd.defaults | 8 + docker/envs/infrastructure/etcd.env.example | 4 - .../infrastructure/milvus-standalone.defaults | 10 + .../milvus-standalone.env.example | 4 - docker/envs/infrastructure/minio.defaults | 6 + docker/envs/infrastructure/minio.env.example | 4 - docker/envs/infrastructure/nginx.defaults | 20 + docker/envs/infrastructure/nginx.env.example | 18 - .../envs/infrastructure/ssrf-proxy.defaults | 30 + .../infrastructure/ssrf-proxy.env.example | 26 - docker/envs/vectorstores/chroma.defaults | 16 + docker/envs/vectorstores/chroma.env.example | 13 - docker/envs/vectorstores/couchbase.defaults | 14 + .../envs/vectorstores/couchbase.env.example | 9 - .../envs/vectorstores/elasticsearch.defaults | 21 + .../vectorstores/elasticsearch.env.example | 17 - docker/envs/vectorstores/iris.defaults | 20 + docker/envs/vectorstores/iris.env.example | 17 - docker/envs/vectorstores/milvus.env.example | 9 + docker/envs/vectorstores/oceanbase.defaults | 21 + .../envs/vectorstores/oceanbase.env.example | 6 - docker/envs/vectorstores/opengauss.defaults | 17 + .../envs/vectorstores/opengauss.env.example | 12 - docker/envs/vectorstores/opensearch.defaults | 27 + .../envs/vectorstores/opensearch.env.example | 22 - docker/envs/vectorstores/oracle.defaults | 13 + docker/envs/vectorstores/oracle.env.example | 13 - docker/envs/vectorstores/pgvecto-rs.defaults | 19 + .../envs/vectorstores/pgvecto-rs.env.example | 9 - docker/envs/vectorstores/pgvector.defaults | 27 + docker/envs/vectorstores/pgvector.env.example | 8 - docker/envs/vectorstores/qdrant.defaults | 12 + docker/envs/vectorstores/qdrant.env.example | 4 - docker/envs/vectorstores/seekdb.defaults | 10 + docker/envs/vectorstores/seekdb.env.example | 4 - docker/envs/vectorstores/weaviate.defaults | 37 ++ docker/envs/vectorstores/weaviate.env.example | 18 - docker/generate_docker_compose | 47 +- docker/tests/test_compose_environment.py | 573 +++++++++++++++++ 75 files changed, 2793 insertions(+), 2045 deletions(-) create mode 100644 docker/envs/core-services/api.defaults delete mode 100644 docker/envs/core-services/api.env.example create mode 100644 docker/envs/core-services/dify-agent.defaults delete mode 100644 docker/envs/core-services/dify-agent.env.example create mode 100644 docker/envs/core-services/plugin-daemon.defaults delete mode 100644 docker/envs/core-services/plugin-daemon.env.example create mode 100644 docker/envs/core-services/sandbox.defaults delete mode 100644 docker/envs/core-services/sandbox.env.example delete mode 100644 docker/envs/core-services/shared.env.example create mode 100644 docker/envs/core-services/web.defaults delete mode 100644 docker/envs/core-services/web.env.example create mode 100644 docker/envs/core-services/worker-beat.defaults delete mode 100644 docker/envs/core-services/worker-beat.env.example create mode 100644 docker/envs/core-services/worker.defaults delete mode 100644 docker/envs/core-services/worker.env.example create mode 100644 docker/envs/databases/db-mysql.defaults delete mode 100644 docker/envs/databases/db-mysql.env.example create mode 100644 docker/envs/databases/db-postgres.defaults delete mode 100644 docker/envs/databases/db-postgres.env.example create mode 100644 docker/envs/databases/redis.defaults delete mode 100644 docker/envs/databases/redis.env.example create mode 100644 docker/envs/infrastructure/certbot.defaults delete mode 100644 docker/envs/infrastructure/certbot.env.example create mode 100644 docker/envs/infrastructure/etcd.defaults delete mode 100644 docker/envs/infrastructure/etcd.env.example create mode 100644 docker/envs/infrastructure/milvus-standalone.defaults delete mode 100644 docker/envs/infrastructure/milvus-standalone.env.example create mode 100644 docker/envs/infrastructure/minio.defaults delete mode 100644 docker/envs/infrastructure/minio.env.example create mode 100644 docker/envs/infrastructure/nginx.defaults delete mode 100644 docker/envs/infrastructure/nginx.env.example create mode 100644 docker/envs/infrastructure/ssrf-proxy.defaults delete mode 100644 docker/envs/infrastructure/ssrf-proxy.env.example create mode 100644 docker/envs/vectorstores/chroma.defaults delete mode 100644 docker/envs/vectorstores/chroma.env.example create mode 100644 docker/envs/vectorstores/couchbase.defaults delete mode 100644 docker/envs/vectorstores/couchbase.env.example create mode 100644 docker/envs/vectorstores/elasticsearch.defaults delete mode 100644 docker/envs/vectorstores/elasticsearch.env.example create mode 100644 docker/envs/vectorstores/iris.defaults delete mode 100644 docker/envs/vectorstores/iris.env.example create mode 100644 docker/envs/vectorstores/oceanbase.defaults delete mode 100644 docker/envs/vectorstores/oceanbase.env.example create mode 100644 docker/envs/vectorstores/opengauss.defaults delete mode 100644 docker/envs/vectorstores/opengauss.env.example create mode 100644 docker/envs/vectorstores/opensearch.defaults delete mode 100644 docker/envs/vectorstores/opensearch.env.example create mode 100644 docker/envs/vectorstores/oracle.defaults delete mode 100644 docker/envs/vectorstores/oracle.env.example create mode 100644 docker/envs/vectorstores/pgvecto-rs.defaults delete mode 100644 docker/envs/vectorstores/pgvecto-rs.env.example create mode 100644 docker/envs/vectorstores/pgvector.defaults delete mode 100644 docker/envs/vectorstores/pgvector.env.example create mode 100644 docker/envs/vectorstores/qdrant.defaults delete mode 100644 docker/envs/vectorstores/qdrant.env.example create mode 100644 docker/envs/vectorstores/seekdb.defaults delete mode 100644 docker/envs/vectorstores/seekdb.env.example create mode 100644 docker/envs/vectorstores/weaviate.defaults delete mode 100644 docker/envs/vectorstores/weaviate.env.example create mode 100644 docker/tests/test_compose_environment.py diff --git a/.github/workflows/autofix.yml b/.github/workflows/autofix.yml index 4240a87a01b..f5e5359c105 100644 --- a/.github/workflows/autofix.yml +++ b/.github/workflows/autofix.yml @@ -30,6 +30,9 @@ jobs: docker/.env.example docker/docker-compose-template.yaml docker/docker-compose.yaml + docker/docker-compose.e2b.yaml + docker/envs/** + docker/tests/** - name: Check api inputs if: github.event_name != 'merge_group' id: api-changes @@ -72,6 +75,10 @@ jobs: cd docker ./generate_docker_compose + - name: Check Docker environment overrides + if: github.event_name != 'merge_group' && steps.docker-compose-changes.outputs.any_changed == 'true' + run: python3 -m unittest discover -s docker/tests -p 'test_*.py' + - if: github.event_name != 'merge_group' && steps.api-changes.outputs.any_changed == 'true' run: | cd api diff --git a/.github/workflows/knowledge-fs-ci.yml b/.github/workflows/knowledge-fs-ci.yml index 003fa681200..1e8160a4911 100644 --- a/.github/workflows/knowledge-fs-ci.yml +++ b/.github/workflows/knowledge-fs-ci.yml @@ -111,7 +111,7 @@ jobs: - 'docker/dify-env-sync.sh' - 'docker/docker-compose-template.yaml' - 'docker/docker-compose.yaml' - - 'docker/envs/core-services/api.env.example' + - 'docker/envs/core-services/api.defaults' - 'docker/envs/core-services/knowledge-fs.env.example' - 'docker/envs/core-services/knowledge-fs-unstructured-service.defaults' - 'docker/envs/core-services/knowledge-fs-unstructured.env.example' diff --git a/api/tests/e2e_tests/contracts/configs/test_env_consistency.py b/api/tests/e2e_tests/contracts/configs/test_env_consistency.py index ccbf66dd4fb..377797d36eb 100644 --- a/api/tests/e2e_tests/contracts/configs/test_env_consistency.py +++ b/api/tests/e2e_tests/contracts/configs/test_env_consistency.py @@ -1,3 +1,4 @@ +import re from pathlib import Path from dotenv import dotenv_values @@ -99,22 +100,58 @@ def _api_config_set() -> set[str]: return set(dotenv_values(REPO_ROOT / "api" / ".env.example").keys()) -def _docker_config_set() -> set[str]: - docker_config_set = set(dotenv_values(REPO_ROOT / "docker" / ".env.example").keys()) - envs_dir = REPO_ROOT / "docker" / "envs" - if envs_dir.exists(): - for env_file_path in envs_dir.rglob("*.env.example"): - docker_config_set.update(dotenv_values(env_file_path).keys()) +def _docker_config_set(docker_dir: Path) -> set[str]: + """Return keys documented in readable Docker templates, excluding local overrides. + + Include assignments, commented assignments, and explicit root .env instructions. + The root .env.example must exist; read failures propagate without changing any files. + """ + envs_dir = docker_dir / "envs" + env_files = [docker_dir / ".env.example", *envs_dir.rglob("*.env.example"), *envs_dir.rglob("*.defaults")] + docker_config_set: set[str] = set() + for env_file_path in env_files: + content = env_file_path.read_text() + docker_config_set.update(dotenv_values(env_file_path, interpolate=False).keys()) + docker_config_set.update(re.findall(r"(?m)^[ \t]*#[ \t]*([A-Z_][A-Z0-9_]*)=", content)) + docker_config_set.update(re.findall(r"(?m)^# Set ([A-Z_][A-Z0-9_]*) in docker/\.env\b", content)) return docker_config_set +def test_docker_config_set_includes_documented_keys_only(tmp_path: Path) -> None: + """Given templates and local overrides, collect only keys documented in the templates.""" + envs_dir = tmp_path / "envs" / "core-services" + envs_dir.mkdir(parents=True) + (tmp_path / ".env.example").write_text("ROOT_KEY=value\n# ROOT_OPTIONAL=\n") + (envs_dir / "service.defaults").write_text( + "DEFAULT_KEY=${ROOT_KEY:-fallback}\n# OPTIONAL_KEY=\n # INDENTED_KEY=value\n" + "# Set COMPOSE_PORT in docker/.env; Compose needs it before loading service files.\n" + "# Prose with NOT_A_KEY=value\n# NO_ASSIGNMENT\n" + ) + (envs_dir / "provider.env.example").write_text("PROVIDER_KEY=value\n# PROVIDER_OPTIONAL=value\n") + (tmp_path / ".env").write_text("LOCAL_ROOT_KEY=private\n") + (envs_dir / "service.env").write_text("LOCAL_SERVICE_KEY=private\n") + + assert _docker_config_set(tmp_path) == { + "ROOT_KEY", + "ROOT_OPTIONAL", + "DEFAULT_KEY", + "OPTIONAL_KEY", + "INDENTED_KEY", + "COMPOSE_PORT", + "PROVIDER_KEY", + "PROVIDER_OPTIONAL", + } + + def test_api_env_keys_exist_in_docker_env_examples() -> None: - diff = _api_config_set() - _docker_config_set() - BASE_API_AND_DOCKER_CONFIG_SET_DIFF + """Require every non-exempt API setting to remain documented in Docker templates.""" + diff = _api_config_set() - _docker_config_set(REPO_ROOT / "docker") - BASE_API_AND_DOCKER_CONFIG_SET_DIFF assert not diff, f"API and Docker config sets are different with keys: {sorted(diff)}" def test_api_env_keys_exist_in_docker_compose_env_examples() -> None: - diff = _api_config_set() - _docker_config_set() - BASE_API_AND_DOCKER_COMPOSE_CONFIG_SET_DIFF + """Require every non-exempt API setting to remain documented for Docker Compose.""" + diff = _api_config_set() - _docker_config_set(REPO_ROOT / "docker") - BASE_API_AND_DOCKER_COMPOSE_CONFIG_SET_DIFF assert not diff, f"API and Docker Compose config sets are different with keys: {sorted(diff)}" diff --git a/api/tests/unit_tests/configs/extra/test_agent_backend_config.py b/api/tests/unit_tests/configs/extra/test_agent_backend_config.py index b6e1bc51c42..ac1e0b261f6 100644 --- a/api/tests/unit_tests/configs/extra/test_agent_backend_config.py +++ b/api/tests/unit_tests/configs/extra/test_agent_backend_config.py @@ -25,10 +25,8 @@ def test_binding_file_download_timeout_rejects_non_positive_values() -> None: def test_binding_file_timeout_docker_settings_use_their_service_env_files() -> None: root_env_example = (_REPOSITORY_ROOT / "docker/.env.example").read_text(encoding="utf-8") - api_env_example = (_REPOSITORY_ROOT / "docker/envs/core-services/api.env.example").read_text(encoding="utf-8") - agent_env_example = (_REPOSITORY_ROOT / "docker/envs/core-services/dify-agent.env.example").read_text( - encoding="utf-8" - ) + api_env_example = (_REPOSITORY_ROOT / "docker/envs/core-services/api.defaults").read_text(encoding="utf-8") + agent_env_example = (_REPOSITORY_ROOT / "docker/envs/core-services/dify-agent.defaults").read_text(encoding="utf-8") compose_template = (_REPOSITORY_ROOT / "docker/docker-compose-template.yaml").read_text(encoding="utf-8") assert f"{_API_TIMEOUT_ENV}=" not in root_env_example diff --git a/api/tests/unit_tests/configs/extra/test_knowledge_fs_config.py b/api/tests/unit_tests/configs/extra/test_knowledge_fs_config.py index 1723de1be8c..fb88fae9992 100644 --- a/api/tests/unit_tests/configs/extra/test_knowledge_fs_config.py +++ b/api/tests/unit_tests/configs/extra/test_knowledge_fs_config.py @@ -122,7 +122,7 @@ def test_disabled_knowledge_fs_config_allows_partial_connection() -> None: def test_knowledge_fs_docker_config_is_not_shadowed_by_root_env() -> None: root_env_example = (_REPOSITORY_ROOT / "docker/.env.example").read_text(encoding="utf-8") - api_env_example = (_REPOSITORY_ROOT / "docker/envs/core-services/api.env.example").read_text(encoding="utf-8") + api_env_example = (_REPOSITORY_ROOT / "docker/envs/core-services/api.defaults").read_text(encoding="utf-8") for variable in _KNOWLEDGE_FS_DOCKER_VARIABLES: assert f"{variable}=" not in root_env_example diff --git a/dify-agent/.example.env b/dify-agent/.example.env index cf45162451e..e8e35b3cca7 100644 --- a/dify-agent/.example.env +++ b/dify-agent/.example.env @@ -58,7 +58,7 @@ DIFY_AGENT_E2B_ACTIVE_TIMEOUT_SECONDS=3600 DIFY_AGENT_E2B_SHELLCTL_PORT=5004 # Minimal OpenShell configuration subset for local debugging. -# Full configuration reference: docker/envs/core-services/dify-agent.env.example +# Full configuration reference: docker/envs/core-services/dify-agent.defaults DIFY_AGENT_OPENSHELL_GATEWAY_ENDPOINT=localhost:17670 DIFY_AGENT_OPENSHELL_TLS_CA_PATH=${HOME}/.config/openshell/gateways/openshell/mtls/ca.crt DIFY_AGENT_OPENSHELL_TLS_CLIENT_CERT_PATH=${HOME}/.config/openshell/gateways/openshell/mtls/tls.crt diff --git a/dify-agent/docs/dify-agent/guide/openshell.md b/dify-agent/docs/dify-agent/guide/openshell.md index a689d9539d7..0b40f707a86 100644 --- a/dify-agent/docs/dify-agent/guide/openshell.md +++ b/dify-agent/docs/dify-agent/guide/openshell.md @@ -153,7 +153,7 @@ spec: ``` Configure the backend in `docker/.env`. The full `DIFY_AGENT_OPENSHELL_*` -variable reference lives in `docker/envs/core-services/dify-agent.env.example`; +variable reference lives in `docker/envs/core-services/dify-agent.defaults`; values may also be set in the `envs/core-services/dify-agent.env` env_file, but `docker/.env` is loaded last into `agent_backend` and overrides it. These lines are `.env` file content, not shell commands — values are taken verbatim, diff --git a/docker/.env.example b/docker/.env.example index a0e1f159ca0..f3edbfed600 100644 --- a/docker/.env.example +++ b/docker/.env.example @@ -1,10 +1,15 @@ # Only settings requiring manual configuration belong in this file. +# Requires Docker Compose v2.35.0+ for .env override precedence. # Application and Docker defaults are loaded automatically; do not repeat them here. # Copy this file to .env, then select the services for your deployment. -# Optional overrides and provider settings are documented in envs/**/*.env.example. +# Optional overrides and provider settings are documented in envs/. # Values in .env take precedence over the matching envs/*.env files. # Compose requires an explicit selection of the bundled database, vector store, # and websocket service. Match these profiles to your DB_TYPE and VECTOR_STORE # overrides when using different providers; omit services hosted externally. COMPOSE_PROFILES=weaviate,postgresql,collaboration + +# Required for the default local Agent sandbox. Generate with: openssl rand -hex 32 +# Both the Agent backend and shellctl use this shared secret. +DIFY_AGENT_LOCAL_SANDBOX_AUTH_TOKEN= diff --git a/docker/README.md b/docker/README.md index ac4096269b0..6123e4c37e1 100644 --- a/docker/README.md +++ b/docker/README.md @@ -1,103 +1,299 @@ # Docker deployment -Run Dify with Docker Compose. Configuration templates live in `.env.example` and `envs/`; local overrides belong in `.env`. +Run Dify with Docker Compose. For the default deployment, copy `.env.example` to +`.env`, fill in the required token, and start the services. Defaults for the API, +workers, and supporting services load automatically; additional files are optional. -## How to Deploy Dify with `docker-compose.yaml` +- [Start Dify](#start-dify) +- [Configure Dify](#configure-dify) +- [Troubleshoot setup and configuration](#troubleshoot-setup-and-configuration) +- [Review configuration after an upgrade](#review-configuration-after-an-upgrade) +- [Run middleware for development](#run-middleware-for-development) +- [Configure HTTPS and telemetry](#configure-https-and-telemetry) +- [Configure worker queues and webhooks](#configure-worker-queues-and-webhooks) +- [Enable the KnowledgeFS integration](#enable-the-knowledgefs-integration) +- [Configure client IP and trusted proxies](#client-ip-and-trusted-proxies) + +## Start Dify + +Before you begin, install Docker with a running daemon, Docker Compose **v2.35.0 or +later**, and OpenSSL to generate the sandbox token. Check your Compose version with +`docker compose version`. Version 2.35.0 or later is required for root `.env` +overrides to take precedence when Compose resolves service defaults. + +For an existing deployment, edit your current `.env` instead of replacing it. +For a new deployment, follow these steps: + +1. From the repository root, enter the Docker directory and copy the template: -1. **Prerequisites**: Ensure Docker and Docker Compose v2.24.0 or later are installed on your system. -2. **Environment Setup**: - - Navigate to the `docker` directory. - - Copy `.env.example` to `.env`. - - Select the bundled services through `COMPOSE_PROFILES`. Add overrides to `.env` only when needed. Copy optional files from `envs/` without the `.example` suffix when you need advanced settings. - - **Optional (for advanced deployments)**: - If you maintain a full `.env` file copied from `.env.example`, you may use the environment synchronization tool to keep it aligned with the latest `.env.example` updates while preserving your custom settings. - See the [Environment Variables Synchronization](#environment-variables-synchronization) section below. -3. **Running the Services**: - - Execute `docker compose up -d` from the `docker` directory to start the services. - - To specify a vector database, set the `VECTOR_STORE` variable in your `.env` file to your desired vector database service, such as `milvus`, `weaviate`, or `opensearch`, and select the matching service in `COMPOSE_PROFILES`. See `envs/vectorstores/` for the full list of supported options. ```bash + cd docker cp .env.example .env - docker compose up -d ``` -4. **SSL Certificate Setup**: - - Refer to [the Certbot guide](certbot/README.md) to set up SSL certificates using Certbot. -5. **OpenTelemetry Collector Setup**: - - Copy `envs/core-services/shared.env.example` to `envs/core-services/shared.env`. - - Set `ENABLE_OTEL=true` and configure `OTLP_BASE_ENDPOINT`. Tune the other `OTEL_*` knobs in the same file if needed. + Unless noted otherwise, run commands below from `docker/`. -## KnowledgeFS Integration Service +2. Generate a token: -The optional `knowledge-fs` Compose profile starts the internal KnowledgeFS API service and the -dedicated Dataset upgrade worker. The API service publishes no host port and has no nginx route. -Model, datasource, and object-storage calls go through Dify's inner API. -Dify resolves active Workspace/plugin configuration and remains the only owner of physical -storage credentials. `KNOWLEDGE_FS_ENABLED` remains `false`, so starting the container does not -enable product traffic. + ```bash + openssl rand -hex 32 + ``` -Copy and fill the dedicated service configuration before enabling KnowledgeFS traffic. The default -image is the CI-published deployment-branch image; set `KNOWLEDGE_FS_API_IMAGE` to pin another tag -or immutable SHA tag. If the image repository is private, authenticate the deployment host with -`docker login` before pulling: + Open `.env` and paste the output as the value of + `DIFY_AGENT_LOCAL_SANDBOX_AUTH_TOKEN`. Keep + `COMPOSE_PROFILES=weaviate,postgresql,collaboration` for the default deployment. + No other configuration files are required. -```bash -cp envs/core-services/knowledge-fs.env.example envs/core-services/knowledge-fs.env -docker compose --profile knowledge-fs --profile knowledge-fs-unstructured config -docker compose --profile knowledge-fs --profile knowledge-fs-unstructured pull knowledge_fs knowledge_fs_unstructured -docker compose --profile knowledge-fs --profile knowledge-fs-unstructured up -d +3. Start the services and check their status: + + ```bash + docker compose up -d + docker compose ps --all + ``` + + Wait for the long-running services to start and their health checks to pass. + The one-time `init_permissions` service is expected to exit with code `0`. + +4. Open [http://localhost/install](http://localhost/install) and complete the + initial setup. If you changed the published HTTP port, use that port in the URL. + +Before using this deployment in production, replace the bundled credentials using +[the security template](envs/security.env.example) and the matching service files, +and configure [HTTPS](#configure-https-and-telemetry) and +[trusted proxies](#client-ip-and-trusted-proxies). + +## Configure Dify + +### Choose the configuration file + +| File | Purpose | Loaded directly? | +| --- | --- | --- | +| `.env.example` | Minimal template for required deployment inputs | No; copy it to `.env` | +| `.env` | Deployment inputs and explicit overrides, including Compose settings | Yes | +| `envs/**/*.defaults` | Tracked defaults for the matching services | Yes, automatically | +| `envs/**/*.env` | Optional service or provider overrides | Yes, when the matching service references the file | +| `envs/**/*.env.example` | Templates for optional application and provider settings | No; remove only `.example` when copying | + +Keep settings with usable defaults out of the root `.env.example`. To customize a +service, copy its `.defaults` file to the override path named in its comments, +usually replacing `.defaults` with `.env` in the same directory. Edit the copy, +or the existing `.env` if one is already present. Uncomment only the optional +assignments you want to change; leave unused empty values and placeholders +commented. For an optional `.env.example` template, review its comments and fill +in the settings before use. + +Find settings in: + +- [Core services](envs/core-services/): API, workers, web, plugin daemon, and + sandboxes. [shared.defaults](envs/core-services/shared.defaults) contains the + common API, worker, and Beat settings, grouped by function. API process options + live in `api.defaults`; worker autoscaling lives in `worker.defaults`. +- [Databases](envs/databases/): PostgreSQL, MySQL, and Redis. +- [Vector stores](envs/vectorstores/): bundled and external vector databases, + including their API/worker client settings. Providers without a dedicated + template are documented in `shared.defaults`. +- [Infrastructure](envs/infrastructure/): nginx, Certbot, proxies, and supporting services. + +### Change a service setting + +For example, to set nginx's upload limit to `200M`: + +1. Create the optional override file if it does not already exist: + + ```bash + cp envs/infrastructure/nginx.defaults envs/infrastructure/nginx.env + ``` + +2. In `envs/infrastructure/nginx.env`, replace the input setting with: + + ```dotenv + NGINX_CLIENT_MAX_BODY_SIZE=200M + ``` + + Leave expressions under `Container variable mappings` unchanged. They translate + input names such as `SANDBOX_WORKER_TIMEOUT` into the container's `WORKER_TIMEOUT`. + +3. Apply the change and inspect nginx's active configuration: + + ```bash + docker compose up -d + docker compose exec -T nginx nginx -T 2>&1 | grep client_max_body_size + ``` + + The output should contain `client_max_body_size 200M;`. + +Use `docker compose up -d` after editing configuration files. Compose recreates +containers whose configuration changed. `docker compose restart` does not reload +environment variables. No extra `--env-file` option is needed for this deployment. + +### Change a published port or another Compose setting + +Put settings consumed by Compose in root `.env`: `COMPOSE_PROFILES`, published +ports, volume paths, resource limits, and health-check intervals. Service env +files cannot change those parts of the Compose model. + +For example, add or update this line in `.env` to use HTTP port `8080`: + +```dotenv +EXPOSE_NGINX_PORT=8080 ``` -The service env file intentionally contains only operator-owned inputs: the dedicated database, -durable compilation switch, Capability v2 public verification material, and optional Unstructured -endpoint. Compose injects the Dify inner API connection and integrated mode. KnowledgeFS stores -objects below an internal namespace in Dify's configured `STORAGE_TYPE`; do not duplicate Dify's -bucket, endpoint, or provider credentials in `knowledge-fs.env`. Feature-specific rollout flags -and capacity tunables should be added only when deliberately overriding their safe runtime -defaults. +Run `docker compose up -d`, then open +[http://localhost:8080/install](http://localhost:8080/install). +Use root `.env` for values shared with the isolated local Agent sandbox, including +its authentication token. -The optional `knowledge-fs-unstructured` profile starts one isolated parser service named -`knowledge_fs_unstructured` for every KnowledgeFS remote format. Its tracked -`knowledge-fs-unstructured-service.defaults` additionally enables bounded page parallelism for -PDFs and a 25-million-pixel pre-allocation limit per page at the pinned 350 DPI; an optional copied -`knowledge-fs-unstructured.env` can override those service values. Do not raise the DPI or pixel -limit, or disable the guard: the KnowledgeFS client estimates page rasters at 350 DPI and rejects -oversized or unverifiable page geometry before sending it to the parser. Oversized pages need -smaller page dimensions or tiling before import; reducing compressed file size alone does not -help. Existing installations need the new KnowledgeFS image and a recreated parser service to -activate both guards. The -existing `unstructured` profile remains unchanged for Dify's legacy ETL, so KnowledgeFS tuning -cannot alter its PDF or Office parsing behavior. The copied -`knowledge-fs.env` pairs every PDF and structurally/byte-heavy remote document with the longer -deadline and narrower admission gate; ordinary formats retain two concurrent requests and a -600-second timeout, while heavy work uses one concurrent request and a 2,400-second timeout. The -remote input cap defaults to 15 MiB so the parser accepts the complete product upload envelope. The -legacy `UNSTRUCTURED_PDF_*` names remain lower-precedence compatibility aliases. The required -defaults file deliberately does not end -in `.env`, so it remains tracked and a clean checkout can validate the profile; files ending in -`.env.example` are copy-only templates and are never loaded as runtime configuration. The isolated -service is available to KnowledgeFS at `http://knowledge_fs_unstructured:8000`; it does not publish -a host port. +### Change shared plugin credentials -The KnowledgeFS API image includes Poppler and enables its `pdftoppm` PDF image rasterizer with -bounded defaults (144 DPI, 48 DPI thumbnails, a 30-second timeout, at most 500 assets per document, -and two concurrent Poppler page batches per replica). Canonical settings live in the dedicated -`knowledge-fs.env`; whitelisted `DIFY_ROOT_*_OVERRIDE` proxies let explicitly set values in -`docker/.env` take precedence without injecting the complete root environment. When a root value -is unset, the service env or image default remains authoritative. Set -`KNOWLEDGE_PDF_RASTERIZER=off` in either operator env as an emergency or low-resource kill switch. Existing -documents whose parse artifacts lack image asset references must be ingested again after rollout. +Set both shared plugin credentials in root `.env`, so every service receives the +same values: -Use a dedicated KnowledgeFS database. Do not point `DATABASE_URL` at Dify's application database, -reuse Dataset/Document tables, or run a data migration as part of this service. KnowledgeFS -migrations remain a separate controlled operator step. The selected Dify storage backend must -support recursive `scan`; the currently verified paths are S3 and OpenDAL/local. +| Root `.env` setting | Used by | +| --- | --- | +| `PLUGIN_DAEMON_KEY` | Plugin daemon authentication and its API, worker, and Agent clients | +| `PLUGIN_DIFY_INNER_API_KEY` | API inner-endpoint authentication and its plugin daemon, worker, Agent, and KnowledgeFS clients | -The production entrypoint supports the explicitly selected Capability v2 verifier and accepts only -public JWKS material. The `knowledge_fs` container can still return `200` from -`/health` while `/ready` returns `503` when the verifier or another durable dependency is missing; -this is intentional fail-closed behavior. Do not add a proxy route, set -`KNOWLEDGE_FS_ENABLED=true`, or send product traffic until readiness returns `200` and the migration -and per-Workspace cutover gates are complete. +Generate a separate value for each key with `openssl rand -hex 32`, then add the two +assignments to `.env`. If you previously set these keys in a service `.env` file, +move them to root `.env`. Leave the derived container key expressions unchanged +when copying service defaults, including the Agent's derived key expressions. +Editing only `plugin-daemon.env`, `api.env`, or `worker.env` does not update every +consumer and can cause HTTP 401 responses. + +Run `docker compose up -d` to apply the credentials to all affected services. +These optional overrides do not need to be added to `.env.example`. + +### Select a database or vector store + +Set `DB_TYPE` or `VECTOR_STORE` and the provider's connection settings in the +appropriate override file. In root `.env`, update `COMPOSE_PROFILES` to select the +matching bundled services. Omit a service's profile when that service runs +externally. Starting a different database does not migrate existing data. + +For example, the default deployment selects PostgreSQL and Weaviate with +`COMPOSE_PROFILES=weaviate,postgresql,collaboration`. The `collaboration` profile +starts the WebSocket API. See the [database](envs/databases/) and +[vector store](envs/vectorstores/) files for provider-specific settings. + +### Understand which value takes effect + +For settings loaded through these env files, precedence is: + +1. Root `.env` overrides. +2. Optional service `.env` overrides. +3. Tracked `.defaults` values, followed by application defaults for unset settings. + +Shell variables take precedence when Compose interpolates `${...}` expressions. +An explicit Compose `environment` entry also takes precedence over service env +files; [trusted-proxy settings](#client-ip-and-trusted-proxies) are one example. +The [KnowledgeFS section](#enable-the-knowledgefs-integration) describes its +restricted root overrides separately. + +The API, workers, and plugin daemon also read the database and Redis override +files, so shared connection settings reach both the bundled server and its clients. +Changing a PostgreSQL or MySQL initialization password does not rotate credentials +in an existing database; use the database's credential-rotation procedure as well. + +## Troubleshoot setup and configuration + +Validate the Compose configuration and inspect the affected services: + +```bash +docker compose config --quiet +docker compose ps --all +docker compose logs --tail=100 api worker nginx +``` + +Replace the service names in the logs command with the service you are diagnosing. +A successful `config --quiet` produces no output; it validates the Compose model, +not application readiness. + +| Symptom | What to check | +| --- | --- | +| Editing a template has no effect | Copy `.env.example` to `.env`, or a service template to its documented `.env` path. Templates ending in `.example` are not loaded. | +| A service still uses its old value | Run `docker compose up -d`. Check for a higher-priority value in root `.env`, the shell, or an explicit Compose `environment` entry. | +| A port or profile change has no effect | Put the setting in root `.env`; these settings cannot be supplied through a service env file. | +| Service overrides resolve unexpectedly | Check that Docker Compose is v2.35.0 or later and that container-variable mapping expressions are intact. | +| Python sandbox code returns `operation not permitted` | Check the network-setting note in [sandbox.defaults](envs/core-services/sandbox.defaults). On sandbox 0.2.15/ARM64, requests with networking disabled can trigger a runtime syscall restriction. | + +## Review configuration after an upgrade + +Review the new root `.env.example` for required deployment inputs and the matching +`.defaults` files for changed service defaults. Preserve your existing `.env` +files; a copied service file can pin values from an older release. + +Bundled service templates now use the `.defaults` extension. Existing service +`.env` paths still work. Root `.env.example` remains the only required starting +template, and the Compose generator creates only a missing root `.env`. + +The shared application reference is now consolidated in +[shared.defaults](envs/core-services/shared.defaults). Existing `shared.env` +and other override paths still work; do not replace or rename your local files. +New customizations use the matching template listed above. Root `.env` retains +precedence, including for settings that have moved to a provider template. + +Review accidental empty assignments left by older template copies. For example, +`CELERY_WORKER_AMOUNT=` overrides the Docker default of `4` and lets the worker +entrypoint fall back to `1`. Remove or comment out that assignment to restore +the Docker default. Likewise, remove accidental empty `SERVER_WORKER_CLASS=` +or `GUNICORN_TIMEOUT=` entries to restore `gevent` and `360`. Keep intentional +overrides; the configuration loader does not rewrite or discard them. + +To merge new root-template keys into an existing `.env`, optionally run: + +```bash +bash dify-env-sync.sh +``` + +The script backs up `.env` to `env-backup/.env.backup_`, adds missing +keys from `.env.example`, preserves existing values and extra local overrides, +and reports differences for review. It does not synchronize the files in `envs/`. +Review the resulting settings before running `docker compose up -d`. + +## Run middleware for development + +Use `docker-compose.middleware.yaml` when running the application from source and +you need its supporting services in Docker. + +1. Create `middleware.env` if it does not exist: + + ```bash + cp envs/middleware.env.example middleware.env + ``` + +2. Edit database and other middleware settings, then start the services: + + ```bash + docker compose --env-file middleware.env -f docker-compose.middleware.yaml -p dify up -d + ``` + +This separate workflow requires `--env-file middleware.env`. The template defines +`COMPOSE_PROFILES=${DB_TYPE:-postgresql},weaviate`, which selects the database and +Weaviate. Update that value if you need different profiles. + +## Configure HTTPS and telemetry + +For HTTPS certificates, follow the [Certbot guide](certbot/README.md). + +For OpenTelemetry, create `envs/core-services/shared.env` from `shared.defaults` +if it does not exist. Uncomment `ENABLE_OTEL` and set it to `true`; uncomment +`OTLP_BASE_ENDPOINT` and set it to your collector's endpoint, reachable from +the API and worker containers. The same file documents optional `OTEL_*` +tuning. Apply the changes with `docker compose up -d`. + +## Configure worker queues and webhooks + +Custom worker queue lists set through `CELERY_QUEUES` or `CELERY_WORKER_QUEUES` must include +`human_input_delivery` to process durable Human Input IM inbox events and recover +unconfirmed Human Input v2 notifications, and +`human_input_contact_sync` to process manual IM Contact directory synchronization. + +Human Input IM channels in `WEBHOOK` mode build their Callback URL from `TRIGGER_URL`. +Set it to the externally reachable HTTPS origin of the API when configuring a remote +deployment. A localhost value is only suitable for local development. Reverse proxies +must forward `/callbacks/human-input/v2/im/` to the API and preserve the request path, +body, and provider signature headers. Copy the channel's Callback URL into the +provider's interaction configuration after changing the public origin. ### Ops tracing rolling upgrades @@ -111,7 +307,87 @@ retry tasks finish. To return to `ops_trace`, first remove old workers, have new both queues, then change `OPS_TRACE_QUEUE` on all new processes and drain `ops_trace_v2` before removing its consumers. See the [complete rollout sequence](../api/providers/trace/README.md#rolling-upgrade-from-the-previous-pipeline). -### KnowledgeFS lifecycle and upgrade workers +## Enable the KnowledgeFS integration + +The optional `knowledge-fs` Compose profile starts the internal KnowledgeFS API service and the +dedicated Dataset upgrade worker. The API service publishes no host port and has no nginx route. +Model, datasource, and object-storage calls go through Dify's inner API. +Dify resolves active Workspace/plugin configuration and remains the only owner of physical +storage credentials. `KNOWLEDGE_FS_ENABLED` remains `false`, so starting the container does not +enable product traffic. + +Copy the dedicated service template if the local file does not already exist: + +```bash +cp envs/core-services/knowledge-fs.env.example envs/core-services/knowledge-fs.env +``` + +Fill in the operator-owned inputs described in `knowledge-fs.env` before starting +the services. The default image is the CI-published deployment-branch image; set +`KNOWLEDGE_FS_API_IMAGE` to pin another tag or immutable SHA tag. If the image +repository is private, authenticate with `docker login` before pulling: + +```bash +docker compose --profile knowledge-fs --profile knowledge-fs-unstructured config --quiet +docker compose --profile knowledge-fs --profile knowledge-fs-unstructured pull knowledge_fs knowledge_fs_unstructured +docker compose --profile knowledge-fs --profile knowledge-fs-unstructured up -d +``` + +The service env file intentionally contains only operator-owned inputs: the dedicated database, +durable compilation switch, Capability v2 public verification material, and optional Unstructured +endpoint. Compose injects the Dify inner API connection and integrated mode. KnowledgeFS stores +objects below an internal namespace in Dify's configured `STORAGE_TYPE`; do not duplicate Dify's +bucket, endpoint, or provider credentials in `knowledge-fs.env`. Feature-specific rollout flags +and capacity tunables should be added only when deliberately overriding their safe runtime +defaults. + +### Configure document parsing + +The optional `knowledge-fs-unstructured` profile starts one isolated parser service named +`knowledge_fs_unstructured` for every KnowledgeFS remote format. Its tracked +`knowledge-fs-unstructured-service.defaults` additionally enables bounded page parallelism for +PDFs and a 25-million-pixel pre-allocation limit per page at the pinned 350 DPI; an optional copied +`knowledge-fs-unstructured.env` can override those service values. Do not raise the DPI or pixel +limit, or disable the guard: the KnowledgeFS client estimates page rasters at 350 DPI and rejects +oversized or unverifiable page geometry before sending it to the parser. Oversized pages need +smaller page dimensions or tiling before import; reducing compressed file size alone does not +help. Existing installations need the new KnowledgeFS image and a recreated parser service to +activate both guards. The existing `unstructured` profile remains unchanged for Dify's legacy +ETL, so KnowledgeFS tuning cannot alter its PDF or Office parsing behavior. The copied +`knowledge-fs.env` pairs every PDF and structurally/byte-heavy remote document with the longer +deadline and narrower admission gate; ordinary formats retain two concurrent requests and a +600-second timeout, while heavy work uses one concurrent request and a 2,400-second timeout. The +remote input cap defaults to 15 MiB so the parser accepts the complete product upload envelope. The +legacy `UNSTRUCTURED_PDF_*` names remain lower-precedence compatibility aliases. The isolated +service is available to KnowledgeFS at `http://knowledge_fs_unstructured:8000`; it does not +publish a host port. + +### Configure PDF image rendering + +The KnowledgeFS API image includes Poppler and enables its `pdftoppm` PDF image rasterizer with +bounded defaults (144 DPI, 48 DPI thumbnails, a 30-second timeout, at most 500 assets per document, +and two concurrent Poppler page batches per replica). Canonical settings live in the dedicated +`knowledge-fs.env`; whitelisted `DIFY_ROOT_*_OVERRIDE` proxies let explicitly set values in +`docker/.env` take precedence without injecting the complete root environment. When a root value +is unset, the service env or image default remains authoritative. Set +`KNOWLEDGE_PDF_RASTERIZER=off` in either operator env as an emergency or low-resource kill switch. Existing +documents whose parse artifacts lack image asset references must be ingested again after rollout. + +### Check storage and readiness before enabling traffic + +Use a dedicated KnowledgeFS database. Do not point `DATABASE_URL` at Dify's application database, +reuse Dataset/Document tables, or run a data migration as part of this service. KnowledgeFS +migrations remain a separate controlled operator step. The selected Dify storage backend must +support recursive `scan`; the currently verified paths are S3 and OpenDAL/local. + +The production entrypoint supports the explicitly selected Capability v2 verifier and accepts only +public JWKS material. The `knowledge_fs` container can still return `200` from +`/health` while `/ready` returns `503` when the verifier or another durable dependency is missing; +this is intentional fail-closed behavior. Do not add a proxy route, set +`KNOWLEDGE_FS_ENABLED=true`, or send product traffic until readiness returns `200` and the migration +and per-Workspace cutover gates are complete. + +### Run lifecycle and upgrade workers The standard Dify worker consumes `knowledge_fs_lifecycle` for provisioning, deletion, initial source imports, and staged-upload cleanup. Deployments overriding `CELERY_QUEUES` or @@ -120,8 +396,9 @@ on the API, workers, and Beat before creating spaces. Classic Dataset upgrades use the separate `worker_knowledge_fs_upgrade` service. It starts with the `knowledge-fs` Compose profile, so enable that profile before submitting an upgrade. This -worker must use `CELERY_WORKER_POOL=prefork`; local development uses -`./dev/start-worker --queues knowledge_fs_upgrade --pool prefork`. Each checkpoint has a 570-second +worker must use `CELERY_WORKER_POOL=prefork`; for local development, run +`./dev/start-worker --queues knowledge_fs_upgrade --pool prefork` from the repository root. +Each checkpoint has a 570-second soft limit and a 600-second hard limit. Do not override these task limits: recovery relies on the child having stopped before reclaiming its execution window. Beat checks for abandoned running jobs and publication claims every minute after the lifecycle gates are ready. Claims without @@ -260,89 +537,3 @@ changing client-supplied IP headers does not change the resolved address, and an untrusted pod cannot connect directly to the API. IP limits still need independent account/email limits because NAT can share public addresses and attackers can use multiple real addresses. - -## How to Deploy Middleware for Developing Dify - -1. **Middleware Setup**: - - Use the `docker-compose.middleware.yaml` for setting up essential middleware services like databases and caches. - - Navigate to the `docker` directory. - - Ensure the `middleware.env` file is created by running `cp envs/middleware.env.example middleware.env` (refer to the `envs/middleware.env.example` file). -2. **Running Middleware Services**: - - Navigate to the `docker` directory. - - Execute `docker compose --env-file middleware.env -f docker-compose.middleware.yaml -p dify up -d` to start PostgreSQL/MySQL (per `DB_TYPE`) plus the bundled Weaviate instance. - -> Compose automatically loads `COMPOSE_PROFILES=${DB_TYPE:-postgresql},weaviate` from `middleware.env`, so no extra `--profile` flags are needed. Adjust variables in `middleware.env` if you want a different combination of services. - -## Overview of `.env`, `.env.example`, and `envs/` - -- `.env.example` contains only settings requiring manual configuration, such as the Compose service selection. -- `.env` contains your deployment inputs and any explicit overrides. -- `envs/*.env.example` files contain optional advanced configuration grouped by theme. - -Keep the root `.env.example` limited to settings that require manual user configuration. -Settings with usable application or Docker defaults must not appear there. Document optional, -advanced, provider-specific, and service-specific overrides in the appropriate `envs/*.env.example` file. - -Docker Compose loads Docker-specific defaults from `envs/core-services/shared.defaults`, -then optional `envs/*.env` files, then `.env` so your overrides take precedence. Other defaults -come from the application and each service in `docker-compose.yaml`; do not copy them into `.env`. -The bundled credentials are intended for local deployment; override them for production using -the matching security and service templates. - -For the available variables and defaults, read the matching `envs/**/*.env.example` template and its service in `docker-compose.yaml`. - -Custom worker queue lists set through `CELERY_QUEUES` or `CELERY_WORKER_QUEUES` must include -`human_input_delivery` to process durable Human Input IM inbox events and recover -unconfirmed Human Input v2 notifications, and -`human_input_contact_sync` to process manual IM Contact directory synchronization. - -Human Input IM channels in `WEBHOOK` mode build their Callback URL from `TRIGGER_URL`. -Set it to the externally reachable HTTPS origin of the API when configuring a remote -deployment. A localhost value is only suitable for local development. Reverse proxies -must forward `/callbacks/human-input/v2/im/` to the API and preserve the request path, -body, and provider signature headers. Copy the channel's Callback URL into the -provider's interaction configuration after changing the public origin. - -## Environment Variables Synchronization - -When upgrading Dify or pulling the latest changes, new environment variables may be introduced in `.env.example` only when they require manual configuration, -or in the optional files under `envs/` for advanced, provider-specific, and service-specific settings. - -If you use the default workflow, review `.env.example` and supply any new deployment inputs that require manual configuration. - -If you maintain a customized `.env` file copied from `.env.example`, an optional environment variables synchronization tool is provided. - -> This tool performs a **one-way synchronization** from `.env.example` to `.env`. -> Existing values in `.env` are never overwritten automatically. - -### `dify-env-sync.sh` (Optional) - -This script compares your current `.env` file with the latest `.env.example` template and helps safely apply new or updated environment variables. - -**What it does** - -- Creates a backup of the current `.env` file before making any changes -- Synchronizes newly added environment variables from `.env.example` -- Preserves the existing `.env`, including custom settings absent from `.env.example` -- Displays differences and additional local overrides for review - -**Backup behavior** - -Before synchronization, the current `.env` file is saved to the `env-backup/` directory with a timestamped filename -(e.g. `env-backup/.env.backup_20231218_143022`). - -**When to use** - -- After upgrading Dify to a newer version with a full `.env` file -- When `.env.example` has been updated with new environment variables -- When managing a large or heavily customized `.env` file copied from `.env.example` - -**Usage** - -```bash -# Grant execution permission (first time only) -chmod +x dify-env-sync.sh - -# Run the synchronization -./dify-env-sync.sh -``` diff --git a/docker/docker-compose-template.yaml b/docker/docker-compose-template.yaml index 928835b9e9b..fd6f5e33799 100644 --- a/docker/docker-compose-template.yaml +++ b/docker/docker-compose-template.yaml @@ -1,11 +1,8 @@ # Shared configuration using YAML anchors and env_file -x-shared-api-worker-config: &shared-api-worker-config +x-shared-api-config: &shared-api-config env_file: - - ./envs/core-services/shared.defaults - path: ./envs/core-services/shared.env required: false - - path: ./envs/core-services/api.env - required: false - path: ./envs/security.env required: false - path: ./envs/databases/db-postgres.env @@ -60,7 +57,11 @@ x-shared-api-worker-config: &shared-api-worker-config required: false - path: ./envs/infrastructure/milvus-standalone.env required: false + - path: ./envs/core-services/api.env + required: false - ./.env + - ./envs/core-services/shared.defaults + - ./envs/core-services/api.defaults networks: - ssrf_proxy_network - default @@ -68,11 +69,8 @@ x-shared-api-worker-config: &shared-api-worker-config x-shared-worker-config: &shared-worker-config env_file: - - ./envs/core-services/shared.defaults - path: ./envs/core-services/shared.env required: false - - path: ./envs/core-services/worker.env - required: false - path: ./envs/security.env required: false - path: ./envs/databases/db-postgres.env @@ -127,7 +125,11 @@ x-shared-worker-config: &shared-worker-config required: false - path: ./envs/infrastructure/milvus-standalone.env required: false + - path: ./envs/core-services/worker.env + required: false - ./.env + - ./envs/core-services/shared.defaults + - ./envs/core-services/worker.defaults networks: - ssrf_proxy_network - default @@ -143,11 +145,8 @@ x-knowledge-fs-celery-worker: &knowledge-fs-celery-worker # Match the two-hour default task deadline plus shutdown margin for long document jobs. stop_grace_period: 125m env_file: - - ./envs/core-services/shared.defaults - path: ./envs/core-services/shared.env required: false - - path: ./envs/core-services/worker.env - required: false - path: ./envs/security.env required: false - path: ./envs/databases/db-postgres.env @@ -160,7 +159,11 @@ x-knowledge-fs-celery-worker: &knowledge-fs-celery-worker required: false - path: ./envs/core-services/knowledge-fs-worker.env required: false + - path: ./envs/core-services/worker.env + required: false - ./.env + - ./envs/core-services/shared.defaults + - ./envs/core-services/worker.defaults environment: &knowledge-fs-celery-environment MODE: worker MIGRATION_ENABLED: "false" @@ -181,11 +184,8 @@ x-knowledge-fs-celery-worker: &knowledge-fs-celery-worker x-shared-worker-beat-config: &shared-worker-beat-config env_file: - - ./envs/core-services/shared.defaults - path: ./envs/core-services/shared.env required: false - - path: ./envs/core-services/worker-beat.env - required: false - path: ./envs/security.env required: false - path: ./envs/databases/db-postgres.env @@ -240,7 +240,11 @@ x-shared-worker-beat-config: &shared-worker-beat-config required: false - path: ./envs/infrastructure/milvus-standalone.env required: false + - path: ./envs/core-services/worker-beat.env + required: false - ./.env + - ./envs/core-services/shared.defaults + - ./envs/core-services/worker-beat.defaults networks: - ssrf_proxy_network - default @@ -268,22 +272,10 @@ services: # API service api: - <<: *shared-api-worker-config + <<: *shared-api-config image: langgenius/dify-api:1.18.0 environment: MODE: api - # The bundled Nginx supplies a verified client IP; keep direct API callers trusted. - RESPECT_XFORWARD_HEADERS_ENABLED: ${RESPECT_XFORWARD_HEADERS_ENABLED:-true} - SENTRY_DSN: ${API_SENTRY_DSN:-} - SENTRY_TRACES_SAMPLE_RATE: ${API_SENTRY_TRACES_SAMPLE_RATE:-1.0} - SENTRY_PROFILES_SAMPLE_RATE: ${API_SENTRY_PROFILES_SAMPLE_RATE:-1.0} - PLUGIN_REMOTE_INSTALL_HOST: ${EXPOSE_PLUGIN_DEBUGGING_HOST:-localhost} - PLUGIN_REMOTE_INSTALL_PORT: ${EXPOSE_PLUGIN_DEBUGGING_PORT:-5003} - PLUGIN_MAX_PACKAGE_SIZE: ${PLUGIN_MAX_PACKAGE_SIZE:-52428800} - PLUGIN_DAEMON_TIMEOUT: ${PLUGIN_DAEMON_TIMEOUT:-600.0} - INNER_API_KEY_FOR_PLUGIN: ${PLUGIN_DIFY_INNER_API_KEY:-QaHbTe77CtuXmsfyhR7+vRjI/+XbV1AaFy691iy+kGDv2Jvy0/eAh8Y1} - AGENT_BACKEND_BASE_URL: ${AGENT_BACKEND_BASE_URL:-http://agent_backend:5050} - AGENT_BACKEND_API_TOKEN: ${DIFY_AGENT_API_TOKEN:-dify-agent-run-token-for-dev-only} depends_on: init_permissions: condition: service_completed_successfully @@ -318,17 +310,22 @@ services: # WebSocket service for workflow collaboration. api_websocket: - <<: *shared-api-worker-config + <<: *shared-api-config image: langgenius/dify-api:1.18.0 profiles: - collaboration environment: MODE: api - RESPECT_XFORWARD_HEADERS_ENABLED: ${RESPECT_XFORWARD_HEADERS_ENABLED:-true} - SERVER_WORKER_AMOUNT: ${API_WEBSOCKET_WORKER_AMOUNT:-1} - SERVER_WORKER_CLASS: ${API_WEBSOCKET_WORKER_CLASS:-geventwebsocket.gunicorn.workers.GeventWebSocketWorker} - SERVER_WORKER_CONNECTIONS: ${API_WEBSOCKET_WORKER_CONNECTIONS:-1000} - GUNICORN_TIMEOUT: ${API_WEBSOCKET_GUNICORN_TIMEOUT:-360} + # 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 @@ -347,20 +344,12 @@ services: - ssrf_proxy_network - default - # worker service # The Celery worker for processing all queues (dataset, workflow, mail, etc.) worker: <<: *shared-worker-config image: langgenius/dify-api:1.18.0 environment: MODE: worker - SENTRY_DSN: ${API_SENTRY_DSN:-} - SENTRY_TRACES_SAMPLE_RATE: ${API_SENTRY_TRACES_SAMPLE_RATE:-1.0} - SENTRY_PROFILES_SAMPLE_RATE: ${API_SENTRY_PROFILES_SAMPLE_RATE:-1.0} - PLUGIN_MAX_PACKAGE_SIZE: ${PLUGIN_MAX_PACKAGE_SIZE:-52428800} - INNER_API_KEY_FOR_PLUGIN: ${PLUGIN_DIFY_INNER_API_KEY:-QaHbTe77CtuXmsfyhR7+vRjI/+XbV1AaFy691iy+kGDv2Jvy0/eAh8Y1} - AGENT_BACKEND_BASE_URL: ${AGENT_BACKEND_BASE_URL:-http://agent_backend:5050} - AGENT_BACKEND_API_TOKEN: ${DIFY_AGENT_API_TOKEN:-dify-agent-run-token-for-dev-only} depends_on: init_permissions: condition: service_completed_successfully @@ -407,9 +396,6 @@ services: CELERY_WORKER_POOL: prefork CELERY_WORKER_CONCURRENCY: ${KNOWLEDGE_FS_UPGRADE_WORKER_AMOUNT:-2} CELERY_PREFETCH_MULTIPLIER: 1 - SENTRY_DSN: ${API_SENTRY_DSN:-} - SENTRY_TRACES_SAMPLE_RATE: ${API_SENTRY_TRACES_SAMPLE_RATE:-1.0} - SENTRY_PROFILES_SAMPLE_RATE: ${API_SENTRY_PROFILES_SAMPLE_RATE:-1.0} depends_on: init_permissions: condition: service_completed_successfully @@ -467,123 +453,91 @@ services: image: langgenius/dify-web:1.18.0 restart: always env_file: - - ./envs/core-services/shared.defaults - path: ./envs/core-services/web.env required: false - path: ./envs/security.env required: false - ./.env - environment: - CONSOLE_API_URL: ${CONSOLE_API_URL:-} - SERVER_CONSOLE_API_URL: ${SERVER_CONSOLE_API_URL:-http://api:5001} - APP_API_URL: ${APP_API_URL:-} - AMPLITUDE_API_KEY: ${AMPLITUDE_API_KEY:-} - TURNSTILE_SITE_KEY: ${TURNSTILE_SITE_KEY:-} - NEXT_PUBLIC_COOKIE_DOMAIN: ${NEXT_PUBLIC_COOKIE_DOMAIN:-} - NEXT_PUBLIC_SOCKET_URL: ${NEXT_PUBLIC_SOCKET_URL:-ws://localhost} - SENTRY_DSN: ${WEB_SENTRY_DSN:-} - NEXT_TELEMETRY_DISABLED: ${NEXT_TELEMETRY_DISABLED:-0} - EXPERIMENTAL_ENABLE_VINEXT: ${EXPERIMENTAL_ENABLE_VINEXT:-false} - TEXT_GENERATION_TIMEOUT_MS: ${TEXT_GENERATION_TIMEOUT_MS:-60000} - CSP_WHITELIST: ${CSP_WHITELIST:-} - ALLOW_EMBED: ${ALLOW_EMBED:-false} - ALLOW_UNSAFE_DATA_SCHEME: ${ALLOW_UNSAFE_DATA_SCHEME:-false} - MARKETPLACE_API_URL: ${MARKETPLACE_API_URL:-https://marketplace.dify.ai} - MARKETPLACE_URL: ${MARKETPLACE_URL:-https://marketplace.dify.ai} - TOP_K_MAX_VALUE: ${TOP_K_MAX_VALUE:-10} - INDEXING_MAX_SEGMENTATION_TOKENS_LENGTH: ${INDEXING_MAX_SEGMENTATION_TOKENS_LENGTH:-4000} - LOOP_NODE_MAX_COUNT: ${LOOP_NODE_MAX_COUNT:-100} - MAX_TOOLS_NUM: ${MAX_TOOLS_NUM:-10} - MAX_PARALLEL_LIMIT: ${MAX_PARALLEL_LIMIT:-10} - MAX_ITERATIONS_NUM: ${MAX_ITERATIONS_NUM:-99} - MAX_TREE_DEPTH: ${MAX_TREE_DEPTH:-50} - ENABLE_WEBSITE_JINAREADER: ${ENABLE_WEBSITE_JINAREADER:-true} - ENABLE_WEBSITE_FIRECRAWL: ${ENABLE_WEBSITE_FIRECRAWL:-true} - ENABLE_WEBSITE_WATERCRAWL: ${ENABLE_WEBSITE_WATERCRAWL:-true} + - ./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 - environment: - POSTGRES_USER: ${DB_USERNAME:-postgres} - POSTGRES_PASSWORD: ${DB_PASSWORD:-difyai123456} - POSTGRES_DB: ${DB_DATABASE:-dify} - PGDATA: ${PGDATA:-/var/lib/postgresql/data/pgdata} - command: > - postgres -c 'max_connections=${POSTGRES_MAX_CONNECTIONS:-200}' - -c 'shared_buffers=${POSTGRES_SHARED_BUFFERS:-128MB}' - -c 'work_mem=${POSTGRES_WORK_MEM:-4MB}' - -c 'maintenance_work_mem=${POSTGRES_MAINTENANCE_WORK_MEM:-64MB}' - -c 'effective_cache_size=${POSTGRES_EFFECTIVE_CACHE_SIZE:-4096MB}' - -c 'statement_timeout=${POSTGRES_STATEMENT_TIMEOUT:-0}' - -c 'idle_in_transaction_session_timeout=${POSTGRES_IDLE_IN_TRANSACTION_SESSION_TIMEOUT:-0}' + # 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", - "pg_isready", - "-h", - "db_postgres", - "-U", - "${DB_USERNAME:-postgres}", - "-d", - "${DB_DATABASE:-dify}", - ] + 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 - environment: - MYSQL_ROOT_PASSWORD: ${DB_PASSWORD:-difyai123456} - MYSQL_DATABASE: ${DB_DATABASE:-dify} - command: > - --max_connections=${MYSQL_MAX_CONNECTIONS:-1000} - --innodb_buffer_pool_size=${MYSQL_INNODB_BUFFER_POOL_SIZE:-512M} - --innodb_log_file_size=${MYSQL_INNODB_LOG_FILE_SIZE:-128M} - --innodb_flush_log_at_trx_commit=${MYSQL_INNODB_FLUSH_LOG_AT_TRX_COMMIT:-2} + 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", - "mysqladmin", - "ping", - "-u", - "root", - "-p${DB_PASSWORD:-difyai123456}", - ] + 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 - environment: - REDISCLI_AUTH: ${REDIS_PASSWORD:-difyai123456} volumes: # Mount the redis data directory to the container. - ./volumes/redis/data:/data # Set the redis password when startup redis server. - command: redis-server --requirepass ${REDIS_PASSWORD:-difyai123456} + command: ["sh", "-c", 'exec docker-entrypoint.sh redis-server --requirepass "$$REDIS_PASSWORD"'] healthcheck: test: [ "CMD-SHELL", - "redis-cli -a ${REDIS_PASSWORD:-difyai123456} ping | grep -q PONG", + "redis-cli ping | grep -q PONG", ] # The DifySandbox @@ -596,23 +550,12 @@ services: - path: ./envs/security.env required: false - ./.env - environment: - # The DifySandbox configurations - # Make sure you are changing this key for your deployment with a strong key. - # You can generate a strong key using `openssl rand -base64 42`. - API_KEY: ${SANDBOX_API_KEY:-dify-sandbox} - GIN_MODE: ${SANDBOX_GIN_MODE:-release} - WORKER_TIMEOUT: ${SANDBOX_WORKER_TIMEOUT:-15} - ENABLE_NETWORK: ${SANDBOX_ENABLE_NETWORK:-true} - HTTP_PROXY: ${SANDBOX_HTTP_PROXY:-http://ssrf_proxy:3128} - HTTPS_PROXY: ${SANDBOX_HTTPS_PROXY:-http://ssrf_proxy:3128} - SANDBOX_PORT: ${SANDBOX_PORT:-8194} - PIP_MIRROR_URL: ${PIP_MIRROR_URL:-} + - ./envs/core-services/sandbox.defaults volumes: - ./volumes/sandbox/dependencies:/dependencies - ./volumes/sandbox/conf:/conf healthcheck: - test: ["CMD", "curl", "-f", "http://localhost:8194/health"] + test: ["CMD-SHELL", 'curl -f "http://localhost:$$SANDBOX_PORT/health"'] networks: - ssrf_proxy_network @@ -668,57 +611,13 @@ services: - path: ./envs/databases/redis.env required: false - ./.env + - ./envs/core-services/shared.defaults + - ./envs/core-services/plugin-daemon.defaults networks: - ssrf_proxy_network - default environment: - DB_DATABASE: ${DB_PLUGIN_DATABASE:-dify_plugin} - DB_SSL_MODE: ${DB_SSL_MODE:-disable} - SERVER_PORT: ${PLUGIN_DAEMON_PORT:-5002} - SERVER_KEY: ${PLUGIN_DAEMON_KEY:-lYkiYYT6owG+71oLerGzA7GXCgOT++6ovaezWAjpCjf+Sjc3ZtU+qUEi} - MAX_PLUGIN_PACKAGE_SIZE: ${PLUGIN_MAX_PACKAGE_SIZE:-52428800} - PPROF_ENABLED: ${PLUGIN_PPROF_ENABLED:-false} - 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} - PLUGIN_REMOTE_INSTALLING_HOST: ${PLUGIN_DEBUGGING_HOST:-0.0.0.0} PLUGIN_REMOTE_INSTALLING_PORT: ${PLUGIN_DEBUGGING_PORT:-5003} - PLUGIN_WORKING_PATH: ${PLUGIN_WORKING_PATH:-/app/storage/cwd} - FORCE_VERIFYING_SIGNATURE: ${FORCE_VERIFYING_SIGNATURE:-true} - PYTHON_ENV_INIT_TIMEOUT: ${PLUGIN_PYTHON_ENV_INIT_TIMEOUT:-120} - PLUGIN_MAX_EXECUTION_TIMEOUT: ${PLUGIN_MAX_EXECUTION_TIMEOUT:-600} - PLUGIN_STDIO_BUFFER_SIZE: ${PLUGIN_STDIO_BUFFER_SIZE:-1024} - PLUGIN_STDIO_MAX_BUFFER_SIZE: ${PLUGIN_STDIO_MAX_BUFFER_SIZE:-5242880} - PIP_MIRROR_URL: ${PIP_MIRROR_URL:-} - PLUGIN_STORAGE_TYPE: ${PLUGIN_STORAGE_TYPE:-local} - PLUGIN_STORAGE_LOCAL_ROOT: ${PLUGIN_STORAGE_LOCAL_ROOT:-/app/storage} - PLUGIN_INSTALLED_PATH: ${PLUGIN_INSTALLED_PATH:-plugin} - PLUGIN_PACKAGE_CACHE_PATH: ${PLUGIN_PACKAGE_CACHE_PATH:-plugin_packages} - PLUGIN_MEDIA_CACHE_PATH: ${PLUGIN_MEDIA_CACHE_PATH:-assets} - PLUGIN_STORAGE_OSS_BUCKET: ${PLUGIN_STORAGE_OSS_BUCKET:-} - S3_USE_AWS_MANAGED_IAM: ${PLUGIN_S3_USE_AWS_MANAGED_IAM:-false} - S3_USE_AWS: ${PLUGIN_S3_USE_AWS:-false} - S3_ENDPOINT: ${PLUGIN_S3_ENDPOINT:-} - S3_USE_PATH_STYLE: ${PLUGIN_S3_USE_PATH_STYLE:-false} - AWS_ACCESS_KEY: ${PLUGIN_AWS_ACCESS_KEY:-} - AWS_SECRET_KEY: ${PLUGIN_AWS_SECRET_KEY:-} - AWS_REGION: ${PLUGIN_AWS_REGION:-} - AZURE_BLOB_STORAGE_CONNECTION_STRING: ${PLUGIN_AZURE_BLOB_STORAGE_CONNECTION_STRING:-} - AZURE_BLOB_STORAGE_CONTAINER_NAME: ${PLUGIN_AZURE_BLOB_STORAGE_CONTAINER_NAME:-} - TENCENT_COS_SECRET_KEY: ${PLUGIN_TENCENT_COS_SECRET_KEY:-} - TENCENT_COS_SECRET_ID: ${PLUGIN_TENCENT_COS_SECRET_ID:-} - TENCENT_COS_REGION: ${PLUGIN_TENCENT_COS_REGION:-} - ALIYUN_OSS_REGION: ${PLUGIN_ALIYUN_OSS_REGION:-} - ALIYUN_OSS_ENDPOINT: ${PLUGIN_ALIYUN_OSS_ENDPOINT:-} - ALIYUN_OSS_ACCESS_KEY_ID: ${PLUGIN_ALIYUN_OSS_ACCESS_KEY_ID:-} - ALIYUN_OSS_ACCESS_KEY_SECRET: ${PLUGIN_ALIYUN_OSS_ACCESS_KEY_SECRET:-} - ALIYUN_OSS_AUTH_VERSION: ${PLUGIN_ALIYUN_OSS_AUTH_VERSION:-v4} - ALIYUN_OSS_PATH: ${PLUGIN_ALIYUN_OSS_PATH:-} - VOLCENGINE_TOS_ENDPOINT: ${PLUGIN_VOLCENGINE_TOS_ENDPOINT:-} - VOLCENGINE_TOS_ACCESS_KEY: ${PLUGIN_VOLCENGINE_TOS_ACCESS_KEY:-} - VOLCENGINE_TOS_SECRET_KEY: ${PLUGIN_VOLCENGINE_TOS_SECRET_KEY:-} - VOLCENGINE_TOS_REGION: ${PLUGIN_VOLCENGINE_TOS_REGION:-} - SENTRY_ENABLED: ${PLUGIN_SENTRY_ENABLED:-false} - SENTRY_DSN: ${PLUGIN_SENTRY_DSN:-} ports: - "${EXPOSE_PLUGIN_DEBUGGING_PORT:-5003}:${PLUGIN_DEBUGGING_PORT:-5003}" volumes: @@ -827,29 +726,14 @@ services: image: langgenius/dify-agent-backend:1.18.0 restart: always env_file: - - path: ./envs/core-services/dify-agent.env - required: false - path: ./envs/security.env required: false - path: ./envs/databases/redis.env required: false + - path: ./envs/core-services/dify-agent.env + required: false - ./.env - environment: - DIFY_AGENT_REDIS_URL: ${DIFY_AGENT_REDIS_URL:-redis://:${REDIS_PASSWORD:-difyai123456}@redis:6379/2} - DIFY_AGENT_PLUGIN_DAEMON_URL: ${DIFY_AGENT_PLUGIN_DAEMON_URL:-${PLUGIN_DAEMON_URL:-http://plugin_daemon:5002}} - DIFY_AGENT_PLUGIN_DAEMON_API_KEY: ${DIFY_AGENT_PLUGIN_DAEMON_API_KEY:-${PLUGIN_DAEMON_KEY:-lYkiYYT6owG+71oLerGzA7GXCgOT++6ovaezWAjpCjf+Sjc3ZtU+qUEi}} - DIFY_AGENT_INNER_API_URL: ${DIFY_AGENT_INNER_API_URL:-${PLUGIN_DIFY_INNER_API_URL:-http://api:5001}} - DIFY_AGENT_INNER_API_KEY: ${DIFY_AGENT_INNER_API_KEY:-${PLUGIN_DIFY_INNER_API_KEY:-QaHbTe77CtuXmsfyhR7+vRjI/+XbV1AaFy691iy+kGDv2Jvy0/eAh8Y1}} - DIFY_AGENT_RUNTIME_BACKEND: ${DIFY_AGENT_RUNTIME_BACKEND:-local} - DIFY_AGENT_LOCAL_SANDBOX_ENDPOINT: ${DIFY_AGENT_LOCAL_SANDBOX_ENDPOINT:-${DIFY_AGENT_SHELLCTL_ENTRYPOINT:-http://local_sandbox:5004}} - DIFY_AGENT_LOCAL_SANDBOX_AUTH_TOKEN: ${DIFY_AGENT_LOCAL_SANDBOX_AUTH_TOKEN:-${DIFY_AGENT_SHELLCTL_AUTH_TOKEN:-}} - DIFY_AGENT_STUB_API_BASE_URL: ${DIFY_AGENT_STUB_API_BASE_URL:-http://agent_backend:5050/agent-stub} - DIFY_AGENT_SANDBOX_FILES_BASE_URL: ${DIFY_AGENT_SANDBOX_FILES_BASE_URL:-http://api:5001} - # This is security-sensitive: it derives the JWE encryption key for Agent Stub bearer tokens. - # Replace this development default in production. - # Generate one with: python -c 'import secrets; print(secrets.token_urlsafe(32))' - DIFY_AGENT_SERVER_SECRET_KEY: ${DIFY_AGENT_SERVER_SECRET_KEY:-MDEyMzQ1Njc4OWFiY2RlZjAxMjM0NTY3ODlhYmNkZWY} - DIFY_AGENT_API_TOKEN: ${DIFY_AGENT_API_TOKEN:-dify-agent-run-token-for-dev-only} + - ./envs/core-services/dify-agent.defaults depends_on: redis: condition: service_started @@ -875,11 +759,10 @@ services: "-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: - HTTP_PORT: ${SSRF_HTTP_PORT:-3128} - COREDUMP_DIR: ${SSRF_COREDUMP_DIR:-/var/spool/squid} - SSRF_PROXY_ALLOW_PRIVATE_IPS: ${SSRF_PROXY_ALLOW_PRIVATE_IPS:-} - SSRF_PROXY_ALLOW_PRIVATE_DOMAINS: ${SSRF_PROXY_ALLOW_PRIVATE_DOMAINS:-} + 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 @@ -891,6 +774,10 @@ services: # 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: @@ -903,12 +790,6 @@ services: "-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: - # pls clearly modify the squid env vars to fit your network environment. - HTTP_PORT: ${SSRF_HTTP_PORT:-3128} - COREDUMP_DIR: ${SSRF_COREDUMP_DIR:-/var/spool/squid} - SSRF_PROXY_ALLOW_PRIVATE_IPS: ${SSRF_PROXY_ALLOW_PRIVATE_IPS:-} - SSRF_PROXY_ALLOW_PRIVATE_DOMAINS: ${SSRF_PROXY_ALLOW_PRIVATE_DOMAINS:-} networks: - ssrf_proxy_network - default @@ -916,6 +797,10 @@ services: # 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 @@ -926,16 +811,16 @@ services: - ./volumes/certbot/conf/live:/etc/letsencrypt/live - ./certbot/update-cert.template.txt:/update-cert.template.txt - ./certbot/docker-entrypoint.sh:/docker-entrypoint.sh - environment: - - CERTBOT_EMAIL=${CERTBOT_EMAIL:-} - - CERTBOT_DOMAIN=${CERTBOT_DOMAIN:-} - - CERTBOT_OPTIONS=${CERTBOT_OPTIONS:-} 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: @@ -955,23 +840,8 @@ services: "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_SERVER_NAME: ${NGINX_SERVER_NAME:-_} - NGINX_HTTPS_ENABLED: ${NGINX_HTTPS_ENABLED:-false} NGINX_SSL_PORT: ${NGINX_SSL_PORT:-443} NGINX_PORT: ${NGINX_PORT:-80} - # You're required to add your own SSL certificates/keys to the `./nginx/ssl` directory - # and modify the env vars below in .env if HTTPS_ENABLED is true. - NGINX_SSL_CERT_FILENAME: ${NGINX_SSL_CERT_FILENAME:-dify.crt} - NGINX_SSL_CERT_KEY_FILENAME: ${NGINX_SSL_CERT_KEY_FILENAME:-dify.key} - NGINX_SSL_PROTOCOLS: ${NGINX_SSL_PROTOCOLS:-TLSv1.2 TLSv1.3} - NGINX_WORKER_PROCESSES: ${NGINX_WORKER_PROCESSES:-auto} - NGINX_CLIENT_MAX_BODY_SIZE: ${NGINX_CLIENT_MAX_BODY_SIZE:-100M} - NGINX_KEEPALIVE_TIMEOUT: ${NGINX_KEEPALIVE_TIMEOUT:-65} - NGINX_PROXY_READ_TIMEOUT: ${NGINX_PROXY_READ_TIMEOUT:-3600s} - NGINX_PROXY_SEND_TIMEOUT: ${NGINX_PROXY_SEND_TIMEOUT:-3600s} - NGINX_ENABLE_CERTBOT_CHALLENGE: ${NGINX_ENABLE_CERTBOT_CHALLENGE:-false} - NGINX_SOCKET_IO_UPSTREAM: ${NGINX_SOCKET_IO_UPSTREAM:-api_websocket:5001} - CERTBOT_DOMAIN: ${CERTBOT_DOMAIN:-} depends_on: - api - web @@ -981,6 +851,10 @@ services: # 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 @@ -988,26 +862,12 @@ services: volumes: # Mount the Weaviate data directory to the con tainer. - ./volumes/weaviate:/var/lib/weaviate - environment: - # The Weaviate configurations - # You can refer to the [Weaviate](https://weaviate.io/developers/weaviate/config-refs/env-vars) documentation for more information. - PERSISTENCE_DATA_PATH: ${WEAVIATE_PERSISTENCE_DATA_PATH:-/var/lib/weaviate} - QUERY_DEFAULTS_LIMIT: ${WEAVIATE_QUERY_DEFAULTS_LIMIT:-25} - AUTHENTICATION_ANONYMOUS_ACCESS_ENABLED: ${WEAVIATE_AUTHENTICATION_ANONYMOUS_ACCESS_ENABLED:-false} - DEFAULT_VECTORIZER_MODULE: ${WEAVIATE_DEFAULT_VECTORIZER_MODULE:-none} - CLUSTER_HOSTNAME: ${WEAVIATE_CLUSTER_HOSTNAME:-node1} - AUTHENTICATION_APIKEY_ENABLED: ${WEAVIATE_AUTHENTICATION_APIKEY_ENABLED:-true} - AUTHENTICATION_APIKEY_ALLOWED_KEYS: ${WEAVIATE_AUTHENTICATION_APIKEY_ALLOWED_KEYS:-WVF5YThaHlkYwhGUSmCRgsX3tD5ngdN8pkih} - AUTHENTICATION_APIKEY_USERS: ${WEAVIATE_AUTHENTICATION_APIKEY_USERS:-hello@dify.ai} - AUTHORIZATION_ADMINLIST_ENABLED: ${WEAVIATE_AUTHORIZATION_ADMINLIST_ENABLED:-true} - AUTHORIZATION_ADMINLIST_USERS: ${WEAVIATE_AUTHORIZATION_ADMINLIST_USERS:-hello@dify.ai} - DISABLE_TELEMETRY: ${WEAVIATE_DISABLE_TELEMETRY:-false} - ENABLE_TOKENIZER_GSE: ${WEAVIATE_ENABLE_TOKENIZER_GSE:-false} - ENABLE_TOKENIZER_KAGOME_JA: ${WEAVIATE_ENABLE_TOKENIZER_KAGOME_JA:-false} - ENABLE_TOKENIZER_KAGOME_KR: ${WEAVIATE_ENABLE_TOKENIZER_KAGOME_KR:-false} - # 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: @@ -1018,10 +878,6 @@ services: - ./volumes/oceanbase/conf:/root/.obd/cluster - ./volumes/oceanbase/init.d:/root/boot/init.d environment: - OB_MEMORY_LIMIT: ${OCEANBASE_MEMORY_LIMIT:-6G} - OB_SYS_PASSWORD: ${OCEANBASE_VECTOR_PASSWORD:-difyai123456} - OB_TENANT_PASSWORD: ${OCEANBASE_VECTOR_PASSWORD:-difyai123456} - OB_CLUSTER_NAME: ${OCEANBASE_CLUSTER_NAME:-difyai} OB_SERVER_IP: 127.0.0.1 MODE: mini LANG: C.UTF-8 @@ -1032,7 +888,7 @@ services: test: [ "CMD-SHELL", - 'obclient -h127.0.0.1 -P2881 -uroot@test -p${OCEANBASE_VECTOR_PASSWORD:-difyai123456} -e "SELECT 1;"', + 'obclient -h127.0.0.1 -P2881 -uroot@test -p"$$OB_TENANT_PASSWORD" -e "SELECT 1;"', ] interval: 10s retries: 30 @@ -1041,6 +897,10 @@ services: # 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: @@ -1049,8 +909,6 @@ services: volumes: - ./volumes/seekdb:/var/lib/oceanbase environment: - ROOT_PASSWORD: ${OCEANBASE_VECTOR_PASSWORD:-difyai123456} - MEMORY_LIMIT: ${SEEKDB_MEMORY_LIMIT:-2G} REPORTER: dify-ai-seekdb ports: - "${OCEANBASE_VECTOR_PORT:-2881}:2881" @@ -1058,7 +916,7 @@ services: test: [ "CMD-SHELL", - 'mysql -h127.0.0.1 -P2881 -uroot -p${OCEANBASE_VECTOR_PASSWORD:-difyai123456} -e "SELECT 1;"', + 'mysql -h127.0.0.1 -P2881 -uroot -p"$$ROOT_PASSWORD" -e "SELECT 1;"', ] interval: 5s retries: 60 @@ -1067,26 +925,29 @@ services: # 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 - environment: - QDRANT_API_KEY: ${QDRANT_API_KEY:-difyai123456} # 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_ADMINISTRATOR_USERNAME=${COUCHBASE_USER:-Administrator} - - COUCHBASE_ADMINISTRATOR_PASSWORD=${COUCHBASE_PASSWORD:-password} - - COUCHBASE_BUCKET=${COUCHBASE_BUCKET_NAME:-Embeddings} - COUCHBASE_BUCKET_RAMSIZE=512 - COUCHBASE_RAM_SIZE=2048 - COUCHBASE_EVENTING_RAM_SIZE=512 @@ -1106,7 +967,7 @@ services: test: [ "CMD-SHELL", - "curl -s -f -u Administrator:password http://localhost:8091/pools/default/buckets | grep -q '\\[{' || exit 1", + "curl -s -f -u \"$$COUCHBASE_ADMINISTRATOR_USERNAME:$$COUCHBASE_ADMINISTRATOR_PASSWORD\" http://localhost:8091/pools/default/buckets | grep -q '\\[{' || exit 1", ] interval: 10s retries: 10 @@ -1115,21 +976,14 @@ services: # 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 - environment: - PGUSER: ${PGVECTOR_PGUSER:-postgres} - # The password for the default postgres user. - POSTGRES_PASSWORD: ${PGVECTOR_POSTGRES_PASSWORD:-difyai123456} - # The name of the default postgres database. - POSTGRES_DB: ${PGVECTOR_POSTGRES_DB:-dify} - # postgres data directory - PGDATA: ${PGVECTOR_PGDATA:-/var/lib/postgresql/data/pgdata} - # pg_bigm module for full text search - PG_BIGM: ${PGVECTOR_PG_BIGM:-false} - PG_BIGM_VERSION: ${PGVECTOR_PG_BIGM_VERSION:-1.2-20240606} volumes: - ./volumes/pgvector/data:/var/lib/postgresql/data - ./pgvector/docker-entrypoint.sh:/docker-entrypoint.sh @@ -1166,18 +1020,14 @@ services: # 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 - environment: - PGUSER: ${PGVECTOR_PGUSER:-postgres} - # The password for the default postgres user. - POSTGRES_PASSWORD: ${PGVECTOR_POSTGRES_PASSWORD:-difyai123456} - # The name of the default postgres database. - POSTGRES_DB: ${PGVECTOR_POSTGRES_DB:-dify} - # postgres data directory - PGDATA: ${PGVECTOR_PGDATA:-/var/lib/postgresql/data/pgdata} volumes: - ./volumes/pgvecto_rs/data:/var/lib/postgresql/data healthcheck: @@ -1188,19 +1038,23 @@ services: # 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 - environment: - CHROMA_SERVER_AUTHN_CREDENTIALS: ${CHROMA_SERVER_AUTHN_CREDENTIALS:-difyai123456} - CHROMA_SERVER_AUTHN_PROVIDER: ${CHROMA_SERVER_AUTHN_PROVIDER:-chromadb.auth.token_authn.TokenAuthenticationServerProvider} - IS_PERSISTENT: ${CHROMA_IS_PERSISTENT:-TRUE} # 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 @@ -1217,11 +1071,14 @@ services: entrypoint: ["/custom-entrypoint.sh"] tty: true environment: - TZ: ${IRIS_TIMEZONE:-UTC} 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 @@ -1231,21 +1088,19 @@ services: type: volume target: /opt/oracle/oradata - ./startupscripts:/opt/oracle/scripts/startup - environment: - ORACLE_PWD: ${ORACLE_PWD:-Dify123456} - ORACLE_CHARACTERSET: ${ORACLE_CHARACTERSET:-AL32UTF8} # 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 - environment: - ETCD_AUTO_COMPACTION_MODE: ${ETCD_AUTO_COMPACTION_MODE:-revision} - ETCD_AUTO_COMPACTION_RETENTION: ${ETCD_AUTO_COMPACTION_RETENTION:-1000} - ETCD_QUOTA_BACKEND_BYTES: ${ETCD_QUOTA_BACKEND_BYTES:-4294967296} - ETCD_SNAPSHOT_COUNT: ${ETCD_SNAPSHOT_COUNT:-50000} 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 @@ -1258,13 +1113,16 @@ services: - 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 - environment: - MINIO_ACCESS_KEY: ${MINIO_ACCESS_KEY:-minioadmin} - MINIO_SECRET_KEY: ${MINIO_SECRET_KEY:-minioadmin} volumes: - ./volumes/milvus/minio:/minio_data command: minio server /minio_data --console-address ":9001" @@ -1277,15 +1135,17 @@ services: - 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"] - environment: - ETCD_ENDPOINTS: ${ETCD_ENDPOINTS:-etcd:2379} - MINIO_ADDRESS: ${MINIO_ADDRESS:-minio:9000} - common.security.authorizationEnabled: ${MILVUS_AUTHORIZATION_ENABLED:-true} volumes: - ./volumes/milvus/milvus:/var/lib/milvus healthcheck: @@ -1305,15 +1165,14 @@ services: # 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 - environment: - discovery.type: ${OPENSEARCH_DISCOVERY_TYPE:-single-node} - bootstrap.memory_lock: ${OPENSEARCH_BOOTSTRAP_MEMORY_LOCK:-true} - OPENSEARCH_JAVA_OPTS: -Xms${OPENSEARCH_JAVA_OPTS_MIN:-512m} -Xmx${OPENSEARCH_JAVA_OPTS_MAX:-1024m} - OPENSEARCH_INITIAL_ADMIN_PASSWORD: ${OPENSEARCH_INITIAL_ADMIN_PASSWORD:-Qazwsxedc!@#123} ulimits: memlock: soft: ${OPENSEARCH_MEMLOCK_SOFT:--1} @@ -1342,16 +1201,17 @@ services: # 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_USERNAME: ${OPENGAUSS_USER:-postgres} - GS_PASSWORD: ${OPENGAUSS_PASSWORD:-Dify@123} GS_PORT: ${OPENGAUSS_PORT:-6600} - GS_DB: ${OPENGAUSS_DATABASE:-dify} volumes: - ./volumes/opengauss/data:/var/lib/opengauss/data healthcheck: @@ -1392,18 +1252,17 @@ services: # 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 - volumes: - - ./elasticsearch/docker-entrypoint.sh:/docker-entrypoint-mount.sh - - dify_es01_data:/usr/share/elasticsearch/data environment: - ELASTIC_PASSWORD: ${ELASTICSEARCH_PASSWORD:-elastic} - VECTOR_STORE: ${VECTOR_STORE:-} cluster.name: dify-es-cluster node.name: dify-es0 discovery.type: single-node @@ -1411,6 +1270,11 @@ services: 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: diff --git a/docker/docker-compose.e2b.yaml b/docker/docker-compose.e2b.yaml index 2bfb5d566b5..4b32b5ab64b 100644 --- a/docker/docker-compose.e2b.yaml +++ b/docker/docker-compose.e2b.yaml @@ -26,8 +26,6 @@ services: dockerfile: dify-agent/Dockerfile environment: DIFY_AGENT_RUNTIME_BACKEND: e2b - DIFY_AGENT_E2B_API_KEY: ${DIFY_AGENT_E2B_API_KEY:-${E2B_API_KEY:-${E2B_API_TOKEN:-}}} - DIFY_AGENT_E2B_TEMPLATE: ${DIFY_AGENT_E2B_TEMPLATE:-difys-default-team/dify-agent-local-sandbox} ports: - "${EXPOSE_AGENT_BACKEND_PORT:-15050}:5050" diff --git a/docker/docker-compose.yaml b/docker/docker-compose.yaml index c4a45549a4c..347d6fd4017 100644 --- a/docker/docker-compose.yaml +++ b/docker/docker-compose.yaml @@ -5,13 +5,10 @@ # ================================================================== # Shared configuration using YAML anchors and env_file -x-shared-api-worker-config: &shared-api-worker-config +x-shared-api-config: &shared-api-config env_file: - - ./envs/core-services/shared.defaults - path: ./envs/core-services/shared.env required: false - - path: ./envs/core-services/api.env - required: false - path: ./envs/security.env required: false - path: ./envs/databases/db-postgres.env @@ -66,7 +63,11 @@ x-shared-api-worker-config: &shared-api-worker-config required: false - path: ./envs/infrastructure/milvus-standalone.env required: false + - path: ./envs/core-services/api.env + required: false - ./.env + - ./envs/core-services/shared.defaults + - ./envs/core-services/api.defaults networks: - ssrf_proxy_network - default @@ -74,11 +75,8 @@ x-shared-api-worker-config: &shared-api-worker-config x-shared-worker-config: &shared-worker-config env_file: - - ./envs/core-services/shared.defaults - path: ./envs/core-services/shared.env required: false - - path: ./envs/core-services/worker.env - required: false - path: ./envs/security.env required: false - path: ./envs/databases/db-postgres.env @@ -133,7 +131,11 @@ x-shared-worker-config: &shared-worker-config required: false - path: ./envs/infrastructure/milvus-standalone.env required: false + - path: ./envs/core-services/worker.env + required: false - ./.env + - ./envs/core-services/shared.defaults + - ./envs/core-services/worker.defaults networks: - ssrf_proxy_network - default @@ -149,11 +151,8 @@ x-knowledge-fs-celery-worker: &knowledge-fs-celery-worker # Match the two-hour default task deadline plus shutdown margin for long document jobs. stop_grace_period: 125m env_file: - - ./envs/core-services/shared.defaults - path: ./envs/core-services/shared.env required: false - - path: ./envs/core-services/worker.env - required: false - path: ./envs/security.env required: false - path: ./envs/databases/db-postgres.env @@ -166,7 +165,11 @@ x-knowledge-fs-celery-worker: &knowledge-fs-celery-worker required: false - path: ./envs/core-services/knowledge-fs-worker.env required: false + - path: ./envs/core-services/worker.env + required: false - ./.env + - ./envs/core-services/shared.defaults + - ./envs/core-services/worker.defaults environment: &knowledge-fs-celery-environment MODE: worker MIGRATION_ENABLED: "false" @@ -187,11 +190,8 @@ x-knowledge-fs-celery-worker: &knowledge-fs-celery-worker x-shared-worker-beat-config: &shared-worker-beat-config env_file: - - ./envs/core-services/shared.defaults - path: ./envs/core-services/shared.env required: false - - path: ./envs/core-services/worker-beat.env - required: false - path: ./envs/security.env required: false - path: ./envs/databases/db-postgres.env @@ -246,7 +246,11 @@ x-shared-worker-beat-config: &shared-worker-beat-config required: false - path: ./envs/infrastructure/milvus-standalone.env required: false + - path: ./envs/core-services/worker-beat.env + required: false - ./.env + - ./envs/core-services/shared.defaults + - ./envs/core-services/worker-beat.defaults networks: - ssrf_proxy_network - default @@ -274,22 +278,10 @@ services: # API service api: - <<: *shared-api-worker-config + <<: *shared-api-config image: langgenius/dify-api:1.18.0 environment: MODE: api - # The bundled Nginx supplies a verified client IP; keep direct API callers trusted. - RESPECT_XFORWARD_HEADERS_ENABLED: ${RESPECT_XFORWARD_HEADERS_ENABLED:-true} - SENTRY_DSN: ${API_SENTRY_DSN:-} - SENTRY_TRACES_SAMPLE_RATE: ${API_SENTRY_TRACES_SAMPLE_RATE:-1.0} - SENTRY_PROFILES_SAMPLE_RATE: ${API_SENTRY_PROFILES_SAMPLE_RATE:-1.0} - PLUGIN_REMOTE_INSTALL_HOST: ${EXPOSE_PLUGIN_DEBUGGING_HOST:-localhost} - PLUGIN_REMOTE_INSTALL_PORT: ${EXPOSE_PLUGIN_DEBUGGING_PORT:-5003} - PLUGIN_MAX_PACKAGE_SIZE: ${PLUGIN_MAX_PACKAGE_SIZE:-52428800} - PLUGIN_DAEMON_TIMEOUT: ${PLUGIN_DAEMON_TIMEOUT:-600.0} - INNER_API_KEY_FOR_PLUGIN: ${PLUGIN_DIFY_INNER_API_KEY:-QaHbTe77CtuXmsfyhR7+vRjI/+XbV1AaFy691iy+kGDv2Jvy0/eAh8Y1} - AGENT_BACKEND_BASE_URL: ${AGENT_BACKEND_BASE_URL:-http://agent_backend:5050} - AGENT_BACKEND_API_TOKEN: ${DIFY_AGENT_API_TOKEN:-dify-agent-run-token-for-dev-only} depends_on: init_permissions: condition: service_completed_successfully @@ -324,17 +316,22 @@ services: # WebSocket service for workflow collaboration. api_websocket: - <<: *shared-api-worker-config + <<: *shared-api-config image: langgenius/dify-api:1.18.0 profiles: - collaboration environment: MODE: api - RESPECT_XFORWARD_HEADERS_ENABLED: ${RESPECT_XFORWARD_HEADERS_ENABLED:-true} - SERVER_WORKER_AMOUNT: ${API_WEBSOCKET_WORKER_AMOUNT:-1} - SERVER_WORKER_CLASS: ${API_WEBSOCKET_WORKER_CLASS:-geventwebsocket.gunicorn.workers.GeventWebSocketWorker} - SERVER_WORKER_CONNECTIONS: ${API_WEBSOCKET_WORKER_CONNECTIONS:-1000} - GUNICORN_TIMEOUT: ${API_WEBSOCKET_GUNICORN_TIMEOUT:-360} + # 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 @@ -353,20 +350,12 @@ services: - ssrf_proxy_network - default - # worker service # The Celery worker for processing all queues (dataset, workflow, mail, etc.) worker: <<: *shared-worker-config image: langgenius/dify-api:1.18.0 environment: MODE: worker - SENTRY_DSN: ${API_SENTRY_DSN:-} - SENTRY_TRACES_SAMPLE_RATE: ${API_SENTRY_TRACES_SAMPLE_RATE:-1.0} - SENTRY_PROFILES_SAMPLE_RATE: ${API_SENTRY_PROFILES_SAMPLE_RATE:-1.0} - PLUGIN_MAX_PACKAGE_SIZE: ${PLUGIN_MAX_PACKAGE_SIZE:-52428800} - INNER_API_KEY_FOR_PLUGIN: ${PLUGIN_DIFY_INNER_API_KEY:-QaHbTe77CtuXmsfyhR7+vRjI/+XbV1AaFy691iy+kGDv2Jvy0/eAh8Y1} - AGENT_BACKEND_BASE_URL: ${AGENT_BACKEND_BASE_URL:-http://agent_backend:5050} - AGENT_BACKEND_API_TOKEN: ${DIFY_AGENT_API_TOKEN:-dify-agent-run-token-for-dev-only} depends_on: init_permissions: condition: service_completed_successfully @@ -413,9 +402,6 @@ services: CELERY_WORKER_POOL: prefork CELERY_WORKER_CONCURRENCY: ${KNOWLEDGE_FS_UPGRADE_WORKER_AMOUNT:-2} CELERY_PREFETCH_MULTIPLIER: 1 - SENTRY_DSN: ${API_SENTRY_DSN:-} - SENTRY_TRACES_SAMPLE_RATE: ${API_SENTRY_TRACES_SAMPLE_RATE:-1.0} - SENTRY_PROFILES_SAMPLE_RATE: ${API_SENTRY_PROFILES_SAMPLE_RATE:-1.0} depends_on: init_permissions: condition: service_completed_successfully @@ -473,123 +459,91 @@ services: image: langgenius/dify-web:1.18.0 restart: always env_file: - - ./envs/core-services/shared.defaults - path: ./envs/core-services/web.env required: false - path: ./envs/security.env required: false - ./.env - environment: - CONSOLE_API_URL: ${CONSOLE_API_URL:-} - SERVER_CONSOLE_API_URL: ${SERVER_CONSOLE_API_URL:-http://api:5001} - APP_API_URL: ${APP_API_URL:-} - AMPLITUDE_API_KEY: ${AMPLITUDE_API_KEY:-} - TURNSTILE_SITE_KEY: ${TURNSTILE_SITE_KEY:-} - NEXT_PUBLIC_COOKIE_DOMAIN: ${NEXT_PUBLIC_COOKIE_DOMAIN:-} - NEXT_PUBLIC_SOCKET_URL: ${NEXT_PUBLIC_SOCKET_URL:-ws://localhost} - SENTRY_DSN: ${WEB_SENTRY_DSN:-} - NEXT_TELEMETRY_DISABLED: ${NEXT_TELEMETRY_DISABLED:-0} - EXPERIMENTAL_ENABLE_VINEXT: ${EXPERIMENTAL_ENABLE_VINEXT:-false} - TEXT_GENERATION_TIMEOUT_MS: ${TEXT_GENERATION_TIMEOUT_MS:-60000} - CSP_WHITELIST: ${CSP_WHITELIST:-} - ALLOW_EMBED: ${ALLOW_EMBED:-false} - ALLOW_UNSAFE_DATA_SCHEME: ${ALLOW_UNSAFE_DATA_SCHEME:-false} - MARKETPLACE_API_URL: ${MARKETPLACE_API_URL:-https://marketplace.dify.ai} - MARKETPLACE_URL: ${MARKETPLACE_URL:-https://marketplace.dify.ai} - TOP_K_MAX_VALUE: ${TOP_K_MAX_VALUE:-10} - INDEXING_MAX_SEGMENTATION_TOKENS_LENGTH: ${INDEXING_MAX_SEGMENTATION_TOKENS_LENGTH:-4000} - LOOP_NODE_MAX_COUNT: ${LOOP_NODE_MAX_COUNT:-100} - MAX_TOOLS_NUM: ${MAX_TOOLS_NUM:-10} - MAX_PARALLEL_LIMIT: ${MAX_PARALLEL_LIMIT:-10} - MAX_ITERATIONS_NUM: ${MAX_ITERATIONS_NUM:-99} - MAX_TREE_DEPTH: ${MAX_TREE_DEPTH:-50} - ENABLE_WEBSITE_JINAREADER: ${ENABLE_WEBSITE_JINAREADER:-true} - ENABLE_WEBSITE_FIRECRAWL: ${ENABLE_WEBSITE_FIRECRAWL:-true} - ENABLE_WEBSITE_WATERCRAWL: ${ENABLE_WEBSITE_WATERCRAWL:-true} + - ./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 - environment: - POSTGRES_USER: ${DB_USERNAME:-postgres} - POSTGRES_PASSWORD: ${DB_PASSWORD:-difyai123456} - POSTGRES_DB: ${DB_DATABASE:-dify} - PGDATA: ${PGDATA:-/var/lib/postgresql/data/pgdata} - command: > - postgres -c 'max_connections=${POSTGRES_MAX_CONNECTIONS:-200}' - -c 'shared_buffers=${POSTGRES_SHARED_BUFFERS:-128MB}' - -c 'work_mem=${POSTGRES_WORK_MEM:-4MB}' - -c 'maintenance_work_mem=${POSTGRES_MAINTENANCE_WORK_MEM:-64MB}' - -c 'effective_cache_size=${POSTGRES_EFFECTIVE_CACHE_SIZE:-4096MB}' - -c 'statement_timeout=${POSTGRES_STATEMENT_TIMEOUT:-0}' - -c 'idle_in_transaction_session_timeout=${POSTGRES_IDLE_IN_TRANSACTION_SESSION_TIMEOUT:-0}' + # 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", - "pg_isready", - "-h", - "db_postgres", - "-U", - "${DB_USERNAME:-postgres}", - "-d", - "${DB_DATABASE:-dify}", - ] + 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 - environment: - MYSQL_ROOT_PASSWORD: ${DB_PASSWORD:-difyai123456} - MYSQL_DATABASE: ${DB_DATABASE:-dify} - command: > - --max_connections=${MYSQL_MAX_CONNECTIONS:-1000} - --innodb_buffer_pool_size=${MYSQL_INNODB_BUFFER_POOL_SIZE:-512M} - --innodb_log_file_size=${MYSQL_INNODB_LOG_FILE_SIZE:-128M} - --innodb_flush_log_at_trx_commit=${MYSQL_INNODB_FLUSH_LOG_AT_TRX_COMMIT:-2} + 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", - "mysqladmin", - "ping", - "-u", - "root", - "-p${DB_PASSWORD:-difyai123456}", - ] + 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 - environment: - REDISCLI_AUTH: ${REDIS_PASSWORD:-difyai123456} volumes: # Mount the redis data directory to the container. - ./volumes/redis/data:/data # Set the redis password when startup redis server. - command: redis-server --requirepass ${REDIS_PASSWORD:-difyai123456} + command: ["sh", "-c", 'exec docker-entrypoint.sh redis-server --requirepass "$$REDIS_PASSWORD"'] healthcheck: test: [ "CMD-SHELL", - "redis-cli -a ${REDIS_PASSWORD:-difyai123456} ping | grep -q PONG", + "redis-cli ping | grep -q PONG", ] # The DifySandbox @@ -602,23 +556,12 @@ services: - path: ./envs/security.env required: false - ./.env - environment: - # The DifySandbox configurations - # Make sure you are changing this key for your deployment with a strong key. - # You can generate a strong key using `openssl rand -base64 42`. - API_KEY: ${SANDBOX_API_KEY:-dify-sandbox} - GIN_MODE: ${SANDBOX_GIN_MODE:-release} - WORKER_TIMEOUT: ${SANDBOX_WORKER_TIMEOUT:-15} - ENABLE_NETWORK: ${SANDBOX_ENABLE_NETWORK:-true} - HTTP_PROXY: ${SANDBOX_HTTP_PROXY:-http://ssrf_proxy:3128} - HTTPS_PROXY: ${SANDBOX_HTTPS_PROXY:-http://ssrf_proxy:3128} - SANDBOX_PORT: ${SANDBOX_PORT:-8194} - PIP_MIRROR_URL: ${PIP_MIRROR_URL:-} + - ./envs/core-services/sandbox.defaults volumes: - ./volumes/sandbox/dependencies:/dependencies - ./volumes/sandbox/conf:/conf healthcheck: - test: ["CMD", "curl", "-f", "http://localhost:8194/health"] + test: ["CMD-SHELL", 'curl -f "http://localhost:$$SANDBOX_PORT/health"'] networks: - ssrf_proxy_network @@ -674,57 +617,13 @@ services: - path: ./envs/databases/redis.env required: false - ./.env + - ./envs/core-services/shared.defaults + - ./envs/core-services/plugin-daemon.defaults networks: - ssrf_proxy_network - default environment: - DB_DATABASE: ${DB_PLUGIN_DATABASE:-dify_plugin} - DB_SSL_MODE: ${DB_SSL_MODE:-disable} - SERVER_PORT: ${PLUGIN_DAEMON_PORT:-5002} - SERVER_KEY: ${PLUGIN_DAEMON_KEY:-lYkiYYT6owG+71oLerGzA7GXCgOT++6ovaezWAjpCjf+Sjc3ZtU+qUEi} - MAX_PLUGIN_PACKAGE_SIZE: ${PLUGIN_MAX_PACKAGE_SIZE:-52428800} - PPROF_ENABLED: ${PLUGIN_PPROF_ENABLED:-false} - 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} - PLUGIN_REMOTE_INSTALLING_HOST: ${PLUGIN_DEBUGGING_HOST:-0.0.0.0} PLUGIN_REMOTE_INSTALLING_PORT: ${PLUGIN_DEBUGGING_PORT:-5003} - PLUGIN_WORKING_PATH: ${PLUGIN_WORKING_PATH:-/app/storage/cwd} - FORCE_VERIFYING_SIGNATURE: ${FORCE_VERIFYING_SIGNATURE:-true} - PYTHON_ENV_INIT_TIMEOUT: ${PLUGIN_PYTHON_ENV_INIT_TIMEOUT:-120} - PLUGIN_MAX_EXECUTION_TIMEOUT: ${PLUGIN_MAX_EXECUTION_TIMEOUT:-600} - PLUGIN_STDIO_BUFFER_SIZE: ${PLUGIN_STDIO_BUFFER_SIZE:-1024} - PLUGIN_STDIO_MAX_BUFFER_SIZE: ${PLUGIN_STDIO_MAX_BUFFER_SIZE:-5242880} - PIP_MIRROR_URL: ${PIP_MIRROR_URL:-} - PLUGIN_STORAGE_TYPE: ${PLUGIN_STORAGE_TYPE:-local} - PLUGIN_STORAGE_LOCAL_ROOT: ${PLUGIN_STORAGE_LOCAL_ROOT:-/app/storage} - PLUGIN_INSTALLED_PATH: ${PLUGIN_INSTALLED_PATH:-plugin} - PLUGIN_PACKAGE_CACHE_PATH: ${PLUGIN_PACKAGE_CACHE_PATH:-plugin_packages} - PLUGIN_MEDIA_CACHE_PATH: ${PLUGIN_MEDIA_CACHE_PATH:-assets} - PLUGIN_STORAGE_OSS_BUCKET: ${PLUGIN_STORAGE_OSS_BUCKET:-} - S3_USE_AWS_MANAGED_IAM: ${PLUGIN_S3_USE_AWS_MANAGED_IAM:-false} - S3_USE_AWS: ${PLUGIN_S3_USE_AWS:-false} - S3_ENDPOINT: ${PLUGIN_S3_ENDPOINT:-} - S3_USE_PATH_STYLE: ${PLUGIN_S3_USE_PATH_STYLE:-false} - AWS_ACCESS_KEY: ${PLUGIN_AWS_ACCESS_KEY:-} - AWS_SECRET_KEY: ${PLUGIN_AWS_SECRET_KEY:-} - AWS_REGION: ${PLUGIN_AWS_REGION:-} - AZURE_BLOB_STORAGE_CONNECTION_STRING: ${PLUGIN_AZURE_BLOB_STORAGE_CONNECTION_STRING:-} - AZURE_BLOB_STORAGE_CONTAINER_NAME: ${PLUGIN_AZURE_BLOB_STORAGE_CONTAINER_NAME:-} - TENCENT_COS_SECRET_KEY: ${PLUGIN_TENCENT_COS_SECRET_KEY:-} - TENCENT_COS_SECRET_ID: ${PLUGIN_TENCENT_COS_SECRET_ID:-} - TENCENT_COS_REGION: ${PLUGIN_TENCENT_COS_REGION:-} - ALIYUN_OSS_REGION: ${PLUGIN_ALIYUN_OSS_REGION:-} - ALIYUN_OSS_ENDPOINT: ${PLUGIN_ALIYUN_OSS_ENDPOINT:-} - ALIYUN_OSS_ACCESS_KEY_ID: ${PLUGIN_ALIYUN_OSS_ACCESS_KEY_ID:-} - ALIYUN_OSS_ACCESS_KEY_SECRET: ${PLUGIN_ALIYUN_OSS_ACCESS_KEY_SECRET:-} - ALIYUN_OSS_AUTH_VERSION: ${PLUGIN_ALIYUN_OSS_AUTH_VERSION:-v4} - ALIYUN_OSS_PATH: ${PLUGIN_ALIYUN_OSS_PATH:-} - VOLCENGINE_TOS_ENDPOINT: ${PLUGIN_VOLCENGINE_TOS_ENDPOINT:-} - VOLCENGINE_TOS_ACCESS_KEY: ${PLUGIN_VOLCENGINE_TOS_ACCESS_KEY:-} - VOLCENGINE_TOS_SECRET_KEY: ${PLUGIN_VOLCENGINE_TOS_SECRET_KEY:-} - VOLCENGINE_TOS_REGION: ${PLUGIN_VOLCENGINE_TOS_REGION:-} - SENTRY_ENABLED: ${PLUGIN_SENTRY_ENABLED:-false} - SENTRY_DSN: ${PLUGIN_SENTRY_DSN:-} ports: - "${EXPOSE_PLUGIN_DEBUGGING_PORT:-5003}:${PLUGIN_DEBUGGING_PORT:-5003}" volumes: @@ -833,29 +732,14 @@ services: image: langgenius/dify-agent-backend:1.18.0 restart: always env_file: - - path: ./envs/core-services/dify-agent.env - required: false - path: ./envs/security.env required: false - path: ./envs/databases/redis.env required: false + - path: ./envs/core-services/dify-agent.env + required: false - ./.env - environment: - DIFY_AGENT_REDIS_URL: ${DIFY_AGENT_REDIS_URL:-redis://:${REDIS_PASSWORD:-difyai123456}@redis:6379/2} - DIFY_AGENT_PLUGIN_DAEMON_URL: ${DIFY_AGENT_PLUGIN_DAEMON_URL:-${PLUGIN_DAEMON_URL:-http://plugin_daemon:5002}} - DIFY_AGENT_PLUGIN_DAEMON_API_KEY: ${DIFY_AGENT_PLUGIN_DAEMON_API_KEY:-${PLUGIN_DAEMON_KEY:-lYkiYYT6owG+71oLerGzA7GXCgOT++6ovaezWAjpCjf+Sjc3ZtU+qUEi}} - DIFY_AGENT_INNER_API_URL: ${DIFY_AGENT_INNER_API_URL:-${PLUGIN_DIFY_INNER_API_URL:-http://api:5001}} - DIFY_AGENT_INNER_API_KEY: ${DIFY_AGENT_INNER_API_KEY:-${PLUGIN_DIFY_INNER_API_KEY:-QaHbTe77CtuXmsfyhR7+vRjI/+XbV1AaFy691iy+kGDv2Jvy0/eAh8Y1}} - DIFY_AGENT_RUNTIME_BACKEND: ${DIFY_AGENT_RUNTIME_BACKEND:-local} - DIFY_AGENT_LOCAL_SANDBOX_ENDPOINT: ${DIFY_AGENT_LOCAL_SANDBOX_ENDPOINT:-${DIFY_AGENT_SHELLCTL_ENTRYPOINT:-http://local_sandbox:5004}} - DIFY_AGENT_LOCAL_SANDBOX_AUTH_TOKEN: ${DIFY_AGENT_LOCAL_SANDBOX_AUTH_TOKEN:-${DIFY_AGENT_SHELLCTL_AUTH_TOKEN:-}} - DIFY_AGENT_STUB_API_BASE_URL: ${DIFY_AGENT_STUB_API_BASE_URL:-http://agent_backend:5050/agent-stub} - DIFY_AGENT_SANDBOX_FILES_BASE_URL: ${DIFY_AGENT_SANDBOX_FILES_BASE_URL:-http://api:5001} - # This is security-sensitive: it derives the JWE encryption key for Agent Stub bearer tokens. - # Replace this development default in production. - # Generate one with: python -c 'import secrets; print(secrets.token_urlsafe(32))' - DIFY_AGENT_SERVER_SECRET_KEY: ${DIFY_AGENT_SERVER_SECRET_KEY:-MDEyMzQ1Njc4OWFiY2RlZjAxMjM0NTY3ODlhYmNkZWY} - DIFY_AGENT_API_TOKEN: ${DIFY_AGENT_API_TOKEN:-dify-agent-run-token-for-dev-only} + - ./envs/core-services/dify-agent.defaults depends_on: redis: condition: service_started @@ -881,11 +765,10 @@ services: "-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: - HTTP_PORT: ${SSRF_HTTP_PORT:-3128} - COREDUMP_DIR: ${SSRF_COREDUMP_DIR:-/var/spool/squid} - SSRF_PROXY_ALLOW_PRIVATE_IPS: ${SSRF_PROXY_ALLOW_PRIVATE_IPS:-} - SSRF_PROXY_ALLOW_PRIVATE_DOMAINS: ${SSRF_PROXY_ALLOW_PRIVATE_DOMAINS:-} + 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 @@ -897,6 +780,10 @@ services: # 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: @@ -909,12 +796,6 @@ services: "-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: - # pls clearly modify the squid env vars to fit your network environment. - HTTP_PORT: ${SSRF_HTTP_PORT:-3128} - COREDUMP_DIR: ${SSRF_COREDUMP_DIR:-/var/spool/squid} - SSRF_PROXY_ALLOW_PRIVATE_IPS: ${SSRF_PROXY_ALLOW_PRIVATE_IPS:-} - SSRF_PROXY_ALLOW_PRIVATE_DOMAINS: ${SSRF_PROXY_ALLOW_PRIVATE_DOMAINS:-} networks: - ssrf_proxy_network - default @@ -922,6 +803,10 @@ services: # 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 @@ -932,16 +817,16 @@ services: - ./volumes/certbot/conf/live:/etc/letsencrypt/live - ./certbot/update-cert.template.txt:/update-cert.template.txt - ./certbot/docker-entrypoint.sh:/docker-entrypoint.sh - environment: - - CERTBOT_EMAIL=${CERTBOT_EMAIL:-} - - CERTBOT_DOMAIN=${CERTBOT_DOMAIN:-} - - CERTBOT_OPTIONS=${CERTBOT_OPTIONS:-} 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: @@ -961,23 +846,8 @@ services: "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_SERVER_NAME: ${NGINX_SERVER_NAME:-_} - NGINX_HTTPS_ENABLED: ${NGINX_HTTPS_ENABLED:-false} NGINX_SSL_PORT: ${NGINX_SSL_PORT:-443} NGINX_PORT: ${NGINX_PORT:-80} - # You're required to add your own SSL certificates/keys to the `./nginx/ssl` directory - # and modify the env vars below in .env if HTTPS_ENABLED is true. - NGINX_SSL_CERT_FILENAME: ${NGINX_SSL_CERT_FILENAME:-dify.crt} - NGINX_SSL_CERT_KEY_FILENAME: ${NGINX_SSL_CERT_KEY_FILENAME:-dify.key} - NGINX_SSL_PROTOCOLS: ${NGINX_SSL_PROTOCOLS:-TLSv1.2 TLSv1.3} - NGINX_WORKER_PROCESSES: ${NGINX_WORKER_PROCESSES:-auto} - NGINX_CLIENT_MAX_BODY_SIZE: ${NGINX_CLIENT_MAX_BODY_SIZE:-100M} - NGINX_KEEPALIVE_TIMEOUT: ${NGINX_KEEPALIVE_TIMEOUT:-65} - NGINX_PROXY_READ_TIMEOUT: ${NGINX_PROXY_READ_TIMEOUT:-3600s} - NGINX_PROXY_SEND_TIMEOUT: ${NGINX_PROXY_SEND_TIMEOUT:-3600s} - NGINX_ENABLE_CERTBOT_CHALLENGE: ${NGINX_ENABLE_CERTBOT_CHALLENGE:-false} - NGINX_SOCKET_IO_UPSTREAM: ${NGINX_SOCKET_IO_UPSTREAM:-api_websocket:5001} - CERTBOT_DOMAIN: ${CERTBOT_DOMAIN:-} depends_on: - api - web @@ -987,6 +857,10 @@ services: # 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 @@ -994,26 +868,12 @@ services: volumes: # Mount the Weaviate data directory to the con tainer. - ./volumes/weaviate:/var/lib/weaviate - environment: - # The Weaviate configurations - # You can refer to the [Weaviate](https://weaviate.io/developers/weaviate/config-refs/env-vars) documentation for more information. - PERSISTENCE_DATA_PATH: ${WEAVIATE_PERSISTENCE_DATA_PATH:-/var/lib/weaviate} - QUERY_DEFAULTS_LIMIT: ${WEAVIATE_QUERY_DEFAULTS_LIMIT:-25} - AUTHENTICATION_ANONYMOUS_ACCESS_ENABLED: ${WEAVIATE_AUTHENTICATION_ANONYMOUS_ACCESS_ENABLED:-false} - DEFAULT_VECTORIZER_MODULE: ${WEAVIATE_DEFAULT_VECTORIZER_MODULE:-none} - CLUSTER_HOSTNAME: ${WEAVIATE_CLUSTER_HOSTNAME:-node1} - AUTHENTICATION_APIKEY_ENABLED: ${WEAVIATE_AUTHENTICATION_APIKEY_ENABLED:-true} - AUTHENTICATION_APIKEY_ALLOWED_KEYS: ${WEAVIATE_AUTHENTICATION_APIKEY_ALLOWED_KEYS:-WVF5YThaHlkYwhGUSmCRgsX3tD5ngdN8pkih} - AUTHENTICATION_APIKEY_USERS: ${WEAVIATE_AUTHENTICATION_APIKEY_USERS:-hello@dify.ai} - AUTHORIZATION_ADMINLIST_ENABLED: ${WEAVIATE_AUTHORIZATION_ADMINLIST_ENABLED:-true} - AUTHORIZATION_ADMINLIST_USERS: ${WEAVIATE_AUTHORIZATION_ADMINLIST_USERS:-hello@dify.ai} - DISABLE_TELEMETRY: ${WEAVIATE_DISABLE_TELEMETRY:-false} - ENABLE_TOKENIZER_GSE: ${WEAVIATE_ENABLE_TOKENIZER_GSE:-false} - ENABLE_TOKENIZER_KAGOME_JA: ${WEAVIATE_ENABLE_TOKENIZER_KAGOME_JA:-false} - ENABLE_TOKENIZER_KAGOME_KR: ${WEAVIATE_ENABLE_TOKENIZER_KAGOME_KR:-false} - # 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: @@ -1024,10 +884,6 @@ services: - ./volumes/oceanbase/conf:/root/.obd/cluster - ./volumes/oceanbase/init.d:/root/boot/init.d environment: - OB_MEMORY_LIMIT: ${OCEANBASE_MEMORY_LIMIT:-6G} - OB_SYS_PASSWORD: ${OCEANBASE_VECTOR_PASSWORD:-difyai123456} - OB_TENANT_PASSWORD: ${OCEANBASE_VECTOR_PASSWORD:-difyai123456} - OB_CLUSTER_NAME: ${OCEANBASE_CLUSTER_NAME:-difyai} OB_SERVER_IP: 127.0.0.1 MODE: mini LANG: C.UTF-8 @@ -1038,7 +894,7 @@ services: test: [ "CMD-SHELL", - 'obclient -h127.0.0.1 -P2881 -uroot@test -p${OCEANBASE_VECTOR_PASSWORD:-difyai123456} -e "SELECT 1;"', + 'obclient -h127.0.0.1 -P2881 -uroot@test -p"$$OB_TENANT_PASSWORD" -e "SELECT 1;"', ] interval: 10s retries: 30 @@ -1047,6 +903,10 @@ services: # 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: @@ -1055,8 +915,6 @@ services: volumes: - ./volumes/seekdb:/var/lib/oceanbase environment: - ROOT_PASSWORD: ${OCEANBASE_VECTOR_PASSWORD:-difyai123456} - MEMORY_LIMIT: ${SEEKDB_MEMORY_LIMIT:-2G} REPORTER: dify-ai-seekdb ports: - "${OCEANBASE_VECTOR_PORT:-2881}:2881" @@ -1064,7 +922,7 @@ services: test: [ "CMD-SHELL", - 'mysql -h127.0.0.1 -P2881 -uroot -p${OCEANBASE_VECTOR_PASSWORD:-difyai123456} -e "SELECT 1;"', + 'mysql -h127.0.0.1 -P2881 -uroot -p"$$ROOT_PASSWORD" -e "SELECT 1;"', ] interval: 5s retries: 60 @@ -1073,26 +931,29 @@ services: # 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 - environment: - QDRANT_API_KEY: ${QDRANT_API_KEY:-difyai123456} # 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_ADMINISTRATOR_USERNAME=${COUCHBASE_USER:-Administrator} - - COUCHBASE_ADMINISTRATOR_PASSWORD=${COUCHBASE_PASSWORD:-password} - - COUCHBASE_BUCKET=${COUCHBASE_BUCKET_NAME:-Embeddings} - COUCHBASE_BUCKET_RAMSIZE=512 - COUCHBASE_RAM_SIZE=2048 - COUCHBASE_EVENTING_RAM_SIZE=512 @@ -1112,7 +973,7 @@ services: test: [ "CMD-SHELL", - "curl -s -f -u Administrator:password http://localhost:8091/pools/default/buckets | grep -q '\\[{' || exit 1", + "curl -s -f -u \"$$COUCHBASE_ADMINISTRATOR_USERNAME:$$COUCHBASE_ADMINISTRATOR_PASSWORD\" http://localhost:8091/pools/default/buckets | grep -q '\\[{' || exit 1", ] interval: 10s retries: 10 @@ -1121,21 +982,14 @@ services: # 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 - environment: - PGUSER: ${PGVECTOR_PGUSER:-postgres} - # The password for the default postgres user. - POSTGRES_PASSWORD: ${PGVECTOR_POSTGRES_PASSWORD:-difyai123456} - # The name of the default postgres database. - POSTGRES_DB: ${PGVECTOR_POSTGRES_DB:-dify} - # postgres data directory - PGDATA: ${PGVECTOR_PGDATA:-/var/lib/postgresql/data/pgdata} - # pg_bigm module for full text search - PG_BIGM: ${PGVECTOR_PG_BIGM:-false} - PG_BIGM_VERSION: ${PGVECTOR_PG_BIGM_VERSION:-1.2-20240606} volumes: - ./volumes/pgvector/data:/var/lib/postgresql/data - ./pgvector/docker-entrypoint.sh:/docker-entrypoint.sh @@ -1172,18 +1026,14 @@ services: # 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 - environment: - PGUSER: ${PGVECTOR_PGUSER:-postgres} - # The password for the default postgres user. - POSTGRES_PASSWORD: ${PGVECTOR_POSTGRES_PASSWORD:-difyai123456} - # The name of the default postgres database. - POSTGRES_DB: ${PGVECTOR_POSTGRES_DB:-dify} - # postgres data directory - PGDATA: ${PGVECTOR_PGDATA:-/var/lib/postgresql/data/pgdata} volumes: - ./volumes/pgvecto_rs/data:/var/lib/postgresql/data healthcheck: @@ -1194,19 +1044,23 @@ services: # 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 - environment: - CHROMA_SERVER_AUTHN_CREDENTIALS: ${CHROMA_SERVER_AUTHN_CREDENTIALS:-difyai123456} - CHROMA_SERVER_AUTHN_PROVIDER: ${CHROMA_SERVER_AUTHN_PROVIDER:-chromadb.auth.token_authn.TokenAuthenticationServerProvider} - IS_PERSISTENT: ${CHROMA_IS_PERSISTENT:-TRUE} # 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 @@ -1223,11 +1077,14 @@ services: entrypoint: ["/custom-entrypoint.sh"] tty: true environment: - TZ: ${IRIS_TIMEZONE:-UTC} 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 @@ -1237,21 +1094,19 @@ services: type: volume target: /opt/oracle/oradata - ./startupscripts:/opt/oracle/scripts/startup - environment: - ORACLE_PWD: ${ORACLE_PWD:-Dify123456} - ORACLE_CHARACTERSET: ${ORACLE_CHARACTERSET:-AL32UTF8} # 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 - environment: - ETCD_AUTO_COMPACTION_MODE: ${ETCD_AUTO_COMPACTION_MODE:-revision} - ETCD_AUTO_COMPACTION_RETENTION: ${ETCD_AUTO_COMPACTION_RETENTION:-1000} - ETCD_QUOTA_BACKEND_BYTES: ${ETCD_QUOTA_BACKEND_BYTES:-4294967296} - ETCD_SNAPSHOT_COUNT: ${ETCD_SNAPSHOT_COUNT:-50000} 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 @@ -1264,13 +1119,16 @@ services: - 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 - environment: - MINIO_ACCESS_KEY: ${MINIO_ACCESS_KEY:-minioadmin} - MINIO_SECRET_KEY: ${MINIO_SECRET_KEY:-minioadmin} volumes: - ./volumes/milvus/minio:/minio_data command: minio server /minio_data --console-address ":9001" @@ -1283,15 +1141,17 @@ services: - 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"] - environment: - ETCD_ENDPOINTS: ${ETCD_ENDPOINTS:-etcd:2379} - MINIO_ADDRESS: ${MINIO_ADDRESS:-minio:9000} - common.security.authorizationEnabled: ${MILVUS_AUTHORIZATION_ENABLED:-true} volumes: - ./volumes/milvus/milvus:/var/lib/milvus healthcheck: @@ -1311,15 +1171,14 @@ services: # 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 - environment: - discovery.type: ${OPENSEARCH_DISCOVERY_TYPE:-single-node} - bootstrap.memory_lock: ${OPENSEARCH_BOOTSTRAP_MEMORY_LOCK:-true} - OPENSEARCH_JAVA_OPTS: -Xms${OPENSEARCH_JAVA_OPTS_MIN:-512m} -Xmx${OPENSEARCH_JAVA_OPTS_MAX:-1024m} - OPENSEARCH_INITIAL_ADMIN_PASSWORD: ${OPENSEARCH_INITIAL_ADMIN_PASSWORD:-Qazwsxedc!@#123} ulimits: memlock: soft: ${OPENSEARCH_MEMLOCK_SOFT:--1} @@ -1348,16 +1207,17 @@ services: # 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_USERNAME: ${OPENGAUSS_USER:-postgres} - GS_PASSWORD: ${OPENGAUSS_PASSWORD:-Dify@123} GS_PORT: ${OPENGAUSS_PORT:-6600} - GS_DB: ${OPENGAUSS_DATABASE:-dify} volumes: - ./volumes/opengauss/data:/var/lib/opengauss/data healthcheck: @@ -1398,18 +1258,17 @@ services: # 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 - volumes: - - ./elasticsearch/docker-entrypoint.sh:/docker-entrypoint-mount.sh - - dify_es01_data:/usr/share/elasticsearch/data environment: - ELASTIC_PASSWORD: ${ELASTICSEARCH_PASSWORD:-elastic} - VECTOR_STORE: ${VECTOR_STORE:-} cluster.name: dify-es-cluster node.name: dify-es0 discovery.type: single-node @@ -1417,6 +1276,11 @@ services: 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: diff --git a/docker/envs/core-services/api.defaults b/docker/envs/core-services/api.defaults new file mode 100644 index 00000000000..6f938a5c460 --- /dev/null +++ b/docker/envs/core-services/api.defaults @@ -0,0 +1,101 @@ +# Defaults for api. Copy this file to api.env to customize it. +# Edit the settings below in the copy; docker/.env takes precedence. +# Optional application settings are commented to retain their application defaults. + +# MODE is selected by the Compose service. +# Optional SaaS network-policy control plane and current-IP preview trust. +# NETWORK_ACCESS_API_URL= +# Comma-separated proxy CIDRs; empty disables Cloud IP previews. The edge +# must sanitize/append XFF and the trust boundary must match Gateway. +# NETWORK_ACCESS_TRUSTED_PROXY_CIDRS= +API_SENTRY_DSN=${API_SENTRY_DSN:-} +API_SENTRY_TRACES_SAMPLE_RATE=${API_SENTRY_TRACES_SAMPLE_RATE:-1.0} +API_SENTRY_PROFILES_SAMPLE_RATE=${API_SENTRY_PROFILES_SAMPLE_RATE:-1.0} +API_WEBSOCKET_WORKER_AMOUNT=${API_WEBSOCKET_WORKER_AMOUNT:-1} +API_WEBSOCKET_WORKER_CLASS=${API_WEBSOCKET_WORKER_CLASS:-geventwebsocket.gunicorn.workers.GeventWebSocketWorker} +API_WEBSOCKET_WORKER_CONNECTIONS=${API_WEBSOCKET_WORKER_CONNECTIONS:-1000} +API_WEBSOCKET_GUNICORN_TIMEOUT=${API_WEBSOCKET_GUNICORN_TIMEOUT:-360} +# Set EXPOSE_PLUGIN_DEBUGGING_HOST and EXPOSE_PLUGIN_DEBUGGING_PORT in docker/.env +# so the API connection and Compose port mapping use the same address. +PLUGIN_MAX_PACKAGE_SIZE=${PLUGIN_MAX_PACKAGE_SIZE:-52428800} +PLUGIN_DAEMON_TIMEOUT=${PLUGIN_DAEMON_TIMEOUT:-600.0} +# Set PLUGIN_DIFY_INNER_API_KEY in docker/.env; all callers must share the API's inner credential. +# Optional server-side secret mixed into account password hashes. Leave empty to keep the legacy hashing. +# WARNING: setting, changing, or removing this value changes every password hash and existing passwords +# will stop working. Do not change it casually. +# Use a high-entropy random value, e.g. generate one with `openssl rand -base64 32`. +# Keep it in this file rather than docker/.env, which is also loaded by plugin_daemon and agent_backend. +# PASSWORD_PEPPER= +# Client deadline for converting a Binding file to a ToolFile through the Agent backend. +# AGENT_BACKEND_BINDING_FILE_DOWNLOAD_TIMEOUT_SECONDS=240 +# KNOWLEDGE_FS_ENABLED=${KNOWLEDGE_FS_ENABLED:-false} +# Explicitly bypass the Community edition gate for development deployments. +# KNOWLEDGE_FS_COMMUNITY_DEV_ENABLED=${KNOWLEDGE_FS_COMMUNITY_DEV_ENABLED:-false} +# Production deployments require HTTPS; plain HTTP is limited to non-production or loopback. +# KNOWLEDGE_FS_BASE_URL= +# Optional trusted direct origin for KnowledgeFS internal requests. +# KNOWLEDGE_FS_DIRECT_ORIGIN= +# KNOWLEDGE_FS_LIFECYCLE_WORKER_ENABLED=false +# KNOWLEDGE_FS_INTEGRATED_PROVISION_READY=false +# KNOWLEDGE_FS_LEGACY_ACL_FREEZE_READY=false +# KNOWLEDGE_FS_LIFECYCLE_POLL_INTERVAL_SECONDS=15 +# KNOWLEDGE_FS_LIFECYCLE_LEASE_SECONDS=60 +# KNOWLEDGE_FS_LIFECYCLE_BATCH_SIZE=25 +# KNOWLEDGE_FS_CAPABILITY_V2_ENABLED=false +# KNOWLEDGE_FS_CAPABILITY_V2_SIGNING_KID= +# KNOWLEDGE_FS_CAPABILITY_V2_PRIVATE_KEY_PEM= +# KNOWLEDGE_FS_CAPABILITY_V2_PREVIOUS_PUBLIC_JWKS= +# KNOWLEDGE_FS_CAPABILITY_V2_ISSUER=dify-control-plane +# KNOWLEDGE_FS_CAPABILITY_V2_AUDIENCE=knowledge-fs +# KNOWLEDGE_FS_CAPABILITY_V2_MAX_TTL_SECONDS=60 +# KNOWLEDGE_FS_JWKS_CACHE_MAX_AGE_SECONDS=300 +# KNOWLEDGE_FS_PRODUCT_MAX_RESPONSE_BYTES=4194304 +# KNOWLEDGE_FS_TIMEOUT_SECONDS=10 +# Retrieval tests have a 60-second KnowledgeFS execution budget and need transport headroom. +# KNOWLEDGE_FS_RETRIEVAL_TEST_TIMEOUT_SECONDS=75 + +# Lifetime in days of difyctl (OAuth device flow) tokens, 1-365; out-of-range values are clamped +# OAUTH_TTL_DAYS=14 + +# Cloudflare Turnstile server-side verification for Dify Cloud sign-in +# TURNSTILE_SECRET_KEY= +# TURNSTILE_ALLOWED_HOSTNAMES= +# TURNSTILE_EMAIL_CODE_VERIFY_REQUIRED=false +DIFY_AGENT_API_TOKEN=${DIFY_AGENT_API_TOKEN:-dify-agent-run-token-for-dev-only} +AGENT_BACKEND_BASE_URL=${AGENT_BACKEND_BASE_URL:-http://agent_backend:5050} +# The bundled Nginx supplies a verified client IP; keep direct API callers trusted. +# Compose enables forwarded headers here; the bare application default is false. +# Override in api.env or docker/.env. See docker/README.md#client-ip-and-trusted-proxies +# before exposing the API directly. +RESPECT_XFORWARD_HEADERS_ENABLED=${RESPECT_XFORWARD_HEADERS_ENABLED:-true} +CELERY_BROKER_URL=${CELERY_BROKER_URL:-redis://:${REDIS_PASSWORD:-difyai123456}@${REDIS_HOST:-redis}:${REDIS_PORT:-6379}/1} + +# API server and OpenAPI settings +# DIFY_BIND_ADDRESS=0.0.0.0 +# DIFY_PORT=5001 +# SERVER_WORKER_AMOUNT=1 +# SERVER_WORKER_CONNECTIONS=10 +# Must stay above the idle connection reuse window of the HTTP clients that +# pool connections to this server. Node's global fetch (undici) keeps a socket +# idle for 4s before dropping it, so a shorter value here lets the server close +# a connection the client still considers reusable, which surfaces as +# intermittent ECONNRESET on server-side rendering. 0 disables keep-alive. +# GUNICORN_KEEP_ALIVE=10 +# WEBSOCKET_MAX_HTTP_BUFFER_SIZE=10485760 +# SWAGGER_UI_ENABLED=false +# SWAGGER_UI_PATH=/swagger-ui.html +# OPENAPI_ENABLED=false +# OPENAPI_CORS_ALLOW_ORIGINS= +# OPENAPI_KNOWN_CLIENT_IDS=difyctl +# OPENAPI_RATE_LIMIT_PER_TOKEN=60 +# DEVICE_FLOW_APPROVE_RATE_LIMIT_PER_HOUR=10 +# ENABLE_OAUTH_BEARER=false + +# Container variable mappings; keep these expressions unchanged in the copy. +AGENT_BACKEND_API_TOKEN=${DIFY_AGENT_API_TOKEN:-dify-agent-run-token-for-dev-only} +INNER_API_KEY_FOR_PLUGIN=${PLUGIN_DIFY_INNER_API_KEY:-QaHbTe77CtuXmsfyhR7+vRjI/+XbV1AaFy691iy+kGDv2Jvy0/eAh8Y1} +PLUGIN_REMOTE_INSTALL_HOST=${EXPOSE_PLUGIN_DEBUGGING_HOST:-localhost} +PLUGIN_REMOTE_INSTALL_PORT=${EXPOSE_PLUGIN_DEBUGGING_PORT:-5003} +SENTRY_DSN=${API_SENTRY_DSN:-} +SENTRY_PROFILES_SAMPLE_RATE=${API_SENTRY_PROFILES_SAMPLE_RATE:-1.0} +SENTRY_TRACES_SAMPLE_RATE=${API_SENTRY_TRACES_SAMPLE_RATE:-1.0} diff --git a/docker/envs/core-services/api.env.example b/docker/envs/core-services/api.env.example deleted file mode 100644 index 7e35b0bda8a..00000000000 --- a/docker/envs/core-services/api.env.example +++ /dev/null @@ -1,59 +0,0 @@ -# ------------------------------ -# Api Configuration -# ------------------------------ - -MODE=api -# Optional SaaS network-policy control plane and current-IP preview trust. -NETWORK_ACCESS_API_URL= -# Comma-separated proxy CIDRs; empty disables Cloud IP previews. The edge -# must sanitize/append XFF and the trust boundary must match Gateway. -NETWORK_ACCESS_TRUSTED_PROXY_CIDRS= -SENTRY_DSN= -SENTRY_TRACES_SAMPLE_RATE=1.0 -SENTRY_PROFILES_SAMPLE_RATE=1.0 -PLUGIN_REMOTE_INSTALL_HOST=localhost -PLUGIN_REMOTE_INSTALL_PORT=5003 -PLUGIN_MAX_PACKAGE_SIZE=52428800 -PLUGIN_DAEMON_TIMEOUT=600.0 -INNER_API_KEY_FOR_PLUGIN=QaHbTe77CtuXmsfyhR7+vRjI/+XbV1AaFy691iy+kGDv2Jvy0/eAh8Y1 -# Optional server-side secret mixed into account password hashes. Leave empty to keep the legacy hashing. -# WARNING: setting, changing, or removing this value changes every password hash and existing passwords -# will stop working. Do not change it casually. -# Use a high-entropy random value, e.g. generate one with `openssl rand -base64 32`. -# Keep it in this file rather than docker/.env, which is also loaded by plugin_daemon and agent_backend. -PASSWORD_PEPPER= -# Client deadline for converting a Binding file to a ToolFile through the Agent backend. -AGENT_BACKEND_BINDING_FILE_DOWNLOAD_TIMEOUT_SECONDS=240 -KNOWLEDGE_FS_ENABLED=${KNOWLEDGE_FS_ENABLED:-false} -# Explicitly bypass the Community edition gate for development deployments. -KNOWLEDGE_FS_COMMUNITY_DEV_ENABLED=${KNOWLEDGE_FS_COMMUNITY_DEV_ENABLED:-false} -# Production deployments require HTTPS; plain HTTP is limited to non-production or loopback. -KNOWLEDGE_FS_BASE_URL= -# Optional trusted direct origin for KnowledgeFS internal requests. -KNOWLEDGE_FS_DIRECT_ORIGIN= -KNOWLEDGE_FS_LIFECYCLE_WORKER_ENABLED=false -KNOWLEDGE_FS_INTEGRATED_PROVISION_READY=false -KNOWLEDGE_FS_LEGACY_ACL_FREEZE_READY=false -KNOWLEDGE_FS_LIFECYCLE_POLL_INTERVAL_SECONDS=15 -KNOWLEDGE_FS_LIFECYCLE_LEASE_SECONDS=60 -KNOWLEDGE_FS_LIFECYCLE_BATCH_SIZE=25 -KNOWLEDGE_FS_CAPABILITY_V2_ENABLED=false -KNOWLEDGE_FS_CAPABILITY_V2_SIGNING_KID= -KNOWLEDGE_FS_CAPABILITY_V2_PRIVATE_KEY_PEM= -KNOWLEDGE_FS_CAPABILITY_V2_PREVIOUS_PUBLIC_JWKS= -KNOWLEDGE_FS_CAPABILITY_V2_ISSUER=dify-control-plane -KNOWLEDGE_FS_CAPABILITY_V2_AUDIENCE=knowledge-fs -KNOWLEDGE_FS_CAPABILITY_V2_MAX_TTL_SECONDS=60 -KNOWLEDGE_FS_JWKS_CACHE_MAX_AGE_SECONDS=300 -KNOWLEDGE_FS_PRODUCT_MAX_RESPONSE_BYTES=4194304 -KNOWLEDGE_FS_TIMEOUT_SECONDS=10 -# Retrieval tests have a 60-second KnowledgeFS execution budget and need transport headroom. -KNOWLEDGE_FS_RETRIEVAL_TEST_TIMEOUT_SECONDS=75 - -# Lifetime in days of difyctl (OAuth device flow) tokens, 1-365; out-of-range values are clamped -# OAUTH_TTL_DAYS=14 - -# Cloudflare Turnstile server-side verification for Dify Cloud sign-in -TURNSTILE_SECRET_KEY= -TURNSTILE_ALLOWED_HOSTNAMES= -TURNSTILE_EMAIL_CODE_VERIFY_REQUIRED=false diff --git a/docker/envs/core-services/dify-agent.defaults b/docker/envs/core-services/dify-agent.defaults new file mode 100644 index 00000000000..82e7183dcbe --- /dev/null +++ b/docker/envs/core-services/dify-agent.defaults @@ -0,0 +1,132 @@ +# Defaults for agent_backend. Copy this file to dify-agent.env to customize it. +# Edit the settings below in the copy; docker/.env takes precedence. +# Optional application settings are commented to retain their application defaults. + +# AGENT_BACKEND_BASE_URL=http://agent_backend:5050 +# Set this in docker/.env to share the same control-plane token with API and workers. +# Replace the development default in production. +# Generate one with: python -c 'import secrets; print(secrets.token_urlsafe(32))' +DIFY_AGENT_API_TOKEN=${DIFY_AGENT_API_TOKEN:-dify-agent-run-token-for-dev-only} + +# Leave empty to derive from REDIS_PASSWORD in Docker Compose. +DIFY_AGENT_REDIS_URL=${DIFY_AGENT_REDIS_URL:-redis://:${REDIS_PASSWORD:-difyai123456}@redis:6379/2} +# DIFY_AGENT_REDIS_PREFIX=dify-agent +# DIFY_AGENT_SHUTDOWN_GRACE_SECONDS=30 +# DIFY_AGENT_RUN_RETENTION_SECONDS=7200 +# DIFY_AGENT_RUN_EVENT_STREAM_MAX_LENGTH=5000 +# DIFY_AGENT_STREAM_TEXT_DELTA_COALESCING_ENABLED=true +# DIFY_AGENT_STREAM_TEXT_DELTA_FLUSH_INTERVAL_MS=100 +# DIFY_AGENT_STREAM_TEXT_DELTA_MAX_CHARS=4096 +# Internal deadline for the Agent Backend model/tool loop. The effective run +# limit is whichever expires first: this value or the relevant API/Workflow +# outer execution limit. To allow every path to run for one hour, also set +# APP_MAX_EXECUTION_TIME and WORKFLOW_MAX_EXECUTION_TIME to at least 3600. +# DIFY_AGENT_RUN_TIMEOUT_SECONDS=3600 +# DIFY_AGENT_BINDING_FILE_DOWNLOAD_COMMAND_TIMEOUT_SECONDS=210 + +# Shared outbound HTTP client timeouts and connection limits. +# DIFY_AGENT_OUTBOUND_HTTP_CONNECT_TIMEOUT=10 +# DIFY_AGENT_OUTBOUND_HTTP_READ_TIMEOUT=600 +# DIFY_AGENT_OUTBOUND_HTTP_WRITE_TIMEOUT=30 +# DIFY_AGENT_OUTBOUND_HTTP_POOL_TIMEOUT=10 +# DIFY_AGENT_OUTBOUND_HTTP_MAX_CONNECTIONS=100 +# DIFY_AGENT_OUTBOUND_HTTP_MAX_KEEPALIVE_CONNECTIONS=20 +# DIFY_AGENT_OUTBOUND_HTTP_KEEPALIVE_EXPIRY=30 + +# DIFY_AGENT_TRAJECTORY_ENABLED=false +# Collect API-issued application traces into the Dify inner API inbox. +# DIFY_AGENT_APPLICATION_TRACING_ENABLED=false +# DIFY_AGENT_TRAJECTORY_OTLP_TRACES_ENDPOINT= +# DIFY_AGENT_TRAJECTORY_OTLP_HEADERS={} +# DIFY_AGENT_TRAJECTORY_SERVICE_NAME=dify-agent-trajectory +# DIFY_AGENT_TRAJECTORY_INCLUDE_CONTENT=false +# DIFY_AGENT_TRAJECTORY_TRACE_CONTEXT_MODE=isolated +# DIFY_AGENT_TRAJECTORY_MAX_QUEUE_SIZE=2048 +# DIFY_AGENT_TRAJECTORY_MAX_EXPORT_BATCH_SIZE=512 +# DIFY_AGENT_TRAJECTORY_SCHEDULE_DELAY_MS=5000 +# DIFY_AGENT_TRAJECTORY_EXPORT_TIMEOUT_MS=5000 + +# LOGFIRE_CONSOLE=false + +# Leave empty to derive from PLUGIN_DAEMON_URL and PLUGIN_DAEMON_KEY in Docker Compose. +# Set PLUGIN_DAEMON_KEY in docker/.env when using the bundled daemon; keep the key expression below unchanged. +DIFY_AGENT_PLUGIN_DAEMON_URL=${DIFY_AGENT_PLUGIN_DAEMON_URL:-${PLUGIN_DAEMON_URL:-http://plugin_daemon:5002}} +DIFY_AGENT_PLUGIN_DAEMON_API_KEY=${DIFY_AGENT_PLUGIN_DAEMON_API_KEY:-${PLUGIN_DAEMON_KEY:-lYkiYYT6owG+71oLerGzA7GXCgOT++6ovaezWAjpCjf+Sjc3ZtU+qUEi}} + +# Leave empty to derive from PLUGIN_DIFY_INNER_API_URL and PLUGIN_DIFY_INNER_API_KEY in Docker Compose. +# DIFY_AGENT_INNER_API_KEY must match API/worker INNER_API_KEY_FOR_PLUGIN, not INNER_API_KEY. +# Set PLUGIN_DIFY_INNER_API_KEY in docker/.env; keep the key expression below unchanged for the bundled API. +DIFY_AGENT_INNER_API_URL=${DIFY_AGENT_INNER_API_URL:-${PLUGIN_DIFY_INNER_API_URL:-http://api:5001}} +DIFY_AGENT_INNER_API_KEY=${DIFY_AGENT_INNER_API_KEY:-${PLUGIN_DIFY_INNER_API_KEY:-QaHbTe77CtuXmsfyhR7+vRjI/+XbV1AaFy691iy+kGDv2Jvy0/eAh8Y1}} + +# Select exactly one coherent Home Snapshot + Sandbox backend. +DIFY_AGENT_RUNTIME_BACKEND=${DIFY_AGENT_RUNTIME_BACKEND:-local} +DIFY_AGENT_LOCAL_SANDBOX_ENDPOINT=${DIFY_AGENT_LOCAL_SANDBOX_ENDPOINT:-${DIFY_AGENT_SHELLCTL_ENTRYPOINT:-http://local_sandbox:5004}} +# Required nonblank secret for Local. Set in docker/.env to also configure shellctl. +DIFY_AGENT_LOCAL_SANDBOX_AUTH_TOKEN=${DIFY_AGENT_LOCAL_SANDBOX_AUTH_TOKEN:-${DIFY_AGENT_SHELLCTL_AUTH_TOKEN:-}} +# E2B_API_KEY and E2B_API_TOKEN remain accepted as deployment-level fallbacks. +DIFY_AGENT_E2B_API_KEY=${DIFY_AGENT_E2B_API_KEY:-${E2B_API_KEY:-${E2B_API_TOKEN:-}}} +DIFY_AGENT_E2B_TEMPLATE=${DIFY_AGENT_E2B_TEMPLATE:-difys-default-team/dify-agent-local-sandbox} +# RuntimeLease active limit; its default matches the independently configurable Agent run deadline. +# DIFY_AGENT_E2B_ACTIVE_TIMEOUT_SECONDS=3600 +# DIFY_AGENT_E2B_SHELLCTL_PORT=5004 + +# Independent sandbox usage ledger. API owns fixed activation time T0; no backfill. +# Enable only with matching API metering project settings and events API access. +# Celery Beat in the API triggers a protected one-shot collection endpoint. +# No operation logging, Agent polling loop, or collector Redis leader. +# Collection requires the existing DIFY_AGENT_API_TOKEN to be configured. +# DIFY_AGENT_SANDBOX_METERING_ENABLED=false +# DIFY_AGENT_E2B_PROJECT_ID= +# DIFY_AGENT_SANDBOX_METERING_OVERLAP_SECONDS=900 +# DIFY_AGENT_SANDBOX_METERING_FULL_SCAN_INTERVAL_SECONDS=3600 +# DIFY_AGENT_SANDBOX_METERING_MAX_PAGES=1000 + +# OpenShell backend (DIFY_AGENT_RUNTIME_BACKEND=openshell): sandboxes run on a +# self-hosted NVIDIA OpenShell gateway. Setup and validation: +# dify-agent/docs/dify-agent/guide/openshell.md +# gRPC endpoint as host:port (no scheme), e.g. localhost:17670. +# DIFY_AGENT_OPENSHELL_GATEWAY_ENDPOINT= +# One workspace and one dedicated shared volume per tenant in production. +# DIFY_AGENT_OPENSHELL_WORKSPACE=default +# Auth: an OIDC bearer token, or mTLS bundle paths mounted into agent_backend. +# DIFY_AGENT_OPENSHELL_BEARER_TOKEN= +# DIFY_AGENT_OPENSHELL_TLS_CA_PATH= +# DIFY_AGENT_OPENSHELL_TLS_CLIENT_CERT_PATH= +# DIFY_AGENT_OPENSHELL_TLS_CLIENT_KEY_PATH= +# Set true only for plaintext local-dev gateways. +# DIFY_AGENT_OPENSHELL_INSECURE=false +# Required: build the runtime image yourself from this checkout; do not rely on a published latest tag. +# From the repository root (the directory containing dify-agent-runtime/): +# docker build -f dify-agent-runtime/docker/Dockerfile -t dify-agent-runtime:latest dify-agent-runtime +# The image must include shellctl and iproute2. This value must match the built image tag. +# Build into the gateway's Docker daemon, or push to a registry it can pull from and use that image reference. +# DIFY_AGENT_OPENSHELL_SANDBOX_IMAGE=docker.io/library/dify-agent-runtime:latest +# Required JSON driver_config mounting the operator-owned shared volume; +# one-time volume initialization and driver examples live in the guide. +# DIFY_AGENT_OPENSHELL_DRIVER_CONFIG= +# DIFY_AGENT_OPENSHELL_SHARED_MOUNT_PATH=/mnt/dify-agent-shared +# Optional comma-separated host:port egress allowlist (no scheme or path). +# Empty sends no network policy (gateway/driver default egress); when set, +# sandbox egress is enforced to exactly these endpoints — include the Agent +# Stub and files endpoints, e.g. agent.example.com:443,dify.example.com:443. +# DIFY_AGENT_OPENSHELL_EGRESS_ALLOW= +# Required. shellctl path isolation stays off (sandbox Landlock is authoritative). +# DIFY_AGENT_OPENSHELL_SHELLCTL_AUTH_TOKEN= +# DIFY_AGENT_OPENSHELL_SHELLCTL_PORT=5004 +# DIFY_AGENT_OPENSHELL_READY_TIMEOUT_SECONDS=300 +# DIFY_AGENT_OPENSHELL_EXEC_TIMEOUT_SECONDS=120 +# Sandbox-reachable Dify API base for signed /files/* transfers. +DIFY_AGENT_SANDBOX_FILES_BASE_URL=${DIFY_AGENT_SANDBOX_FILES_BASE_URL:-http://api:5001} +# Maximum Agent Stub upload size in MiB; forwarded to Dify API as a signed byte limit. +# DIFY_AGENT_STUB_UPLOAD_FILE_SIZE_LIMIT=50 +DIFY_AGENT_STUB_API_BASE_URL=${DIFY_AGENT_STUB_API_BASE_URL:-http://agent_backend:5050/agent-stub} +# This is security-sensitive: it derives the JWE encryption key for Agent Stub bearer tokens. +# Replace this development default in production. +# Generate one with: python -c 'import secrets; print(secrets.token_urlsafe(32))' +DIFY_AGENT_SERVER_SECRET_KEY=${DIFY_AGENT_SERVER_SECRET_KEY:-MDEyMzQ1Njc4OWFiY2RlZjAxMjM0NTY3ODlhYmNkZWY} + +# JSON array of regex patterns to redact from shell output shown to the agent. +# The JWE token value is always redacted regardless of this setting. +# Example: DIFY_AGENT_SHELL_REDACT_PATTERNS=["sk-[A-Za-z0-9]+","ghp_[A-Za-z0-9]{36}"] +# DIFY_AGENT_SHELL_REDACT_PATTERNS= diff --git a/docker/envs/core-services/dify-agent.env.example b/docker/envs/core-services/dify-agent.env.example deleted file mode 100644 index f80ed6db420..00000000000 --- a/docker/envs/core-services/dify-agent.env.example +++ /dev/null @@ -1,132 +0,0 @@ -# ------------------------------ -# Dify Agent Backend Configuration -# ------------------------------ - -AGENT_BACKEND_BASE_URL=http://agent_backend:5050 -# Set this in docker/.env to share the same control-plane token with API and workers. -# Replace the development default in production. -# Generate one with: python -c 'import secrets; print(secrets.token_urlsafe(32))' -# DIFY_AGENT_API_TOKEN=dify-agent-run-token-for-dev-only - -# Leave empty to derive from REDIS_PASSWORD in Docker Compose. -DIFY_AGENT_REDIS_URL= -DIFY_AGENT_REDIS_PREFIX=dify-agent -DIFY_AGENT_SHUTDOWN_GRACE_SECONDS=30 -DIFY_AGENT_RUN_RETENTION_SECONDS=7200 -DIFY_AGENT_RUN_EVENT_STREAM_MAX_LENGTH=5000 -DIFY_AGENT_STREAM_TEXT_DELTA_COALESCING_ENABLED=true -DIFY_AGENT_STREAM_TEXT_DELTA_FLUSH_INTERVAL_MS=100 -DIFY_AGENT_STREAM_TEXT_DELTA_MAX_CHARS=4096 -# Internal deadline for the Agent Backend model/tool loop. The effective run -# limit is whichever expires first: this value or the relevant API/Workflow -# outer execution limit. To allow every path to run for one hour, also set -# APP_MAX_EXECUTION_TIME and WORKFLOW_MAX_EXECUTION_TIME to at least 3600. -DIFY_AGENT_RUN_TIMEOUT_SECONDS=3600 -DIFY_AGENT_BINDING_FILE_DOWNLOAD_COMMAND_TIMEOUT_SECONDS=210 - -# Shared outbound HTTP client timeouts and connection limits. -DIFY_AGENT_OUTBOUND_HTTP_CONNECT_TIMEOUT=10 -DIFY_AGENT_OUTBOUND_HTTP_READ_TIMEOUT=600 -DIFY_AGENT_OUTBOUND_HTTP_WRITE_TIMEOUT=30 -DIFY_AGENT_OUTBOUND_HTTP_POOL_TIMEOUT=10 -DIFY_AGENT_OUTBOUND_HTTP_MAX_CONNECTIONS=100 -DIFY_AGENT_OUTBOUND_HTTP_MAX_KEEPALIVE_CONNECTIONS=20 -DIFY_AGENT_OUTBOUND_HTTP_KEEPALIVE_EXPIRY=30 - -DIFY_AGENT_TRAJECTORY_ENABLED=false -# Collect API-issued application traces into the Dify inner API inbox. -DIFY_AGENT_APPLICATION_TRACING_ENABLED=false -DIFY_AGENT_TRAJECTORY_OTLP_TRACES_ENDPOINT= -DIFY_AGENT_TRAJECTORY_OTLP_HEADERS={} -DIFY_AGENT_TRAJECTORY_SERVICE_NAME=dify-agent-trajectory -DIFY_AGENT_TRAJECTORY_INCLUDE_CONTENT=false -DIFY_AGENT_TRAJECTORY_TRACE_CONTEXT_MODE=isolated -DIFY_AGENT_TRAJECTORY_MAX_QUEUE_SIZE=2048 -DIFY_AGENT_TRAJECTORY_MAX_EXPORT_BATCH_SIZE=512 -DIFY_AGENT_TRAJECTORY_SCHEDULE_DELAY_MS=5000 -DIFY_AGENT_TRAJECTORY_EXPORT_TIMEOUT_MS=5000 - -LOGFIRE_CONSOLE=false - -# Leave empty to derive from PLUGIN_DAEMON_URL and PLUGIN_DAEMON_KEY in Docker Compose. -DIFY_AGENT_PLUGIN_DAEMON_URL= -DIFY_AGENT_PLUGIN_DAEMON_API_KEY= - -# Leave empty to derive from PLUGIN_DIFY_INNER_API_URL and PLUGIN_DIFY_INNER_API_KEY in Docker Compose. -# DIFY_AGENT_INNER_API_KEY must match API/worker INNER_API_KEY_FOR_PLUGIN, not INNER_API_KEY. -DIFY_AGENT_INNER_API_URL= -DIFY_AGENT_INNER_API_KEY= - -# Select exactly one coherent Home Snapshot + Sandbox backend. -DIFY_AGENT_RUNTIME_BACKEND=local -DIFY_AGENT_LOCAL_SANDBOX_ENDPOINT=http://local_sandbox:5004 -# Required nonblank secret for Local. Set in the shared Compose interpolation source: shell, -# docker/.env, or --env-file. Setting only this service env file is overridden -# by Compose's explicit environment value and does not configure shellctl. -DIFY_AGENT_LOCAL_SANDBOX_AUTH_TOKEN= -# E2B_API_KEY and E2B_API_TOKEN remain accepted as deployment-level fallbacks. -DIFY_AGENT_E2B_API_KEY=${DIFY_AGENT_E2B_API_KEY:-${E2B_API_KEY:-${E2B_API_TOKEN:-}}} -DIFY_AGENT_E2B_TEMPLATE=difys-default-team/dify-agent-local-sandbox -# RuntimeLease active limit; its default matches the independently configurable Agent run deadline. -DIFY_AGENT_E2B_ACTIVE_TIMEOUT_SECONDS=3600 -DIFY_AGENT_E2B_SHELLCTL_PORT=5004 - -# Independent sandbox usage ledger. API owns fixed activation time T0; no backfill. -# Enable only with matching API metering project settings and events API access. -# Celery Beat in the API triggers a protected one-shot collection endpoint. -# No operation logging, Agent polling loop, or collector Redis leader. -# Collection requires the existing DIFY_AGENT_API_TOKEN to be configured. -DIFY_AGENT_SANDBOX_METERING_ENABLED=false -DIFY_AGENT_E2B_PROJECT_ID= -DIFY_AGENT_SANDBOX_METERING_OVERLAP_SECONDS=900 -DIFY_AGENT_SANDBOX_METERING_FULL_SCAN_INTERVAL_SECONDS=3600 -DIFY_AGENT_SANDBOX_METERING_MAX_PAGES=1000 - -# OpenShell backend (DIFY_AGENT_RUNTIME_BACKEND=openshell): sandboxes run on a -# self-hosted NVIDIA OpenShell gateway. Setup and validation: -# dify-agent/docs/dify-agent/guide/openshell.md -# gRPC endpoint as host:port (no scheme), e.g. localhost:17670. -DIFY_AGENT_OPENSHELL_GATEWAY_ENDPOINT= -# One workspace and one dedicated shared volume per tenant in production. -DIFY_AGENT_OPENSHELL_WORKSPACE=default -# Auth: an OIDC bearer token, or mTLS bundle paths mounted into agent_backend. -DIFY_AGENT_OPENSHELL_BEARER_TOKEN= -DIFY_AGENT_OPENSHELL_TLS_CA_PATH= -DIFY_AGENT_OPENSHELL_TLS_CLIENT_CERT_PATH= -DIFY_AGENT_OPENSHELL_TLS_CLIENT_KEY_PATH= -# Set true only for plaintext local-dev gateways. -DIFY_AGENT_OPENSHELL_INSECURE=false -# Required: build the runtime image yourself from this checkout; do not rely on a published latest tag. -# From the repository root (the directory containing dify-agent-runtime/): -# docker build -f dify-agent-runtime/docker/Dockerfile -t dify-agent-runtime:latest dify-agent-runtime -# The image must include shellctl and iproute2. This value must match the built image tag. -# Build into the gateway's Docker daemon, or push to a registry it can pull from and use that image reference. -DIFY_AGENT_OPENSHELL_SANDBOX_IMAGE=docker.io/library/dify-agent-runtime:latest -# Required JSON driver_config mounting the operator-owned shared volume; -# one-time volume initialization and driver examples live in the guide. -DIFY_AGENT_OPENSHELL_DRIVER_CONFIG= -DIFY_AGENT_OPENSHELL_SHARED_MOUNT_PATH=/mnt/dify-agent-shared -# Optional comma-separated host:port egress allowlist (no scheme or path). -# Empty sends no network policy (gateway/driver default egress); when set, -# sandbox egress is enforced to exactly these endpoints — include the Agent -# Stub and files endpoints, e.g. agent.example.com:443,dify.example.com:443. -DIFY_AGENT_OPENSHELL_EGRESS_ALLOW= -# Required. shellctl path isolation stays off (sandbox Landlock is authoritative). -DIFY_AGENT_OPENSHELL_SHELLCTL_AUTH_TOKEN= -DIFY_AGENT_OPENSHELL_SHELLCTL_PORT=5004 -DIFY_AGENT_OPENSHELL_READY_TIMEOUT_SECONDS=300 -DIFY_AGENT_OPENSHELL_EXEC_TIMEOUT_SECONDS=120 -# Sandbox-reachable Dify API base for signed /files/* transfers. -DIFY_AGENT_SANDBOX_FILES_BASE_URL=http://api:5001 -# Maximum Agent Stub upload size in MiB; forwarded to Dify API as a signed byte limit. -DIFY_AGENT_STUB_UPLOAD_FILE_SIZE_LIMIT=50 -DIFY_AGENT_STUB_API_BASE_URL=http://agent_backend:5050/agent-stub -# This is security-sensitive: it derives the JWE encryption key for Agent Stub bearer tokens. -# Replace this development default in production. -# Generate one with: python -c 'import secrets; print(secrets.token_urlsafe(32))' -DIFY_AGENT_SERVER_SECRET_KEY=MDEyMzQ1Njc4OWFiY2RlZjAxMjM0NTY3ODlhYmNkZWY - -# JSON array of regex patterns to redact from shell output shown to the agent. -# The JWE token value is always redacted regardless of this setting. -# Example: DIFY_AGENT_SHELL_REDACT_PATTERNS=["sk-[A-Za-z0-9]+","ghp_[A-Za-z0-9]{36}"] -DIFY_AGENT_SHELL_REDACT_PATTERNS= diff --git a/docker/envs/core-services/plugin-daemon.defaults b/docker/envs/core-services/plugin-daemon.defaults new file mode 100644 index 00000000000..c90f5fa1ebf --- /dev/null +++ b/docker/envs/core-services/plugin-daemon.defaults @@ -0,0 +1,89 @@ +# Defaults for plugin_daemon. Copy this file to plugin-daemon.env to customize it. +# Edit the settings below in the copy; docker/.env takes precedence. +# Optional application settings are commented to retain their application defaults. + +DB_PLUGIN_DATABASE=${DB_PLUGIN_DATABASE:-dify_plugin} +# PLUGIN_DAEMON_URL=http://plugin_daemon:5002 +PLUGIN_PPROF_ENABLED=${PLUGIN_PPROF_ENABLED:-false} +PLUGIN_DIFY_INNER_API_URL=${PLUGIN_DIFY_INNER_API_URL:-http://api:5001} +FORCE_VERIFYING_SIGNATURE=${FORCE_VERIFYING_SIGNATURE:-true} +PLUGIN_STDIO_BUFFER_SIZE=${PLUGIN_STDIO_BUFFER_SIZE:-1024} +PLUGIN_STDIO_MAX_BUFFER_SIZE=${PLUGIN_STDIO_MAX_BUFFER_SIZE:-5242880} +PLUGIN_PYTHON_ENV_INIT_TIMEOUT=${PLUGIN_PYTHON_ENV_INIT_TIMEOUT:-120} +PLUGIN_MAX_EXECUTION_TIMEOUT=${PLUGIN_MAX_EXECUTION_TIMEOUT:-600} +PLUGIN_DEBUGGING_HOST=${PLUGIN_DEBUGGING_HOST:-0.0.0.0} +# Set PLUGIN_DEBUGGING_PORT in docker/.env; Compose needs it before loading service files. +# Set PLUGIN_DAEMON_KEY in docker/.env; API, workers, and Agent must share the daemon's credential. +# Set PLUGIN_DIFY_INNER_API_KEY in docker/.env; all callers must share the API's inner credential. +PLUGIN_DAEMON_PORT=${PLUGIN_DAEMON_PORT:-5002} +PLUGIN_STORAGE_TYPE=${PLUGIN_STORAGE_TYPE:-local} +PLUGIN_STORAGE_LOCAL_ROOT=${PLUGIN_STORAGE_LOCAL_ROOT:-/app/storage} +PLUGIN_WORKING_PATH=${PLUGIN_WORKING_PATH:-/app/storage/cwd} +# Optional: set in docker/.env for Docker/WSL bind-mount permission errors (langgenius/dify#42433). +# UV_CACHE_DIR=/tmp/.uv-cache +PLUGIN_STORAGE_OSS_BUCKET=${PLUGIN_STORAGE_OSS_BUCKET:-} +PLUGIN_ALIYUN_OSS_ACCESS_KEY_ID=${PLUGIN_ALIYUN_OSS_ACCESS_KEY_ID:-} +PLUGIN_ALIYUN_OSS_ACCESS_KEY_SECRET=${PLUGIN_ALIYUN_OSS_ACCESS_KEY_SECRET:-} +PLUGIN_ALIYUN_OSS_AUTH_VERSION=${PLUGIN_ALIYUN_OSS_AUTH_VERSION:-v4} +PLUGIN_ALIYUN_OSS_ENDPOINT=${PLUGIN_ALIYUN_OSS_ENDPOINT:-} +PLUGIN_ALIYUN_OSS_PATH=${PLUGIN_ALIYUN_OSS_PATH:-} +PLUGIN_ALIYUN_OSS_REGION=${PLUGIN_ALIYUN_OSS_REGION:-} +PLUGIN_AWS_ACCESS_KEY=${PLUGIN_AWS_ACCESS_KEY:-} +PLUGIN_AWS_REGION=${PLUGIN_AWS_REGION:-} +PLUGIN_AWS_SECRET_KEY=${PLUGIN_AWS_SECRET_KEY:-} +PLUGIN_AZURE_BLOB_STORAGE_CONNECTION_STRING=${PLUGIN_AZURE_BLOB_STORAGE_CONNECTION_STRING:-} +PLUGIN_AZURE_BLOB_STORAGE_CONTAINER_NAME=${PLUGIN_AZURE_BLOB_STORAGE_CONTAINER_NAME:-} +DB_SSL_MODE=${DB_SSL_MODE:-disable} +PLUGIN_MAX_PACKAGE_SIZE=${PLUGIN_MAX_PACKAGE_SIZE:-52428800} +PIP_MIRROR_URL=${PIP_MIRROR_URL:-} +PLUGIN_INSTALLED_PATH=${PLUGIN_INSTALLED_PATH:-plugin} +PLUGIN_MEDIA_CACHE_PATH=${PLUGIN_MEDIA_CACHE_PATH:-assets} +PLUGIN_PACKAGE_CACHE_PATH=${PLUGIN_PACKAGE_CACHE_PATH:-plugin_packages} +PLUGIN_S3_ENDPOINT=${PLUGIN_S3_ENDPOINT:-} +PLUGIN_S3_USE_AWS=${PLUGIN_S3_USE_AWS:-false} +PLUGIN_S3_USE_AWS_MANAGED_IAM=${PLUGIN_S3_USE_AWS_MANAGED_IAM:-false} +PLUGIN_S3_USE_PATH_STYLE=${PLUGIN_S3_USE_PATH_STYLE:-false} +PLUGIN_SENTRY_DSN=${PLUGIN_SENTRY_DSN:-} +PLUGIN_SENTRY_ENABLED=${PLUGIN_SENTRY_ENABLED:-false} +PLUGIN_TENCENT_COS_REGION=${PLUGIN_TENCENT_COS_REGION:-} +PLUGIN_TENCENT_COS_SECRET_ID=${PLUGIN_TENCENT_COS_SECRET_ID:-} +PLUGIN_TENCENT_COS_SECRET_KEY=${PLUGIN_TENCENT_COS_SECRET_KEY:-} +PLUGIN_VOLCENGINE_TOS_ACCESS_KEY=${PLUGIN_VOLCENGINE_TOS_ACCESS_KEY:-} +PLUGIN_VOLCENGINE_TOS_ENDPOINT=${PLUGIN_VOLCENGINE_TOS_ENDPOINT:-} +PLUGIN_VOLCENGINE_TOS_REGION=${PLUGIN_VOLCENGINE_TOS_REGION:-} +PLUGIN_VOLCENGINE_TOS_SECRET_KEY=${PLUGIN_VOLCENGINE_TOS_SECRET_KEY:-} + +# Container variable mappings; keep these expressions unchanged in the copy. +ALIYUN_OSS_ACCESS_KEY_ID=${PLUGIN_ALIYUN_OSS_ACCESS_KEY_ID:-} +ALIYUN_OSS_ACCESS_KEY_SECRET=${PLUGIN_ALIYUN_OSS_ACCESS_KEY_SECRET:-} +ALIYUN_OSS_AUTH_VERSION=${PLUGIN_ALIYUN_OSS_AUTH_VERSION:-v4} +ALIYUN_OSS_ENDPOINT=${PLUGIN_ALIYUN_OSS_ENDPOINT:-} +ALIYUN_OSS_PATH=${PLUGIN_ALIYUN_OSS_PATH:-} +ALIYUN_OSS_REGION=${PLUGIN_ALIYUN_OSS_REGION:-} +AWS_ACCESS_KEY=${PLUGIN_AWS_ACCESS_KEY:-} +AWS_REGION=${PLUGIN_AWS_REGION:-} +AWS_SECRET_KEY=${PLUGIN_AWS_SECRET_KEY:-} +AZURE_BLOB_STORAGE_CONNECTION_STRING=${PLUGIN_AZURE_BLOB_STORAGE_CONNECTION_STRING:-} +AZURE_BLOB_STORAGE_CONTAINER_NAME=${PLUGIN_AZURE_BLOB_STORAGE_CONTAINER_NAME:-} +DB_DATABASE=${DB_PLUGIN_DATABASE:-dify_plugin} +DIFY_INNER_API_KEY=${PLUGIN_DIFY_INNER_API_KEY:-QaHbTe77CtuXmsfyhR7+vRjI/+XbV1AaFy691iy+kGDv2Jvy0/eAh8Y1} +DIFY_INNER_API_URL=${PLUGIN_DIFY_INNER_API_URL:-http://api:5001} +MAX_PLUGIN_PACKAGE_SIZE=${PLUGIN_MAX_PACKAGE_SIZE:-52428800} +PLUGIN_REMOTE_INSTALLING_HOST=${PLUGIN_DEBUGGING_HOST:-0.0.0.0} +PPROF_ENABLED=${PLUGIN_PPROF_ENABLED:-false} +PYTHON_ENV_INIT_TIMEOUT=${PLUGIN_PYTHON_ENV_INIT_TIMEOUT:-120} +S3_ENDPOINT=${PLUGIN_S3_ENDPOINT:-} +S3_USE_AWS=${PLUGIN_S3_USE_AWS:-false} +S3_USE_AWS_MANAGED_IAM=${PLUGIN_S3_USE_AWS_MANAGED_IAM:-false} +S3_USE_PATH_STYLE=${PLUGIN_S3_USE_PATH_STYLE:-false} +SENTRY_DSN=${PLUGIN_SENTRY_DSN:-} +SENTRY_ENABLED=${PLUGIN_SENTRY_ENABLED:-false} +SERVER_KEY=${PLUGIN_DAEMON_KEY:-lYkiYYT6owG+71oLerGzA7GXCgOT++6ovaezWAjpCjf+Sjc3ZtU+qUEi} +SERVER_PORT=${PLUGIN_DAEMON_PORT:-5002} +TENCENT_COS_REGION=${PLUGIN_TENCENT_COS_REGION:-} +TENCENT_COS_SECRET_ID=${PLUGIN_TENCENT_COS_SECRET_ID:-} +TENCENT_COS_SECRET_KEY=${PLUGIN_TENCENT_COS_SECRET_KEY:-} +VOLCENGINE_TOS_ACCESS_KEY=${PLUGIN_VOLCENGINE_TOS_ACCESS_KEY:-} +VOLCENGINE_TOS_ENDPOINT=${PLUGIN_VOLCENGINE_TOS_ENDPOINT:-} +VOLCENGINE_TOS_REGION=${PLUGIN_VOLCENGINE_TOS_REGION:-} +VOLCENGINE_TOS_SECRET_KEY=${PLUGIN_VOLCENGINE_TOS_SECRET_KEY:-} diff --git a/docker/envs/core-services/plugin-daemon.env.example b/docker/envs/core-services/plugin-daemon.env.example deleted file mode 100644 index b9b60168c6e..00000000000 --- a/docker/envs/core-services/plugin-daemon.env.example +++ /dev/null @@ -1,25 +0,0 @@ -# ------------------------------ -# Plugin Daemon Configuration -# ------------------------------ - -DB_PLUGIN_DATABASE=dify_plugin -PLUGIN_DAEMON_URL=http://plugin_daemon:5002 -PLUGIN_PPROF_ENABLED=false -PLUGIN_DIFY_INNER_API_URL=http://api:5001 -FORCE_VERIFYING_SIGNATURE=true -PLUGIN_STDIO_BUFFER_SIZE=1024 -PLUGIN_STDIO_MAX_BUFFER_SIZE=5242880 -PLUGIN_PYTHON_ENV_INIT_TIMEOUT=120 -PLUGIN_MAX_EXECUTION_TIMEOUT=600 -PLUGIN_DEBUGGING_HOST=0.0.0.0 -PLUGIN_DEBUGGING_PORT=5003 -PLUGIN_DAEMON_KEY=lYkiYYT6owG+71oLerGzA7GXCgOT++6ovaezWAjpCjf+Sjc3ZtU+qUEi -PLUGIN_DIFY_INNER_API_KEY=QaHbTe77CtuXmsfyhR7+vRjI/+XbV1AaFy691iy+kGDv2Jvy0/eAh8Y1 -PLUGIN_DAEMON_PORT=5002 -CELERY_WORKER_CLASS= -PLUGIN_STORAGE_TYPE=local -PLUGIN_STORAGE_LOCAL_ROOT=/app/storage -PLUGIN_WORKING_PATH=/app/storage/cwd -# Optional: set in docker/.env for Docker/WSL bind-mount permission errors (langgenius/dify#42433). -# UV_CACHE_DIR=/tmp/.uv-cache -PLUGIN_STORAGE_OSS_BUCKET= diff --git a/docker/envs/core-services/sandbox.defaults b/docker/envs/core-services/sandbox.defaults new file mode 100644 index 00000000000..45b1d9e312c --- /dev/null +++ b/docker/envs/core-services/sandbox.defaults @@ -0,0 +1,26 @@ +# Defaults for sandbox. Copy this file to sandbox.env to customize it. +# Edit the settings below in the copy; docker/.env takes precedence. +# Optional application settings are commented to retain their application defaults. + +SANDBOX_HTTP_PROXY=${SANDBOX_HTTP_PROXY:-http://ssrf_proxy:3128} +SANDBOX_HTTPS_PROXY=${SANDBOX_HTTPS_PROXY:-http://ssrf_proxy:3128} +SANDBOX_PORT=${SANDBOX_PORT:-8194} +PIP_MIRROR_URL=${PIP_MIRROR_URL:-} +# Use a strong key for your deployment, generated with `openssl rand -base64 42`. +# Set the same value for CODE_EXECUTION_API_KEY in the API/worker configuration. +SANDBOX_API_KEY=${SANDBOX_API_KEY:-dify-sandbox} +SANDBOX_GIN_MODE=${SANDBOX_GIN_MODE:-release} +SANDBOX_WORKER_TIMEOUT=${SANDBOX_WORKER_TIMEOUT:-15} +# Dify's code executor requests networking; false rejects those requests. +# Sandbox 0.2.15 on ARM64 can also fail with "operation not permitted" for +# requests with enable_network=false, even without network operations: +# the runtime needs epoll_pwait, which is only allowed with networking enabled. +SANDBOX_ENABLE_NETWORK=${SANDBOX_ENABLE_NETWORK:-true} + +# Container variable mappings; keep these expressions unchanged in the copy. +API_KEY=${SANDBOX_API_KEY:-dify-sandbox} +ENABLE_NETWORK=${SANDBOX_ENABLE_NETWORK:-true} +GIN_MODE=${SANDBOX_GIN_MODE:-release} +HTTPS_PROXY=${SANDBOX_HTTPS_PROXY:-http://ssrf_proxy:3128} +HTTP_PROXY=${SANDBOX_HTTP_PROXY:-http://ssrf_proxy:3128} +WORKER_TIMEOUT=${SANDBOX_WORKER_TIMEOUT:-15} diff --git a/docker/envs/core-services/sandbox.env.example b/docker/envs/core-services/sandbox.env.example deleted file mode 100644 index 5d4ee6614bd..00000000000 --- a/docker/envs/core-services/sandbox.env.example +++ /dev/null @@ -1,17 +0,0 @@ -# ------------------------------ -# Sandbox Configuration -# ------------------------------ - -SANDBOX_HTTP_PROXY=http://ssrf_proxy:3128 -SANDBOX_HTTPS_PROXY=http://ssrf_proxy:3128 -SANDBOX_PORT=8194 -PIP_MIRROR_URL= -SANDBOX_API_KEY=dify-sandbox -SANDBOX_GIN_MODE=release -SANDBOX_WORKER_TIMEOUT=15 -SANDBOX_ENABLE_NETWORK=true -SANDBOX_EXPIRED_RECORDS_CLEAN_GRACEFUL_PERIOD=21 -SANDBOX_EXPIRED_RECORDS_CLEAN_BATCH_SIZE=1000 -SANDBOX_EXPIRED_RECORDS_CLEAN_BATCH_MAX_INTERVAL=200 -SANDBOX_EXPIRED_RECORDS_RETENTION_DAYS=30 -SANDBOX_EXPIRED_RECORDS_CLEAN_TASK_LOCK_TTL=90000 diff --git a/docker/envs/core-services/shared.defaults b/docker/envs/core-services/shared.defaults index 1e63c8e9aba..d8d4dafa9e2 100644 --- a/docker/envs/core-services/shared.defaults +++ b/docker/envs/core-services/shared.defaults @@ -1,28 +1,567 @@ -# Docker-specific defaults, loaded before optional env files and docker/.env. -# Keep runtime defaults in the application or Compose; only Docker overrides belong here. -# Override these values in docker/.env or the matching envs/*.env file, not here. -CONSOLE_CORS_ALLOW_ORIGINS=* -TRIGGER_URL=http://localhost -ENDPOINT_URL_TEMPLATE=http://localhost/e/{hook_id} -MIGRATION_ENABLED=true -LOG_FILE=/app/logs/server.log -LOG_DATEFORMAT=%Y-%m-%d %H:%M:%S -CELERY_WORKER_AMOUNT=4 -DB_HOST=db_postgres -DB_PASSWORD=difyai123456 -REDIS_HOST=redis -REDIS_PASSWORD=difyai123456 -REDIS_KEEPALIVE=true -CELERY_BROKER_URL=redis://:${REDIS_PASSWORD:-difyai123456}@${REDIS_HOST:-redis}:${REDIS_PORT:-6379}/1 -OPENDAL_FS_ROOT=storage -VECTOR_STORE=weaviate -WEAVIATE_ENDPOINT=http://weaviate:8080 -WEAVIATE_API_KEY=WVF5YThaHlkYwhGUSmCRgsX3tD5ngdN8pkih -WEAVIATE_GRPC_ENDPOINT=grpc://weaviate:50051 -SSRF_PROXY_HTTP_URL=http://ssrf_proxy:3128 -SSRF_PROXY_HTTPS_URL=http://ssrf_proxy:3128 -PLUGIN_DAEMON_URL=http://plugin_daemon:5002 -PLUGIN_DAEMON_KEY=lYkiYYT6owG+71oLerGzA7GXCgOT++6ovaezWAjpCjf+Sjc3ZtU+qUEi -PLUGIN_MAX_PACKAGE_SIZE=52428800 -AGENT_BACKEND_BASE_URL=http://agent_backend:5050 -NEXT_PUBLIC_ENABLE_AGENT_V2=true +# Shared API, worker and Beat configuration. Copy this file to shared.env to customize it. +# Edit the copy; docker/.env takes precedence. Existing shared.env files remain supported. +# Active assignments preserve Docker defaults and explicit overrides, including empty values. +# Optional application/provider settings are commented examples: uncomment only what you need. +# Do not uncomment empty values or placeholders unless you intend to set them. +# Remove accidental empty overrides (especially CELERY_WORKER_AMOUNT=) to restore defaults. +# API process settings: api.defaults. Worker process settings: worker.defaults. +# Sentry mappings: api.defaults and worker.defaults. Web and plugin-daemon settings stay in their files. +# Shared secrets and token lifetimes: copy ../security.env.example to ../security.env. +# Set COMPOSE_PROFILES in docker/.env; Compose reads it before service env files. +# Set EXPOSE_NGINX_PORT in docker/.env; Compose reads published ports before service env files. +# Set EXPOSE_NGINX_SSL_PORT in docker/.env; Compose reads published ports before service env files. +# Set EXPOSE_PLUGIN_DEBUGGING_HOST in docker/.env; the API and daemon must use the published address. +# Set EXPOSE_PLUGIN_DEBUGGING_PORT in docker/.env; the API and daemon must use the published port. + +# ------------------------------ +# URLs, deployment and locale +# ------------------------------ +# CONSOLE_WEB_URL= +# SERVICE_API_URL= +TRIGGER_URL=${TRIGGER_URL-http://localhost} +# APP_WEB_URL= +# FILES_URL= +# INTERNAL_FILES_URL= +ENDPOINT_URL_TEMPLATE=${ENDPOINT_URL_TEMPLATE-http://localhost/e/{hook_id}} +# DEPLOY_ENV=PRODUCTION +# DEPLOYMENT_EDITION=COMMUNITY +MIGRATION_ENABLED=${MIGRATION_ENABLED-true} +# LANG=C.UTF-8 +# LC_ALL=C.UTF-8 +# PYTHONIOENCODING=utf-8 +# CHECK_UPDATE_URL=https://updates.dify.ai +# OPENAI_API_BASE=https://api.openai.com/v1 +# Nacos remote settings source HTTP timeouts (seconds). +# Bound how long requests to the Nacos endpoint wait before failing, so a slow or +# unresponsive Nacos server cannot stall API startup or token refresh. +# DIFY_ENV_NACOS_REQUEST_TIMEOUT=10.0 +# DIFY_ENV_NACOS_CONNECT_TIMEOUT=3.0 + +# ------------------------------ +# System features and account access +# ------------------------------ +# MARKETPLACE_ENABLED=true +# ENABLE_EMAIL_CODE_LOGIN=false +# EMAIL_CODE_LOGIN_TOKEN_EXPIRY_MINUTES=5 +# EMAIL_CODE_LOGIN_MAX_ATTEMPTS=5 +# ENABLE_EMAIL_PASSWORD_LOGIN=true +# ENABLE_SOCIAL_OAUTH_LOGIN=false +# Remove `collaboration` from COMPOSE_PROFILES to stop the dedicated websocket service. +# ENABLE_COLLABORATION_MODE=true +# ALLOW_REGISTER=false +# ALLOW_CREATE_WORKSPACE=false +# ENABLE_CHANGE_EMAIL=true +# ENABLE_TRIAL_APP=false +# ENABLE_EXPLORE_BANNER=false +# ENABLE_LEARN_APP=true +# ENABLE_STEP_BY_STEP_TOUR=false +# ENABLE_SKILL=true +# RBAC_ENABLED=false +# ENABLE_LICENSE_EXPIRY_NOTICE=true +# ACCESS_TOKEN_EXPIRE_MINUTES=60 +# REFRESH_TOKEN_EXPIRE_DAYS=30 +# INVITE_EXPIRY_HOURS=72 +# WEB_API_CORS_ALLOW_ORIGINS=* +CONSOLE_CORS_ALLOW_ORIGINS=${CONSOLE_CORS_ALLOW_ORIGINS-*} +# COOKIE_DOMAIN= + +# ------------------------------ +# API and Celery Docker defaults +# ------------------------------ +# Unset values use these Docker defaults; an explicit empty value remains an override. +SERVER_WORKER_CLASS=${SERVER_WORKER_CLASS-gevent} +GUNICORN_TIMEOUT=${GUNICORN_TIMEOUT-360} +CELERY_WORKER_AMOUNT=${CELERY_WORKER_AMOUNT-4} + +# ------------------------------ +# Celery broker and KnowledgeFS dispatch +# ------------------------------ +# Broker URL defaults/mappings live in api.defaults, worker.defaults and worker-beat.defaults. +# Set a common CELERY_BROKER_URL override in shared.env or docker/.env for every producer/consumer. +# Optional long-running KnowledgeFS Celery rollout: set identically in every API, worker and Beat +# process sharing this Redis broker. It must exceed the KnowledgeFS hard task limit (7230s by +# default). Keep unset to preserve Celery's existing default when this rollout is disabled. +# CELERY_BROKER_VISIBILITY_TIMEOUT=10800 +# CELERY_TASK_ANNOTATIONS=null +# CELERY_SENTINEL_PASSWORD= +# CELERY_BACKEND=redis +# CELERY_USE_SENTINEL=false +# CELERY_SENTINEL_MASTER_NAME= +# CELERY_SENTINEL_SOCKET_TIMEOUT=0.1 +# KNOWLEDGE_FS_BACKGROUND_WORKER_ENABLED=true +# KNOWLEDGE_FS_BACKGROUND_POLL_INTERVAL_SECONDS=5 + +# ------------------------------ +# Database and Redis connections +# ------------------------------ +DB_HOST=${DB_HOST-db_postgres} +DB_USERNAME=${DB_USERNAME-postgres} +DB_PASSWORD=${DB_PASSWORD-difyai123456} +DB_PORT=${DB_PORT-5432} +REDIS_HOST=${REDIS_HOST-redis} +REDIS_PASSWORD=${REDIS_PASSWORD-difyai123456} +REDIS_PORT=${REDIS_PORT-6379} +REDIS_KEEPALIVE=${REDIS_KEEPALIVE-true} + +# ------------------------------ +# Logging, traces and telemetry +# ------------------------------ +# LOG_LEVEL=INFO +# LOG_OUTPUT_FORMAT=text +LOG_FILE=${LOG_FILE-/app/logs/server.log} +# LOG_FILE_MAX_SIZE=20 +# LOG_FILE_BACKUP_COUNT=5 +LOG_DATEFORMAT=${LOG_DATEFORMAT-%Y-%m-%d %H:%M:%S} +# LOG_TZ=UTC +# DEBUG=false +# FLASK_DEBUG=false +# ENABLE_REQUEST_LOGGING=False +# Application-level Agent tracing; also enable the collector in dify-agent.env. +# OPS_AGENT_TRACE_ENABLED=false +# ENABLE_APPLICATION_TRACE_RECONCILE_TASK=true +# Shared by API, workers, and Beat. Keep separate from legacy ops_trace during upgrades. +# OPS_TRACE_QUEUE=ops_trace_v2 +# Attempts to send one trace while its provider is temporarily unavailable. +# OPS_TRACE_EXPORT_MAX_ATTEMPTS=6 +# Daily cleanup of trace payloads an interrupted delivery left in storage. +# ENABLE_TRACE_PAYLOAD_SWEEP_TASK=true +# ENABLE_OTEL=false +# OTLP_TRACE_ENDPOINT= +# OTLP_METRIC_ENDPOINT= +# OTLP_BASE_ENDPOINT=http://localhost:4318 +# OTEL_EXPORTER_OTLP_PROTOCOL= +# OTEL_EXPORTER_TYPE=otlp +# OTEL_SAMPLING_RATE=0.1 +# OTEL_BATCH_EXPORT_SCHEDULE_DELAY=5000 +# OTEL_MAX_QUEUE_SIZE=2048 +# OTEL_MAX_EXPORT_BATCH_SIZE=512 +# OTEL_METRIC_EXPORT_INTERVAL=60000 +# OTEL_BATCH_EXPORT_TIMEOUT=10000 +# OTEL_METRIC_EXPORT_TIMEOUT=30000 + +# ------------------------------ +# Uploads, parsing and package limits +# ------------------------------ +# These parser settings are for Dify; knowledge-fs.env configures the separate KnowledgeFS parser client. +# FILES_ACCESS_TIMEOUT=300 +# UPLOAD_FILE_SIZE_LIMIT=15 +# KNOWLEDGE_UPLOAD_FILE_SIZE_LIMIT_FOR_PAID_PLAN=15 +# UPLOAD_FILE_BATCH_LIMIT=5 +# UPLOAD_FILE_EXTENSION_BLACKLIST= +# SINGLE_CHUNK_ATTACHMENT_LIMIT=10 +# IMAGE_FILE_BATCH_LIMIT=10 +# ATTACHMENT_IMAGE_FILE_SIZE_LIMIT=2 +# ATTACHMENT_IMAGE_DOWNLOAD_TIMEOUT=60 +# ETL_TYPE=dify +# UNSTRUCTURED_API_URL= +# MULTIMODAL_SEND_FORMAT=base64 +# UPLOAD_IMAGE_FILE_SIZE_LIMIT=10 +# UPLOAD_VIDEO_FILE_SIZE_LIMIT=100 +# UPLOAD_AUDIO_FILE_SIZE_LIMIT=50 +# UPLOAD_SKILL_FILE_SIZE_LIMIT=50 +# SKILL_PACKAGE_MAX_UNCOMPRESSED_BYTES=209715200 +# SKILL_PACKAGE_MAX_ENTRIES=5000 +# SKILL_PACKAGE_MAX_SKILL_MD_BYTES=1048576 +# AGENT_PACKAGE_MAX_BYTES=536870912 +# AGENT_PACKAGE_MAX_MANIFEST_BYTES=5242880 +# AGENT_PACKAGE_MAX_ENTRIES=5000 +# APP_PACKAGE_MAX_WORKFLOW_TOOLS=20 +# SCARF_NO_ANALYTICS=true +# ANNOTATION_IMPORT_FILE_SIZE_LIMIT=2 +# ANNOTATION_IMPORT_MAX_RECORDS=10000 +# ANNOTATION_IMPORT_MIN_RECORDS=1 +# ANNOTATION_IMPORT_RATE_LIMIT_PER_MINUTE=5 +# ANNOTATION_IMPORT_RATE_LIMIT_PER_HOUR=20 +# ANNOTATION_IMPORT_MAX_CONCURRENT=5 + +# ------------------------------ +# Plugin clients and marketplace +# ------------------------------ +# Set PLUGIN_DAEMON_KEY in docker/.env; the daemon, API, workers, and Agent must use the same key. +# Set PLUGIN_DIFY_INNER_API_KEY in docker/.env; the API, daemon, workers, Agent and KnowledgeFS share it. +# Keep the PLUGIN_DAEMON_KEY expression below unchanged in the copy. +PLUGIN_DAEMON_URL=${PLUGIN_DAEMON_URL-http://plugin_daemon:5002} +PLUGIN_DAEMON_KEY=${PLUGIN_DAEMON_KEY-lYkiYYT6owG+71oLerGzA7GXCgOT++6ovaezWAjpCjf+Sjc3ZtU+qUEi} +PLUGIN_MAX_PACKAGE_SIZE=${PLUGIN_MAX_PACKAGE_SIZE-52428800} +# PLUGIN_MAX_FILE_SIZE=52428800 +# PLUGIN_MODEL_SCHEMA_CACHE_TTL=3600 +# PLUGIN_MODEL_PROVIDERS_CACHE_ENABLED=true +# PLUGIN_MODEL_PROVIDERS_CACHE_TTL=86400 +# Comma-separated marketplace plugin IDs whose latest versions are installed for newly registered users. +# Example: langgenius/openai,langgenius/gemini +# NEW_USER_DEFAULT_PLUGIN_IDS= +# Comma-separated model_type:provider:model entries assigned after default plugins finish installing. +# Example: llm:langgenius/openai/openai:gpt-4o-mini,text-embedding:langgenius/openai/openai:text-embedding-3-small +# NEW_USER_DEFAULT_MODELS= +# POSITION_TOOL_PINS= +# POSITION_TOOL_INCLUDES= +# POSITION_TOOL_EXCLUDES= +# POSITION_PROVIDER_PINS= +# POSITION_PROVIDER_INCLUDES= +# POSITION_PROVIDER_EXCLUDES= +# CREATORS_PLATFORM_FEATURES_ENABLED=true +# CREATORS_PLATFORM_API_URL=https://creators.dify.ai +# CREATORS_PLATFORM_OAUTH_CLIENT_ID= + +# ------------------------------ +# Agent clients and execution +# ------------------------------ +AGENT_BACKEND_BASE_URL=${AGENT_BACKEND_BASE_URL-http://agent_backend:5050} +NEXT_PUBLIC_ENABLE_AGENT_V2=${NEXT_PUBLIC_ENABLE_AGENT_V2-true} +# Agent backend client settings shared by API and worker processes. +# AGENT_BACKEND_STREAM_READ_TIMEOUT_SECONDS=30 +# AGENT_BACKEND_STREAM_MAX_RECONNECTS=3 +# Client timeout for Agent backend calls that may carry a Home Snapshot transfer. +# Must stay above DIFY_AGENT_ENTERPRISE_SANDBOX_SNAPSHOT_TIMEOUT on the Agent backend. +# AGENT_BACKEND_HOME_SNAPSHOT_TIMEOUT_SECONDS=45 +# Independent E2B accounting, collected only by API Celery Beat/workers. +# Uses OPS_TRACE_QUEUE; business binding operations never call metering. +# Enable after migrations. START_AT is an immutable, whole-second UTC T0. +# AGENT_SANDBOX_METERING_ENABLED=false +# AGENT_SANDBOX_METERING_PROJECT_ID= +# AGENT_SANDBOX_METERING_START_AT= +# AGENT_SANDBOX_METERING_INTERVAL_SECONDS=60 +# APP_DEFAULT_ACTIVE_REQUESTS=0 +# APP_MAX_ACTIVE_REQUESTS=0 +# Outer execution limit for Agent Apps. Agent Backend may stop the internal +# model/tool loop sooner when DIFY_AGENT_RUN_TIMEOUT_SECONDS is lower. +# APP_MAX_EXECUTION_TIME=3600 + +# ------------------------------ +# Code execution and template transformation +# ------------------------------ +# CODE_EXECUTION_ENDPOINT=http://sandbox:8194 +# CODE_EXECUTION_SSL_VERIFY=True +# CODE_EXECUTION_POOL_MAX_CONNECTIONS=100 +# CODE_EXECUTION_POOL_MAX_KEEPALIVE_CONNECTIONS=20 +# CODE_EXECUTION_POOL_KEEPALIVE_EXPIRY=5.0 +# CODE_MAX_NUMBER=9223372036854775807 +# CODE_MIN_NUMBER=-9223372036854775808 +# CODE_MAX_DEPTH=5 +# CODE_MAX_PRECISION=20 +# CODE_MAX_STRING_LENGTH=400000 +# CODE_MAX_STRING_ARRAY_LENGTH=30 +# CODE_MAX_OBJECT_ARRAY_LENGTH=30 +# CODE_MAX_NUMBER_ARRAY_LENGTH=1000 +# CODE_EXECUTION_CONNECT_TIMEOUT=10 +# CODE_EXECUTION_READ_TIMEOUT=60 +# CODE_EXECUTION_WRITE_TIMEOUT=10 +# TEMPLATE_TRANSFORM_MAX_LENGTH=400000 + +# ------------------------------ +# Workflow and HTTP request limits +# ------------------------------ +# WORKFLOW_MAX_EXECUTION_STEPS=500 +# Outer execution limit for Workflow and Chatflow callers. Agent Backend may +# stop the internal model/tool loop sooner when its run timeout is lower. +# To allow all Agent call paths to run for one hour, set this value, +# APP_MAX_EXECUTION_TIME, and DIFY_AGENT_RUN_TIMEOUT_SECONDS to at least 3600. +# WORKFLOW_MAX_EXECUTION_TIME=3600 +# WORKFLOW_CALL_MAX_DEPTH=5 +# MAX_VARIABLE_SIZE=204800 +# WORKFLOW_GENERATOR_NODE_BUILDER_MAX_WORKERS=6 +# WORKFLOW_FILE_UPLOAD_LIMIT=10 +# GRAPH_ENGINE_MAX_WORKERS=10 +# API_TOOL_DEFAULT_CONNECT_TIMEOUT=10 +# API_TOOL_DEFAULT_READ_TIMEOUT=60 +# WEBHOOK_REQUEST_BODY_MAX_SIZE=10485760 +# HTTP_REQUEST_NODE_MAX_BINARY_SIZE=10485760 +# HTTP_REQUEST_NODE_MAX_TEXT_SIZE=1048576 +# HTTP_REQUEST_NODE_SSL_VERIFY=True +# HTTP_REQUEST_MAX_CONNECT_TIMEOUT=10 +# HTTP_REQUEST_MAX_READ_TIMEOUT=600 +# HTTP_REQUEST_MAX_WRITE_TIMEOUT=600 +# DSL_EXPORT_ENCRYPT_DATASET_ID=true +# DATASET_MAX_SEGMENTS_PER_REQUEST=0 + +# ------------------------------ +# Workflow storage and retention +# ------------------------------ +# WORKFLOW_NODE_EXECUTION_STORAGE=rdbms +# CORE_WORKFLOW_EXECUTION_REPOSITORY=core.repositories.sqlalchemy_workflow_execution_repository.SQLAlchemyWorkflowExecutionRepository +# CORE_WORKFLOW_NODE_EXECUTION_REPOSITORY=core.repositories.sqlalchemy_workflow_node_execution_repository.SQLAlchemyWorkflowNodeExecutionRepository +# API_WORKFLOW_RUN_REPOSITORY=repositories.sqlalchemy_api_workflow_run_repository.DifyAPISQLAlchemyWorkflowRunRepository +# API_WORKFLOW_NODE_EXECUTION_REPOSITORY=repositories.sqlalchemy_api_workflow_node_execution_repository.DifyAPISQLAlchemyWorkflowNodeExecutionRepository +# WORKFLOW_LOG_CLEANUP_ENABLED=false +# WORKFLOW_LOG_RETENTION_DAYS=30 +# WORKFLOW_LOG_CLEANUP_BATCH_SIZE=100 +# WORKFLOW_LOG_CLEANUP_SPECIFIC_WORKFLOW_IDS= +# ALIYUN_SLS_ENDPOINT= +# ALIYUN_SLS_REGION= +# ALIYUN_SLS_PROJECT_NAME= +# ALIYUN_SLS_LOGSTORE_TTL=365 +# LOGSTORE_DUAL_WRITE_ENABLED=false +# LOGSTORE_DUAL_READ_ENABLED=true +# LOGSTORE_ENABLE_PUT_GRAPH_FIELD=true + +# ------------------------------ +# Scheduled tasks and queue monitoring (API, workers and Beat) +# ------------------------------ +# ENABLE_CLEAN_EMBEDDING_CACHE_TASK=false +# ENABLE_CLEAN_UNUSED_DATASETS_TASK=false +# ENABLE_CREATE_TIDB_SERVERLESS_TASK=false +# ENABLE_UPDATE_TIDB_SERVERLESS_STATUS_TASK=false +# ENABLE_CLEAN_MESSAGES=false +# ENABLE_WORKFLOW_RUN_CLEANUP_TASK=false +# ENABLE_MAIL_CLEAN_DOCUMENT_NOTIFY_TASK=false +# ENABLE_DATASETS_QUEUE_MONITOR=false +# ENABLE_CHECK_UPGRADABLE_PLUGIN_TASK=true +# ENABLE_WORKFLOW_SCHEDULE_POLLER_TASK=true +# WORKFLOW_SCHEDULE_POLLER_INTERVAL=1 +# WORKFLOW_SCHEDULE_POLLER_BATCH_SIZE=100 +# WORKFLOW_SCHEDULE_MAX_DISPATCH_PER_TICK=0 +# TENANT_ISOLATED_TASK_CONCURRENCY=1 +# QUEUE_MONITOR_THRESHOLD=200 +# QUEUE_MONITOR_ALERT_EMAILS= +# QUEUE_MONITOR_INTERVAL=30 +# HUMAN_INPUT_IM_EVENT_TRANSPORT_MODE=WEBHOOK +# ENABLE_HUMAN_INPUT_TIMEOUT_TASK=true +# HUMAN_INPUT_TIMEOUT_TASK_INTERVAL=1 +# ENABLE_CONVERSATION_CLEANUP_TASK=true +# CONVERSATION_CLEANUP_TASK_INTERVAL=5 +# CONVERSATION_CLEANUP_BATCH_SIZE=100 +# Expired sandbox records are cleaned by API/Celery, not the sandbox container. +# SANDBOX_EXPIRED_RECORDS_CLEAN_GRACEFUL_PERIOD=21 +# SANDBOX_EXPIRED_RECORDS_CLEAN_BATCH_SIZE=1000 +# SANDBOX_EXPIRED_RECORDS_CLEAN_BATCH_MAX_INTERVAL=200 +# SANDBOX_EXPIRED_RECORDS_RETENTION_DAYS=30 +# SANDBOX_EXPIRED_RECORDS_CLEAN_TASK_LOCK_TTL=90000 +# CREATE_TIDB_SERVICE_JOB_ENABLED=false +# MAX_SUBMIT_COUNT=100 + +# ------------------------------ +# Email and Notion integrations +# ------------------------------ +# NOTION_INTEGRATION_TYPE=public +# NOTION_CLIENT_ID= +# MAIL_TYPE=resend +# MAIL_DEFAULT_SEND_FROM= +# RESEND_API_URL=https://api.resend.com +# SMTP_SERVER= +# SMTP_PORT=465 +# SMTP_USERNAME= +# SMTP_USE_TLS=true +# SMTP_OPPORTUNISTIC_TLS=false +# SMTP_LOCAL_HOSTNAME= + +# ------------------------------ +# SSRF proxy clients +# ------------------------------ +# Client timeouts/pool limits and Squid settings: ../infrastructure/ssrf-proxy.defaults. +SSRF_PROXY_HTTP_URL=${SSRF_PROXY_HTTP_URL-http://ssrf_proxy:3128} +SSRF_PROXY_HTTPS_URL=${SSRF_PROXY_HTTPS_URL-http://ssrf_proxy:3128} + +# ------------------------------ +# Storage and encryption providers +# ------------------------------ +# Application storage settings below also serve workers; plugin-daemon.defaults configures plugin storage separately. +# STORAGE_TYPE=opendal +# KEY_PROVIDER_TYPE=local +# OPENDAL_SCHEME=fs +OPENDAL_FS_ROOT=${OPENDAL_FS_ROOT-storage} +# AZURE_KEYVAULT_VAULT_URL= +# AZURE_KEYVAULT_KEY_SIZE=2048 +# AZURE_KEYVAULT_ROTATION_INTERVAL_DAYS= + +# ------------------------------ +# Amazon S3 and archive storage +# ------------------------------ +# S3_ENDPOINT= +# S3_REGION=us-east-1 +# S3_BUCKET_NAME=difyai +# S3_ACCESS_KEY= +# S3_SECRET_KEY= +# S3_ADDRESS_STYLE=auto +# S3_USE_AWS_MANAGED_IAM=false +# ARCHIVE_STORAGE_ENABLED=false +# ARCHIVE_STORAGE_ENDPOINT= +# ARCHIVE_STORAGE_ARCHIVE_BUCKET= +# ARCHIVE_STORAGE_EXPORT_BUCKET= +# ARCHIVE_STORAGE_REGION=auto +# ARCHIVE_STORAGE_ACCESS_KEY= +# ARCHIVE_STORAGE_SECRET_KEY= +# ARCHIVE_STORAGE_ADDRESS_STYLE=path + +# ------------------------------ +# Azure Blob and Google Cloud Storage +# ------------------------------ +# AZURE_BLOB_ACCOUNT_URL=https://.blob.core.windows.net +# AZURE_BLOB_ACCOUNT_NAME=difyai +# AZURE_BLOB_ACCOUNT_KEY=difyai +# AZURE_BLOB_CONTAINER_NAME=difyai-container +# GOOGLE_STORAGE_BUCKET_NAME=your-bucket-name +# GOOGLE_STORAGE_SERVICE_ACCOUNT_JSON_BASE64= + +# ------------------------------ +# Aliyun OSS, Tencent COS and Volcengine TOS +# ------------------------------ +# ALIYUN_OSS_BUCKET_NAME=your-bucket-name +# ALIYUN_OSS_ENDPOINT=https://oss-ap-southeast-1-internal.aliyuncs.com +# ALIYUN_OSS_REGION=ap-southeast-1 +# ALIYUN_OSS_ACCESS_KEY=your-access-key +# ALIYUN_OSS_SECRET_KEY=your-secret-key +# ALIYUN_OSS_AUTH_VERSION=v4 +# ALIYUN_OSS_PATH=your-path +# ALIYUN_CLOUDBOX_ID=your-cloudbox-id +# TENCENT_COS_BUCKET_NAME=your-bucket-name +# TENCENT_COS_REGION=your-region +# TENCENT_COS_SECRET_KEY=your-secret-key +# TENCENT_COS_SECRET_ID=your-secret-id +# TENCENT_COS_SCHEME=your-scheme +# TENCENT_COS_CUSTOM_DOMAIN=your-custom-domain +# VOLCENGINE_TOS_BUCKET_NAME=your-bucket-name +# VOLCENGINE_TOS_ENDPOINT=your-server-url +# VOLCENGINE_TOS_REGION=your-region +# VOLCENGINE_TOS_SECRET_KEY=your-secret-key +# VOLCENGINE_TOS_ACCESS_KEY=your-access-key + +# ------------------------------ +# OCI, Huawei OBS, Baidu OBS and Supabase storage +# ------------------------------ +# OCI_ENDPOINT=https://your-object-storage-namespace.compat.objectstorage.us-ashburn-1.oraclecloud.com +# OCI_BUCKET_NAME=your-bucket-name +# OCI_REGION=us-ashburn-1 +# OCI_ACCESS_KEY=your-access-key +# OCI_SECRET_KEY=your-secret-key +# HUAWEI_OBS_BUCKET_NAME=your-bucket-name +# HUAWEI_OBS_SERVER=your-server-url +# HUAWEI_OBS_SECRET_KEY=your-secret-key +# HUAWEI_OBS_ACCESS_KEY=your-access-key +# HUAWEI_OBS_PATH_STYLE=false +# BAIDU_OBS_BUCKET_NAME=your-bucket-name +# BAIDU_OBS_ENDPOINT=your-server-url +# BAIDU_OBS_SECRET_KEY=your-secret-key +# BAIDU_OBS_ACCESS_KEY=your-access-key +# SUPABASE_URL=your-server-url +# SUPABASE_BUCKET_NAME=your-bucket-name +# SUPABASE_API_KEY=your-access-key + +# ------------------------------ +# ClickZetta storage and vector search +# ------------------------------ +# CLICKZETTA_USERNAME= +# CLICKZETTA_PASSWORD= +# CLICKZETTA_INSTANCE= +# CLICKZETTA_SERVICE=api.clickzetta.com +# CLICKZETTA_WORKSPACE=quick_start +# CLICKZETTA_VCLUSTER=default_ap +# CLICKZETTA_SCHEMA=dify +# CLICKZETTA_BATCH_SIZE=100 +# CLICKZETTA_ENABLE_INVERTED_INDEX=true +# CLICKZETTA_ANALYZER_TYPE=chinese +# CLICKZETTA_ANALYZER_MODE=smart +# CLICKZETTA_VECTOR_DISTANCE_FUNCTION=cosine_distance +# CLICKZETTA_VOLUME_TYPE=user +# CLICKZETTA_VOLUME_NAME= +# CLICKZETTA_VOLUME_TABLE_PREFIX=dataset_ +# CLICKZETTA_VOLUME_DIFY_PREFIX=dify_km + +# ------------------------------ +# Vector store selection and Docker connection defaults +# ------------------------------ +# Provider client options live in ../vectorstores/{weaviate,qdrant,oceanbase,pgvector}.defaults +# and ../vectorstores/milvus.env.example. Their .env overrides reach API, workers and Beat. +# KnowledgeFS uses the API vector bridge, not its own vector provider connection. +VECTOR_STORE=${VECTOR_STORE-weaviate} +# Prefix used to create collection names in the vector database. +# VECTOR_INDEX_NAME_PREFIX=Vector_index +WEAVIATE_ENDPOINT=${WEAVIATE_ENDPOINT-http://weaviate:8080} +WEAVIATE_API_KEY=${WEAVIATE_API_KEY-WVF5YThaHlkYwhGUSmCRgsX3tD5ngdN8pkih} +WEAVIATE_GRPC_ENDPOINT=${WEAVIATE_GRPC_ENDPOINT-grpc://weaviate:50051} + +# ------------------------------ +# TiDB and TiDB on Qdrant +# ------------------------------ +# TIDB_VECTOR_HOST=tidb +# TIDB_VECTOR_PORT=4000 +# TIDB_VECTOR_USER= +# TIDB_VECTOR_DATABASE=dify +# TIDB_VECTOR_ENABLE_FULLTEXT_SEARCH=false +# TIDB_ON_QDRANT_URL=http://127.0.0.1 +# TIDB_ON_QDRANT_CLIENT_TIMEOUT=20 +# TIDB_ON_QDRANT_GRPC_ENABLED=false +# TIDB_ON_QDRANT_GRPC_PORT=6334 +# TIDB_ON_QDRANT_ESTIMATED_STORAGE_LIMITS_MB=sandbox:60,professional:6400,team:25600 +# TIDB_API_URL=http://127.0.0.1 +# TIDB_IAM_API_URL=http://127.0.0.1 +# TIDB_REGION=regions/aws-us-east-1 +# TIDB_PROJECT_ID=dify +# TIDB_SPEND_LIMIT=100 + +# ------------------------------ +# Tencent and Baidu vector databases +# ------------------------------ +# TENCENT_VECTOR_DB_URL=http://127.0.0.1 +# TENCENT_VECTOR_DB_TIMEOUT=30 +# TENCENT_VECTOR_DB_USERNAME=dify +# TENCENT_VECTOR_DB_DATABASE=dify +# TENCENT_VECTOR_DB_SHARD=1 +# TENCENT_VECTOR_DB_REPLICAS=2 +# TENCENT_VECTOR_DB_ENABLE_HYBRID_SEARCH=false +# BAIDU_VECTOR_DB_ENDPOINT=http://127.0.0.1:5287 +# BAIDU_VECTOR_DB_CONNECTION_TIMEOUT_MS=30000 +# BAIDU_VECTOR_DB_ACCOUNT=root +# BAIDU_VECTOR_DB_DATABASE=dify +# BAIDU_VECTOR_DB_SHARD=1 +# BAIDU_VECTOR_DB_REPLICAS=3 +# BAIDU_VECTOR_DB_INVERTED_INDEX_ANALYZER=DEFAULT_ANALYZER +# BAIDU_VECTOR_DB_INVERTED_INDEX_PARSER_MODE=COARSE_MODE +# BAIDU_VECTOR_DB_AUTO_BUILD_ROW_COUNT_INCREMENT=500 +# BAIDU_VECTOR_DB_AUTO_BUILD_ROW_COUNT_INCREMENT_RATIO=0.05 +# BAIDU_VECTOR_DB_REBUILD_INDEX_TIMEOUT_IN_SECONDS=300 + +# ------------------------------ +# Lindorm, Huawei Cloud, Upstash and Tablestore +# ------------------------------ +# LINDORM_URL=http://localhost:30070 +# LINDORM_USERNAME=admin +# LINDORM_USING_UGC=True +# LINDORM_QUERY_TIMEOUT=1 +# HUAWEI_CLOUD_HOSTS=https://127.0.0.1:9200 +# HUAWEI_CLOUD_USER=admin +# UPSTASH_VECTOR_URL=https://xxx-vector.upstash.io +# TABLESTORE_ENDPOINT=https://instance-name.cn-hangzhou.ots.aliyuncs.com +# TABLESTORE_INSTANCE_NAME=instance-name +# TABLESTORE_NORMALIZE_FULLTEXT_BM25_SCORE=false + +# ------------------------------ +# AnalyticDB, Alibaba Cloud MySQL and Relyt +# ------------------------------ +# ANALYTICDB_REGION_ID=cn-hangzhou +# ANALYTICDB_INSTANCE_ID=gp-ab123456 +# ANALYTICDB_ACCOUNT=testaccount +# ANALYTICDB_NAMESPACE=dify +# ANALYTICDB_HOST=gp-test.aliyuncs.com +# ANALYTICDB_PORT=5432 +# ANALYTICDB_MIN_CONNECTION=1 +# ANALYTICDB_MAX_CONNECTION=5 +# ALIBABACLOUD_MYSQL_HOST=127.0.0.1 +# ALIBABACLOUD_MYSQL_PORT=3306 +# ALIBABACLOUD_MYSQL_USER=root +# ALIBABACLOUD_MYSQL_DATABASE=dify +# ALIBABACLOUD_MYSQL_MAX_CONNECTION=5 +# ALIBABACLOUD_MYSQL_HNSW_M=6 +# RELYT_HOST=db +# RELYT_PORT=5432 +# RELYT_USER=postgres +# RELYT_DATABASE=postgres + +# ------------------------------ +# VikingDB and Hologres +# ------------------------------ +# VIKINGDB_REGION=cn-shanghai +# VIKINGDB_HOST=api-vikingdb.xxx.volces.com +# VIKINGDB_SCHEME=http +# VIKINGDB_CONNECTION_TIMEOUT=30 +# VIKINGDB_SOCKET_TIMEOUT=30 +# HOLOGRES_HOST= +# HOLOGRES_PORT=80 +# HOLOGRES_DATABASE= +# HOLOGRES_ACCESS_KEY_ID= +# HOLOGRES_ACCESS_KEY_SECRET= +# HOLOGRES_SCHEMA=public +# HOLOGRES_TOKENIZER=jieba +# HOLOGRES_DISTANCE_METHOD=Cosine +# HOLOGRES_BASE_QUANTIZATION_TYPE=rabitq +# HOLOGRES_MAX_DEGREE=64 +# HOLOGRES_EF_CONSTRUCTION=400 diff --git a/docker/envs/core-services/shared.env.example b/docker/envs/core-services/shared.env.example deleted file mode 100644 index 7fdb12c8ab2..00000000000 --- a/docker/envs/core-services/shared.env.example +++ /dev/null @@ -1,574 +0,0 @@ -# ------------------------------ -# Shared API/Worker Configuration -# ------------------------------ - -CONSOLE_WEB_URL= -SERVICE_API_URL= -TRIGGER_URL=http://localhost -HUMAN_INPUT_IM_EVENT_TRANSPORT_MODE=WEBHOOK -APP_WEB_URL= -FILES_URL= -INTERNAL_FILES_URL= -LANG=C.UTF-8 -LC_ALL=C.UTF-8 -PYTHONIOENCODING=utf-8 -CHECK_UPDATE_URL=https://updates.dify.ai -OPENAI_API_BASE=https://api.openai.com/v1 -MIGRATION_ENABLED=true -FILES_ACCESS_TIMEOUT=300 -# System Features -MARKETPLACE_ENABLED=true -ENABLE_EMAIL_CODE_LOGIN=false -EMAIL_CODE_LOGIN_TOKEN_EXPIRY_MINUTES=5 -EMAIL_CODE_LOGIN_MAX_ATTEMPTS=5 -ENABLE_EMAIL_PASSWORD_LOGIN=true -ENABLE_SOCIAL_OAUTH_LOGIN=false -# Remove `collaboration` from COMPOSE_PROFILES to stop the dedicated websocket service. -ENABLE_COLLABORATION_MODE=true -ALLOW_REGISTER=false -ALLOW_CREATE_WORKSPACE=false -ENABLE_CHANGE_EMAIL=true -ENABLE_TRIAL_APP=false -ENABLE_EXPLORE_BANNER=false -ENABLE_LEARN_APP=true -ENABLE_STEP_BY_STEP_TOUR=false -ENABLE_SKILL=true -RBAC_ENABLED=false -ENABLE_LICENSE_EXPIRY_NOTICE=true -CELERY_BROKER_URL=redis://:difyai123456@redis:6379/1 -# Optional long-running KnowledgeFS Celery rollout: set identically in every API, worker and Beat -# process sharing this Redis broker. It must exceed the KnowledgeFS hard task limit (7230s by -# default). Keep unset to preserve Celery's existing default when this rollout is disabled. -# CELERY_BROKER_VISIBILITY_TIMEOUT=10800 -# KNOWLEDGE_FS_BACKGROUND_WORKER_ENABLED=true -# KNOWLEDGE_FS_BACKGROUND_POLL_INTERVAL_SECONDS=5 -CELERY_TASK_ANNOTATIONS=null -AZURE_BLOB_ACCOUNT_URL=https://.blob.core.windows.net -SUPABASE_URL=your-server-url -TIDB_ON_QDRANT_URL=http://127.0.0.1 -TIDB_ON_QDRANT_API_KEY=dify -TIDB_API_URL=http://127.0.0.1 -TIDB_IAM_API_URL=http://127.0.0.1 -TIDB_REGION=regions/aws-us-east-1 -TIDB_PROJECT_ID=dify -TIDB_SPEND_LIMIT=100 -TENCENT_VECTOR_DB_URL=http://127.0.0.1 -TENCENT_VECTOR_DB_API_KEY=dify -LINDORM_URL=http://localhost:30070 -LINDORM_USERNAME=admin -UPSTASH_VECTOR_URL=https://xxx-vector.upstash.io -UPLOAD_FILE_SIZE_LIMIT=15 -KNOWLEDGE_UPLOAD_FILE_SIZE_LIMIT_FOR_PAID_PLAN=15 -UPLOAD_FILE_BATCH_LIMIT=5 -UPLOAD_FILE_EXTENSION_BLACKLIST= -SINGLE_CHUNK_ATTACHMENT_LIMIT=10 -IMAGE_FILE_BATCH_LIMIT=10 -ATTACHMENT_IMAGE_FILE_SIZE_LIMIT=2 -ATTACHMENT_IMAGE_DOWNLOAD_TIMEOUT=60 -ETL_TYPE=dify -UNSTRUCTURED_API_URL= -MULTIMODAL_SEND_FORMAT=base64 -UPLOAD_IMAGE_FILE_SIZE_LIMIT=10 -UPLOAD_VIDEO_FILE_SIZE_LIMIT=100 -UPLOAD_AUDIO_FILE_SIZE_LIMIT=50 -UPLOAD_SKILL_FILE_SIZE_LIMIT=50 -SKILL_PACKAGE_MAX_UNCOMPRESSED_BYTES=209715200 -SKILL_PACKAGE_MAX_ENTRIES=5000 -SKILL_PACKAGE_MAX_SKILL_MD_BYTES=1048576 -AGENT_PACKAGE_MAX_BYTES=536870912 -AGENT_PACKAGE_MAX_MANIFEST_BYTES=5242880 -AGENT_PACKAGE_MAX_ENTRIES=5000 -APP_PACKAGE_MAX_WORKFLOW_TOOLS=20 -API_SENTRY_DSN= -API_SENTRY_TRACES_SAMPLE_RATE=1.0 -API_SENTRY_PROFILES_SAMPLE_RATE=1.0 -WEB_SENTRY_DSN= -PLUGIN_SENTRY_ENABLED=false -PLUGIN_SENTRY_DSN= -NOTION_INTEGRATION_TYPE=public -RESEND_API_URL=https://api.resend.com -SSRF_PROXY_HTTP_URL=http://ssrf_proxy:3128 -SSRF_PROXY_HTTPS_URL=http://ssrf_proxy:3128 -PGDATA=/var/lib/postgresql/data/pgdata -PLUGIN_MAX_PACKAGE_SIZE=52428800 -PLUGIN_MAX_FILE_SIZE=52428800 -PLUGIN_MODEL_SCHEMA_CACHE_TTL=3600 -PLUGIN_MODEL_PROVIDERS_CACHE_ENABLED=true -PLUGIN_MODEL_PROVIDERS_CACHE_TTL=86400 -# Comma-separated marketplace plugin IDs whose latest versions are installed for newly registered users. -# Example: langgenius/openai,langgenius/gemini -NEW_USER_DEFAULT_PLUGIN_IDS= -# Comma-separated model_type:provider:model entries assigned after default plugins finish installing. -# Example: llm:langgenius/openai/openai:gpt-4o-mini,text-embedding:langgenius/openai/openai:text-embedding-3-small -NEW_USER_DEFAULT_MODELS= -ENDPOINT_URL_TEMPLATE=http://localhost/e/{hook_id} -LOG_LEVEL=INFO -LOG_OUTPUT_FORMAT=text -LOG_FILE=/app/logs/server.log -LOG_FILE_MAX_SIZE=20 -LOG_FILE_BACKUP_COUNT=5 -LOG_DATEFORMAT=%Y-%m-%d %H:%M:%S -LOG_TZ=UTC -DEBUG=false -FLASK_DEBUG=false -ENABLE_REQUEST_LOGGING=False -# Application-level Agent tracing; also enable the collector in dify-agent.env. -OPS_AGENT_TRACE_ENABLED=false -ENABLE_APPLICATION_TRACE_RECONCILE_TASK=true -# Shared by API, workers, and Beat. Keep separate from legacy ops_trace during upgrades. -OPS_TRACE_QUEUE=ops_trace_v2 -# Attempts to send one trace while its provider is temporarily unavailable. -OPS_TRACE_EXPORT_MAX_ATTEMPTS=6 -# Daily cleanup of trace payloads an interrupted delivery left in storage. -ENABLE_TRACE_PAYLOAD_SWEEP_TASK=true -WORKFLOW_LOG_CLEANUP_ENABLED=false -WORKFLOW_LOG_RETENTION_DAYS=30 -WORKFLOW_LOG_CLEANUP_BATCH_SIZE=100 -WORKFLOW_LOG_CLEANUP_SPECIFIC_WORKFLOW_IDS= -EXPOSE_PLUGIN_DEBUGGING_HOST=localhost -EXPOSE_PLUGIN_DEBUGGING_PORT=5003 -DEPLOY_ENV=PRODUCTION -DEPLOYMENT_EDITION=COMMUNITY -ACCESS_TOKEN_EXPIRE_MINUTES=60 -REFRESH_TOKEN_EXPIRE_DAYS=30 -APP_DEFAULT_ACTIVE_REQUESTS=0 -APP_MAX_ACTIVE_REQUESTS=0 -# Agent backend client settings shared by API and worker processes. -AGENT_BACKEND_STREAM_READ_TIMEOUT_SECONDS=30 -AGENT_BACKEND_STREAM_MAX_RECONNECTS=3 -# Client timeout for Agent backend calls that may carry a Home Snapshot transfer. -# Must stay above DIFY_AGENT_ENTERPRISE_SANDBOX_SNAPSHOT_TIMEOUT on the Agent backend. -AGENT_BACKEND_HOME_SNAPSHOT_TIMEOUT_SECONDS=45 -# Independent E2B accounting, collected only by API Celery Beat/workers. -# Uses OPS_TRACE_QUEUE; business binding operations never call metering. -# Enable after migrations. START_AT is an immutable, whole-second UTC T0. -AGENT_SANDBOX_METERING_ENABLED=false -AGENT_SANDBOX_METERING_PROJECT_ID= -AGENT_SANDBOX_METERING_START_AT= -AGENT_SANDBOX_METERING_INTERVAL_SECONDS=60 -# Outer execution limit for Agent Apps. Agent Backend may stop the internal -# model/tool loop sooner when DIFY_AGENT_RUN_TIMEOUT_SECONDS is lower. -APP_MAX_EXECUTION_TIME=3600 -DIFY_BIND_ADDRESS=0.0.0.0 -DIFY_PORT=5001 -SERVER_WORKER_AMOUNT=1 -SERVER_WORKER_CLASS=gevent -SERVER_WORKER_CONNECTIONS=10 -API_WEBSOCKET_WORKER_AMOUNT=1 -API_WEBSOCKET_WORKER_CLASS=geventwebsocket.gunicorn.workers.GeventWebSocketWorker -API_WEBSOCKET_WORKER_CONNECTIONS=1000 -API_WEBSOCKET_GUNICORN_TIMEOUT=360 -CELERY_SENTINEL_PASSWORD= -S3_ACCESS_KEY= -S3_SECRET_KEY= -ARCHIVE_STORAGE_ACCESS_KEY= -ARCHIVE_STORAGE_SECRET_KEY= -AZURE_BLOB_ACCOUNT_KEY=difyai -ALIYUN_OSS_ACCESS_KEY=your-access-key -ALIYUN_OSS_SECRET_KEY=your-secret-key -TENCENT_COS_SECRET_KEY=your-secret-key -TENCENT_COS_SECRET_ID=your-secret-id -OCI_ACCESS_KEY=your-access-key -OCI_SECRET_KEY=your-secret-key -HUAWEI_OBS_SECRET_KEY=your-secret-key -HUAWEI_OBS_ACCESS_KEY=your-access-key -VOLCENGINE_TOS_SECRET_KEY=your-secret-key -VOLCENGINE_TOS_ACCESS_KEY=your-access-key -BAIDU_OBS_SECRET_KEY=your-secret-key -BAIDU_OBS_ACCESS_KEY=your-access-key -SUPABASE_API_KEY=your-access-key -ALIBABACLOUD_MYSQL_PASSWORD=difyai123456 -RELYT_PASSWORD=difyai123456 -LINDORM_PASSWORD=admin -LINDORM_USING_UGC=True -LINDORM_QUERY_TIMEOUT=1 -HUAWEI_CLOUD_PASSWORD=admin -UPSTASH_VECTOR_TOKEN=dify -TABLESTORE_ACCESS_KEY_ID=xxx -TABLESTORE_ACCESS_KEY_SECRET=xxx -TABLESTORE_NORMALIZE_FULLTEXT_BM25_SCORE=false -CLICKZETTA_PASSWORD= -CLICKZETTA_INSTANCE= -CLICKZETTA_SERVICE=api.clickzetta.com -CLICKZETTA_WORKSPACE=quick_start -CLICKZETTA_VCLUSTER=default_ap -CLICKZETTA_SCHEMA=dify -CLICKZETTA_BATCH_SIZE=100 -CLICKZETTA_ENABLE_INVERTED_INDEX=true -CLICKZETTA_ANALYZER_TYPE=chinese -CLICKZETTA_ANALYZER_MODE=smart -UNSTRUCTURED_API_KEY= -SCARF_NO_ANALYTICS=true -PLUGIN_BASED_TOKEN_COUNTING_ENABLED=false -NOTION_CLIENT_SECRET= -NOTION_CLIENT_ID= -NOTION_INTERNAL_SECRET= -MAIL_TYPE=resend -MAIL_DEFAULT_SEND_FROM= -RESEND_API_KEY=your-resend-api-key -SMTP_SERVER= -SMTP_PORT=465 -SMTP_USERNAME= -SMTP_PASSWORD= -SMTP_USE_TLS=true -SMTP_OPPORTUNISTIC_TLS=false -SMTP_LOCAL_HOSTNAME= -SENDGRID_API_KEY= -INVITE_EXPIRY_HOURS=72 -RESET_PASSWORD_TOKEN_EXPIRY_MINUTES=5 -EMAIL_REGISTER_TOKEN_EXPIRY_MINUTES=5 -CHANGE_EMAIL_TOKEN_EXPIRY_MINUTES=5 -OWNER_TRANSFER_TOKEN_EXPIRY_MINUTES=5 -CODE_EXECUTION_ENDPOINT=http://sandbox:8194 -CODE_EXECUTION_API_KEY=dify-sandbox -CODE_EXECUTION_SSL_VERIFY=True -CODE_EXECUTION_POOL_MAX_CONNECTIONS=100 -CODE_EXECUTION_POOL_MAX_KEEPALIVE_CONNECTIONS=20 -CODE_EXECUTION_POOL_KEEPALIVE_EXPIRY=5.0 -CODE_MAX_NUMBER=9223372036854775807 -CODE_MIN_NUMBER=-9223372036854775808 -CODE_MAX_DEPTH=5 -CODE_MAX_PRECISION=20 -CODE_MAX_STRING_LENGTH=400000 -CODE_MAX_STRING_ARRAY_LENGTH=30 -CODE_MAX_OBJECT_ARRAY_LENGTH=30 -CODE_MAX_NUMBER_ARRAY_LENGTH=1000 -CODE_EXECUTION_CONNECT_TIMEOUT=10 -CODE_EXECUTION_READ_TIMEOUT=60 -CODE_EXECUTION_WRITE_TIMEOUT=10 -TEMPLATE_TRANSFORM_MAX_LENGTH=400000 -WORKFLOW_MAX_EXECUTION_STEPS=500 -# Outer execution limit for Workflow and Chatflow callers. Agent Backend may -# stop the internal model/tool loop sooner when its run timeout is lower. -# To allow all Agent call paths to run for one hour, set this value, -# APP_MAX_EXECUTION_TIME, and DIFY_AGENT_RUN_TIMEOUT_SECONDS to at least 3600. -WORKFLOW_MAX_EXECUTION_TIME=3600 -WORKFLOW_CALL_MAX_DEPTH=5 -MAX_VARIABLE_SIZE=204800 -WORKFLOW_GENERATOR_NODE_BUILDER_MAX_WORKERS=6 -WORKFLOW_FILE_UPLOAD_LIMIT=10 -GRAPH_ENGINE_MAX_WORKERS=10 -ALIYUN_SLS_ACCESS_KEY_ID= -ALIYUN_SLS_ACCESS_KEY_SECRET= -WEBHOOK_REQUEST_BODY_MAX_SIZE=10485760 -# Bare API default. Compose enables this for its API services behind Nginx. -# Override Compose through docker/.env, the shell, or a Compose override, not this env_file. -# See docker/README.md#client-ip-and-trusted-proxies before exposing the API directly. -RESPECT_XFORWARD_HEADERS_ENABLED=false -SSRF_HTTP_PORT=3128 -SSRF_COREDUMP_DIR=/var/spool/squid -SSRF_DEFAULT_TIME_OUT=5 -SSRF_DEFAULT_CONNECT_TIME_OUT=5 -SSRF_DEFAULT_READ_TIME_OUT=5 -SSRF_DEFAULT_WRITE_TIME_OUT=5 -SSRF_POOL_MAX_CONNECTIONS=100 -SSRF_POOL_MAX_KEEPALIVE_CONNECTIONS=20 -SSRF_POOL_KEEPALIVE_EXPIRY=5.0 -PLUGIN_AWS_ACCESS_KEY= -PLUGIN_AWS_SECRET_KEY= -PLUGIN_AWS_REGION= -PLUGIN_TENCENT_COS_SECRET_KEY= -PLUGIN_TENCENT_COS_SECRET_ID= -PLUGIN_ALIYUN_OSS_ACCESS_KEY_ID= -PLUGIN_ALIYUN_OSS_ACCESS_KEY_SECRET= -PLUGIN_VOLCENGINE_TOS_ACCESS_KEY= -PLUGIN_VOLCENGINE_TOS_SECRET_KEY= -OTLP_API_KEY= -OTEL_EXPORTER_OTLP_PROTOCOL= -OTEL_EXPORTER_TYPE=otlp -OTEL_SAMPLING_RATE=0.1 -OTEL_BATCH_EXPORT_SCHEDULE_DELAY=5000 -OTEL_MAX_QUEUE_SIZE=2048 -OTEL_MAX_EXPORT_BATCH_SIZE=512 -OTEL_METRIC_EXPORT_INTERVAL=60000 -OTEL_BATCH_EXPORT_TIMEOUT=10000 -OTEL_METRIC_EXPORT_TIMEOUT=30000 -QUEUE_MONITOR_THRESHOLD=200 -QUEUE_MONITOR_ALERT_EMAILS= -QUEUE_MONITOR_INTERVAL=30 -SWAGGER_UI_ENABLED=false -SWAGGER_UI_PATH=/swagger-ui.html -OPENAPI_ENABLED=false -OPENAPI_CORS_ALLOW_ORIGINS= -OPENAPI_KNOWN_CLIENT_IDS=difyctl -OPENAPI_RATE_LIMIT_PER_TOKEN=60 -DEVICE_FLOW_APPROVE_RATE_LIMIT_PER_HOUR=10 -ENABLE_OAUTH_BEARER=false -DSL_EXPORT_ENCRYPT_DATASET_ID=true -DATASET_MAX_SEGMENTS_PER_REQUEST=0 -ENABLE_CLEAN_EMBEDDING_CACHE_TASK=false -ENABLE_CLEAN_UNUSED_DATASETS_TASK=false -ENABLE_CREATE_TIDB_SERVERLESS_TASK=false -ENABLE_UPDATE_TIDB_SERVERLESS_STATUS_TASK=false -ENABLE_CLEAN_MESSAGES=false -ENABLE_WORKFLOW_RUN_CLEANUP_TASK=false -ENABLE_MAIL_CLEAN_DOCUMENT_NOTIFY_TASK=false -ENABLE_DATASETS_QUEUE_MONITOR=false -ENABLE_CHECK_UPGRADABLE_PLUGIN_TASK=true -ENABLE_WORKFLOW_SCHEDULE_POLLER_TASK=true -WORKFLOW_SCHEDULE_POLLER_INTERVAL=1 -WORKFLOW_SCHEDULE_POLLER_BATCH_SIZE=100 -WORKFLOW_SCHEDULE_MAX_DISPATCH_PER_TICK=0 -TENANT_ISOLATED_TASK_CONCURRENCY=1 -ANNOTATION_IMPORT_FILE_SIZE_LIMIT=2 -ANNOTATION_IMPORT_MAX_RECORDS=10000 -ANNOTATION_IMPORT_MIN_RECORDS=1 -ANNOTATION_IMPORT_RATE_LIMIT_PER_MINUTE=5 -ANNOTATION_IMPORT_RATE_LIMIT_PER_HOUR=20 -ANNOTATION_IMPORT_MAX_CONCURRENT=5 -CREATORS_PLATFORM_FEATURES_ENABLED=true -CREATORS_PLATFORM_API_URL=https://creators.dify.ai -CREATORS_PLATFORM_OAUTH_CLIENT_ID= -TIDB_VECTOR_DATABASE=dify -ALIBABACLOUD_MYSQL_HOST=127.0.0.1 -ALIBABACLOUD_MYSQL_PORT=3306 -ALIBABACLOUD_MYSQL_USER=root -ALIBABACLOUD_MYSQL_DATABASE=dify -ALIBABACLOUD_MYSQL_MAX_CONNECTION=5 -ALIBABACLOUD_MYSQL_HNSW_M=6 -RELYT_DATABASE=postgres -TENCENT_VECTOR_DB_DATABASE=dify -BAIDU_VECTOR_DB_DATABASE=dify -EXPOSE_PLUGIN_DAEMON_PORT=5002 -GUNICORN_TIMEOUT=360 -# Must stay above the idle connection reuse window of the HTTP clients that -# pool connections to this server. Node's global fetch (undici) keeps a socket -# idle for 4s before dropping it, so a shorter value here lets the server close -# a connection the client still considers reusable, which surfaces as -# intermittent ECONNRESET on server-side rendering. 0 disables keep-alive. -GUNICORN_KEEP_ALIVE=10 -CELERY_WORKER_AMOUNT= -CELERY_AUTO_SCALE=false -CELERY_MAX_WORKERS= -CELERY_MIN_WORKERS= -API_TOOL_DEFAULT_CONNECT_TIMEOUT=10 -API_TOOL_DEFAULT_READ_TIMEOUT=60 -CELERY_BACKEND=redis -CELERY_USE_SENTINEL=false -CELERY_SENTINEL_MASTER_NAME= -CELERY_SENTINEL_SOCKET_TIMEOUT=0.1 -WEB_API_CORS_ALLOW_ORIGINS=* -CONSOLE_CORS_ALLOW_ORIGINS=* -WEBSOCKET_MAX_HTTP_BUFFER_SIZE=10485760 -COOKIE_DOMAIN= -OPENDAL_SCHEME=fs -OPENDAL_FS_ROOT=storage -CLICKZETTA_VOLUME_TYPE=user -CLICKZETTA_VOLUME_NAME= -CLICKZETTA_VOLUME_TABLE_PREFIX=dataset_ -CLICKZETTA_VOLUME_DIFY_PREFIX=dify_km -S3_ENDPOINT= -S3_REGION=us-east-1 -S3_BUCKET_NAME=difyai -S3_ADDRESS_STYLE=auto -S3_USE_AWS_MANAGED_IAM=false -ARCHIVE_STORAGE_ENABLED=false -ARCHIVE_STORAGE_ENDPOINT= -ARCHIVE_STORAGE_ARCHIVE_BUCKET= -ARCHIVE_STORAGE_EXPORT_BUCKET= -ARCHIVE_STORAGE_REGION=auto -ARCHIVE_STORAGE_ADDRESS_STYLE=path -AZURE_BLOB_ACCOUNT_NAME=difyai -AZURE_BLOB_CONTAINER_NAME=difyai-container -AZURE_KEYVAULT_VAULT_URL= -AZURE_KEYVAULT_KEY_SIZE=2048 -AZURE_KEYVAULT_ROTATION_INTERVAL_DAYS= -GOOGLE_STORAGE_BUCKET_NAME=your-bucket-name -GOOGLE_STORAGE_SERVICE_ACCOUNT_JSON_BASE64= -ALIYUN_OSS_BUCKET_NAME=your-bucket-name -ALIYUN_OSS_ENDPOINT=https://oss-ap-southeast-1-internal.aliyuncs.com -ALIYUN_OSS_REGION=ap-southeast-1 -ALIYUN_OSS_AUTH_VERSION=v4 -ALIYUN_OSS_PATH=your-path -ALIYUN_CLOUDBOX_ID=your-cloudbox-id -TENCENT_COS_BUCKET_NAME=your-bucket-name -TENCENT_COS_REGION=your-region -TENCENT_COS_SCHEME=your-scheme -TENCENT_COS_CUSTOM_DOMAIN=your-custom-domain -OCI_ENDPOINT=https://your-object-storage-namespace.compat.objectstorage.us-ashburn-1.oraclecloud.com -OCI_BUCKET_NAME=your-bucket-name -OCI_REGION=us-ashburn-1 -HUAWEI_OBS_BUCKET_NAME=your-bucket-name -HUAWEI_OBS_SERVER=your-server-url -HUAWEI_OBS_PATH_STYLE=false -VOLCENGINE_TOS_BUCKET_NAME=your-bucket-name -VOLCENGINE_TOS_ENDPOINT=your-server-url -VOLCENGINE_TOS_REGION=your-region -BAIDU_OBS_BUCKET_NAME=your-bucket-name -BAIDU_OBS_ENDPOINT=your-server-url -SUPABASE_BUCKET_NAME=your-bucket-name -TENCENT_VECTOR_DB_TIMEOUT=30 -TENCENT_VECTOR_DB_USERNAME=dify -TENCENT_VECTOR_DB_SHARD=1 -TENCENT_VECTOR_DB_REPLICAS=2 -TENCENT_VECTOR_DB_ENABLE_HYBRID_SEARCH=false -BAIDU_VECTOR_DB_ENDPOINT=http://127.0.0.1:5287 -BAIDU_VECTOR_DB_CONNECTION_TIMEOUT_MS=30000 -BAIDU_VECTOR_DB_ACCOUNT=root -BAIDU_VECTOR_DB_API_KEY=dify -BAIDU_VECTOR_DB_SHARD=1 -BAIDU_VECTOR_DB_REPLICAS=3 -BAIDU_VECTOR_DB_INVERTED_INDEX_ANALYZER=DEFAULT_ANALYZER -BAIDU_VECTOR_DB_INVERTED_INDEX_PARSER_MODE=COARSE_MODE -BAIDU_VECTOR_DB_AUTO_BUILD_ROW_COUNT_INCREMENT=500 -BAIDU_VECTOR_DB_AUTO_BUILD_ROW_COUNT_INCREMENT_RATIO=0.05 -BAIDU_VECTOR_DB_REBUILD_INDEX_TIMEOUT_IN_SECONDS=300 -HUAWEI_CLOUD_HOSTS=https://127.0.0.1:9200 -HUAWEI_CLOUD_USER=admin -WORKFLOW_NODE_EXECUTION_STORAGE=rdbms -CORE_WORKFLOW_EXECUTION_REPOSITORY=core.repositories.sqlalchemy_workflow_execution_repository.SQLAlchemyWorkflowExecutionRepository -CORE_WORKFLOW_NODE_EXECUTION_REPOSITORY=core.repositories.sqlalchemy_workflow_node_execution_repository.SQLAlchemyWorkflowNodeExecutionRepository -API_WORKFLOW_RUN_REPOSITORY=repositories.sqlalchemy_api_workflow_run_repository.DifyAPISQLAlchemyWorkflowRunRepository -API_WORKFLOW_NODE_EXECUTION_REPOSITORY=repositories.sqlalchemy_api_workflow_node_execution_repository.DifyAPISQLAlchemyWorkflowNodeExecutionRepository -ALIYUN_SLS_ENDPOINT= -ALIYUN_SLS_REGION= -ALIYUN_SLS_PROJECT_NAME= -ALIYUN_SLS_LOGSTORE_TTL=365 -LOGSTORE_DUAL_WRITE_ENABLED=false -LOGSTORE_DUAL_READ_ENABLED=true -LOGSTORE_ENABLE_PUT_GRAPH_FIELD=true -HTTP_REQUEST_NODE_MAX_BINARY_SIZE=10485760 -HTTP_REQUEST_NODE_MAX_TEXT_SIZE=1048576 -HTTP_REQUEST_NODE_SSL_VERIFY=True -HTTP_REQUEST_MAX_CONNECT_TIMEOUT=10 -HTTP_REQUEST_MAX_READ_TIMEOUT=600 -HTTP_REQUEST_MAX_WRITE_TIMEOUT=600 -PLUGIN_INSTALLED_PATH=plugin -PLUGIN_PACKAGE_CACHE_PATH=plugin_packages -PLUGIN_MEDIA_CACHE_PATH=assets -PLUGIN_S3_USE_AWS=false -PLUGIN_S3_USE_AWS_MANAGED_IAM=false -PLUGIN_S3_ENDPOINT= -PLUGIN_S3_USE_PATH_STYLE=false -PLUGIN_AZURE_BLOB_STORAGE_CONTAINER_NAME= -PLUGIN_AZURE_BLOB_STORAGE_CONNECTION_STRING= -PLUGIN_TENCENT_COS_REGION= -PLUGIN_ALIYUN_OSS_REGION= -PLUGIN_ALIYUN_OSS_ENDPOINT= -PLUGIN_ALIYUN_OSS_AUTH_VERSION=v4 -PLUGIN_ALIYUN_OSS_PATH= -PLUGIN_VOLCENGINE_TOS_ENDPOINT= -PLUGIN_VOLCENGINE_TOS_REGION= -ENABLE_OTEL=false -OTLP_TRACE_ENDPOINT= -OTLP_METRIC_ENDPOINT= -# Prefix used to create collection name in vector database -OTLP_BASE_ENDPOINT=http://localhost:4318 -WEAVIATE_GRPC_ENDPOINT=grpc://weaviate:50051 -ANALYTICDB_KEY_ID=your-ak -ANALYTICDB_KEY_SECRET=your-sk -ANALYTICDB_REGION_ID=cn-hangzhou -ANALYTICDB_INSTANCE_ID=gp-ab123456 -ANALYTICDB_ACCOUNT=testaccount -ANALYTICDB_PASSWORD=testpassword -ANALYTICDB_NAMESPACE=dify -ANALYTICDB_NAMESPACE_PASSWORD=difypassword -ANALYTICDB_HOST=gp-test.aliyuncs.com -ANALYTICDB_PORT=5432 -ANALYTICDB_MIN_CONNECTION=1 -ANALYTICDB_MAX_CONNECTION=5 -TIDB_VECTOR_HOST=tidb -TIDB_VECTOR_PORT=4000 -TIDB_VECTOR_USER= -TIDB_VECTOR_PASSWORD= -TIDB_VECTOR_ENABLE_FULLTEXT_SEARCH=false -TIDB_ON_QDRANT_CLIENT_TIMEOUT=20 -TIDB_ON_QDRANT_GRPC_ENABLED=false -TIDB_ON_QDRANT_GRPC_PORT=6334 -TIDB_ON_QDRANT_ESTIMATED_STORAGE_LIMITS_MB=sandbox:60,professional:6400,team:25600 -TIDB_PUBLIC_KEY=dify -TIDB_PRIVATE_KEY=dify -RELYT_HOST=db -RELYT_PORT=5432 -RELYT_USER=postgres -VIKINGDB_ACCESS_KEY=your-ak -VIKINGDB_SECRET_KEY=your-sk -VIKINGDB_REGION=cn-shanghai -VIKINGDB_HOST=api-vikingdb.xxx.volces.com -VIKINGDB_SCHEME=http -VIKINGDB_CONNECTION_TIMEOUT=30 -VIKINGDB_SOCKET_TIMEOUT=30 -TABLESTORE_ENDPOINT=https://instance-name.cn-hangzhou.ots.aliyuncs.com -TABLESTORE_INSTANCE_NAME=instance-name -CLICKZETTA_USERNAME= -CLICKZETTA_VECTOR_DISTANCE_FUNCTION=cosine_distance -COMPOSE_PROFILES=${VECTOR_STORE:-weaviate},${DB_TYPE:-postgresql},collaboration -EXPOSE_NGINX_PORT=80 -EXPOSE_NGINX_SSL_PORT=443 -POSITION_TOOL_PINS= -POSITION_TOOL_INCLUDES= -POSITION_TOOL_EXCLUDES= -POSITION_PROVIDER_PINS= -POSITION_PROVIDER_INCLUDES= -POSITION_PROVIDER_EXCLUDES= -CREATE_TIDB_SERVICE_JOB_ENABLED=false -MAX_SUBMIT_COUNT=100 - -# Vector Store Configuration -STORAGE_TYPE=opendal -KEY_PROVIDER_TYPE=local -VECTOR_STORE=weaviate -VECTOR_INDEX_NAME_PREFIX=Vector_index -WEAVIATE_ENDPOINT=http://weaviate:8080 -WEAVIATE_API_KEY=WVF5YThaHlkYwhGUSmCRgsX3tD5ngdN8pkih -WEAVIATE_TOKENIZATION=word -OCEANBASE_VECTOR_HOST=oceanbase -OCEANBASE_VECTOR_PORT=2881 -OCEANBASE_VECTOR_USER=root@test -OCEANBASE_VECTOR_PASSWORD=difyai123456 -OCEANBASE_VECTOR_DATABASE=test -OCEANBASE_ENABLE_HYBRID_SEARCH=false -OCEANBASE_FULLTEXT_PARSER=ik -SEEKDB_MEMORY_LIMIT=2G -QDRANT_URL=http://qdrant:6333 -QDRANT_API_KEY=difyai123456 -QDRANT_CLIENT_TIMEOUT=20 -QDRANT_GRPC_ENABLED=false -QDRANT_GRPC_PORT=6334 -QDRANT_REPLICATION_FACTOR=1 -MILVUS_URI=http://host.docker.internal:19530 -MILVUS_TOKEN= -MILVUS_USER= -MILVUS_PASSWORD= -MILVUS_ANALYZER_PARAMS= -PGVECTOR_HOST=pgvector -PGVECTOR_PORT=5432 -PGVECTOR_USER=postgres -PGVECTOR_PASSWORD=difyai123456 -PGVECTOR_DATABASE=dify -PGVECTOR_MIN_CONNECTION=1 -PGVECTOR_MAX_CONNECTION=5 -PGVECTOR_PG_BIGM=false -PGVECTOR_PG_BIGM_VERSION=1.2-20240606 - -# Hologres Configuration -HOLOGRES_HOST= -HOLOGRES_PORT=80 -HOLOGRES_DATABASE= -HOLOGRES_ACCESS_KEY_ID= -HOLOGRES_ACCESS_KEY_SECRET= -HOLOGRES_SCHEMA=public -HOLOGRES_TOKENIZER=jieba -HOLOGRES_DISTANCE_METHOD=Cosine -HOLOGRES_BASE_QUANTIZATION_TYPE=rabitq -HOLOGRES_MAX_DEGREE=64 -HOLOGRES_EF_CONSTRUCTION=400 - -# Milvus API Configuration -MILVUS_DATABASE= -MILVUS_ENABLE_HYBRID_SEARCH=False - -# Human Input Task Configuration -ENABLE_HUMAN_INPUT_TIMEOUT_TASK=true -HUMAN_INPUT_TIMEOUT_TASK_INTERVAL=1 - -# Conversation cleanup recovery task -ENABLE_CONVERSATION_CLEANUP_TASK=true -CONVERSATION_CLEANUP_TASK_INTERVAL=5 -CONVERSATION_CLEANUP_BATCH_SIZE=100 - -# Nacos remote settings source HTTP timeouts (seconds). -# Bound how long requests to the Nacos endpoint wait before failing, so a slow or -# unresponsive Nacos server cannot stall API startup or token refresh. -DIFY_ENV_NACOS_REQUEST_TIMEOUT=10.0 -DIFY_ENV_NACOS_CONNECT_TIMEOUT=3.0 diff --git a/docker/envs/core-services/web.defaults b/docker/envs/core-services/web.defaults new file mode 100644 index 00000000000..3dc65ac59b8 --- /dev/null +++ b/docker/envs/core-services/web.defaults @@ -0,0 +1,60 @@ +# Defaults for web. Copy this file to web.env to customize it. +# Edit the settings below in the copy; docker/.env takes precedence. +# Optional application settings are commented to retain their application defaults. + +CONSOLE_API_URL=${CONSOLE_API_URL:-} +SERVER_CONSOLE_API_URL=${SERVER_CONSOLE_API_URL:-http://api:5001} +APP_API_URL=${APP_API_URL:-} +# Optional Cloud WebApp document admission. Enable only after the edge proxy +# overwrites both X-Dify-Webapp-Client-Ip and X-Dify-Webapp-Proxy-Secret, Web is +# private to that proxy, and the target Gateway enforces /api using trusted IPs. +# Use the complete internal Gateway endpoint, never a direct Core API URL. +# Example: http://gateway-public:8080/api/webapp/access-mode +# WEBAPP_ACCESS_PREFLIGHT_URL= +# Provision outside Git; must match the edge-injected secret. Never NEXT_PUBLIC. +# WEBAPP_ACCESS_PREFLIGHT_PROXY_SECRET= +WEB_SENTRY_DSN=${WEB_SENTRY_DSN:-} +NEXT_PUBLIC_SOCKET_URL=${NEXT_PUBLIC_SOCKET_URL:-ws://localhost} +EXPERIMENTAL_ENABLE_VINEXT=${EXPERIMENTAL_ENABLE_VINEXT:-false} +LOOP_NODE_MAX_COUNT=${LOOP_NODE_MAX_COUNT:-100} +MAX_TOOLS_NUM=${MAX_TOOLS_NUM:-10} +MAX_PARALLEL_LIMIT=${MAX_PARALLEL_LIMIT:-10} +MAX_ITERATIONS_NUM=${MAX_ITERATIONS_NUM:-99} +TEXT_GENERATION_TIMEOUT_MS=${TEXT_GENERATION_TIMEOUT_MS:-60000} +# WORKFLOW_GENERATION_TIMEOUT_MS=180000 +# ALLOW_INLINE_STYLES=false +# Example: ()!*&()!*&-。.;;+=— +# MARKDOWN_FORM_FIELD_NAME_EXTRA_CHARS= +# Maximum length of Markdown form field names. +# MARKDOWN_FORM_FIELD_NAME_MAX_LENGTH=128 +ALLOW_UNSAFE_DATA_SCHEME=${ALLOW_UNSAFE_DATA_SCHEME:-false} +MAX_TREE_DEPTH=${MAX_TREE_DEPTH:-50} +MARKETPLACE_API_URL=${MARKETPLACE_API_URL:-https://marketplace.dify.ai} +MARKETPLACE_URL=${MARKETPLACE_URL:-https://marketplace.dify.ai} +INDEXING_MAX_SEGMENTATION_TOKENS_LENGTH=${INDEXING_MAX_SEGMENTATION_TOKENS_LENGTH:-4000} +ALLOW_EMBED=${ALLOW_EMBED:-false} +AMPLITUDE_API_KEY=${AMPLITUDE_API_KEY:-} +TURNSTILE_SITE_KEY=${TURNSTILE_SITE_KEY:-} +# COOKIEYES_SITE_KEY= +ENABLE_WEBSITE_JINAREADER=${ENABLE_WEBSITE_JINAREADER:-true} +ENABLE_WEBSITE_FIRECRAWL=${ENABLE_WEBSITE_FIRECRAWL:-true} +ENABLE_WEBSITE_WATERCRAWL=${ENABLE_WEBSITE_WATERCRAWL:-true} +# NEXT_PUBLIC_ENABLE_SINGLE_DOLLAR_LATEX=false +# NEXT_PUBLIC_ENABLE_FEATURE_PREVIEW=true + +# Enable HITL IM, Contacts, V2 editing/migration and approval UI. Defaults to false. +# When false, retain legacy V1 behavior and disable V2 entry points without converting saved data. +# Restart the Web service and reload the page after changing this setting. +# NEXT_PUBLIC_ENABLE_HITL_IM=false +# Show Access Token settings to workspace owners. Disabled by default. +# NEXT_PUBLIC_ENABLE_ACCESS_TOKEN=false +NEXT_PUBLIC_ENABLE_AGENT_V2=${NEXT_PUBLIC_ENABLE_AGENT_V2:-true} +# NEXT_PUBLIC_ENABLE_AGENT_V2_IN_CHATFLOW=false +NEXT_PUBLIC_COOKIE_DOMAIN=${NEXT_PUBLIC_COOKIE_DOMAIN:-} +# NEXT_PUBLIC_BATCH_CONCURRENCY=5 +CSP_WHITELIST=${CSP_WHITELIST:-} +TOP_K_MAX_VALUE=${TOP_K_MAX_VALUE:-10} +NEXT_TELEMETRY_DISABLED=${NEXT_TELEMETRY_DISABLED:-0} + +# Container variable mappings; keep these expressions unchanged in the copy. +SENTRY_DSN=${WEB_SENTRY_DSN:-} diff --git a/docker/envs/core-services/web.env.example b/docker/envs/core-services/web.env.example deleted file mode 100644 index e2a507ce6e5..00000000000 --- a/docker/envs/core-services/web.env.example +++ /dev/null @@ -1,56 +0,0 @@ -# ------------------------------ -# Web Configuration -# ------------------------------ - -CONSOLE_API_URL= -SERVER_CONSOLE_API_URL=http://api:5001 -APP_API_URL= -# Optional Cloud WebApp document admission. Enable only after the edge proxy -# overwrites both X-Dify-Webapp-Client-Ip and X-Dify-Webapp-Proxy-Secret, Web is -# private to that proxy, and the target Gateway enforces /api using trusted IPs. -# Use the complete internal Gateway endpoint, never a direct Core API URL. -# Example: http://gateway-public:8080/api/webapp/access-mode -WEBAPP_ACCESS_PREFLIGHT_URL= -# Provision outside Git; must match the edge-injected secret. Never NEXT_PUBLIC. -WEBAPP_ACCESS_PREFLIGHT_PROXY_SECRET= -SENTRY_DSN= -NEXT_PUBLIC_SOCKET_URL=ws://localhost -EXPERIMENTAL_ENABLE_VINEXT=false -LOOP_NODE_MAX_COUNT=100 -MAX_TOOLS_NUM=10 -MAX_PARALLEL_LIMIT=10 -MAX_ITERATIONS_NUM=99 -TEXT_GENERATION_TIMEOUT_MS=60000 -WORKFLOW_GENERATION_TIMEOUT_MS=180000 -ALLOW_INLINE_STYLES=false -# Example: ()!*&()!*&-。.;;+=— -MARKDOWN_FORM_FIELD_NAME_EXTRA_CHARS= -# Maximum length of Markdown form field names. -MARKDOWN_FORM_FIELD_NAME_MAX_LENGTH=128 -ALLOW_UNSAFE_DATA_SCHEME=false -MAX_TREE_DEPTH=50 -MARKETPLACE_API_URL=https://marketplace.dify.ai -MARKETPLACE_URL=https://marketplace.dify.ai -INDEXING_MAX_SEGMENTATION_TOKENS_LENGTH=4000 -ALLOW_EMBED=false -AMPLITUDE_API_KEY= -TURNSTILE_SITE_KEY= -COOKIEYES_SITE_KEY= -ENABLE_WEBSITE_JINAREADER=true -ENABLE_WEBSITE_FIRECRAWL=true -ENABLE_WEBSITE_WATERCRAWL=true -NEXT_PUBLIC_ENABLE_SINGLE_DOLLAR_LATEX=false -NEXT_PUBLIC_ENABLE_FEATURE_PREVIEW=true - -# Enable HITL IM, Contacts, V2 editing/migration and approval UI. Defaults to false. -# When false, retain legacy V1 behavior and disable V2 entry points without converting saved data. -# Restart the Web service and reload the page after changing this setting. -NEXT_PUBLIC_ENABLE_HITL_IM=false -# Show Access Token settings to workspace owners. Disabled by default. -NEXT_PUBLIC_ENABLE_ACCESS_TOKEN=false -NEXT_PUBLIC_ENABLE_AGENT_V2=true -NEXT_PUBLIC_ENABLE_AGENT_V2_IN_CHATFLOW=false -NEXT_PUBLIC_COOKIE_DOMAIN= -NEXT_PUBLIC_BATCH_CONCURRENCY=5 -CSP_WHITELIST= -TOP_K_MAX_VALUE=10 diff --git a/docker/envs/core-services/worker-beat.defaults b/docker/envs/core-services/worker-beat.defaults new file mode 100644 index 00000000000..0cdd1720ae2 --- /dev/null +++ b/docker/envs/core-services/worker-beat.defaults @@ -0,0 +1,9 @@ +# Defaults for worker_beat. Copy this file to worker-beat.env to customize it. +# Edit the settings below in the copy; docker/.env takes precedence. +# Optional application settings are commented to retain their application defaults. + +# MODE is selected by the Compose service. +# Set COMPOSE_WORKER_HEALTHCHECK_DISABLED in docker/.env; Compose needs it before loading service files. +# Set COMPOSE_WORKER_HEALTHCHECK_INTERVAL in docker/.env; Compose needs it before loading service files. +# Set COMPOSE_WORKER_HEALTHCHECK_TIMEOUT in docker/.env; Compose needs it before loading service files. +CELERY_BROKER_URL=${CELERY_BROKER_URL:-redis://:${REDIS_PASSWORD:-difyai123456}@${REDIS_HOST:-redis}:${REDIS_PORT:-6379}/1} diff --git a/docker/envs/core-services/worker-beat.env.example b/docker/envs/core-services/worker-beat.env.example deleted file mode 100644 index 380fe02b681..00000000000 --- a/docker/envs/core-services/worker-beat.env.example +++ /dev/null @@ -1,8 +0,0 @@ -# ------------------------------ -# Worker Beat Configuration -# ------------------------------ - -MODE=beat -COMPOSE_WORKER_HEALTHCHECK_DISABLED=true -COMPOSE_WORKER_HEALTHCHECK_INTERVAL=30s -COMPOSE_WORKER_HEALTHCHECK_TIMEOUT=30s diff --git a/docker/envs/core-services/worker.defaults b/docker/envs/core-services/worker.defaults new file mode 100644 index 00000000000..4fa85693b41 --- /dev/null +++ b/docker/envs/core-services/worker.defaults @@ -0,0 +1,29 @@ +# Defaults for worker. Copy this file to worker.env to customize it. +# Edit the settings below in the copy; docker/.env takes precedence. +# Optional application settings are commented to retain their application defaults. + +# MODE is selected by the Compose service. +# CELERY_WORKER_CLASS=gevent +API_SENTRY_DSN=${API_SENTRY_DSN:-} +API_SENTRY_TRACES_SAMPLE_RATE=${API_SENTRY_TRACES_SAMPLE_RATE:-1.0} +API_SENTRY_PROFILES_SAMPLE_RATE=${API_SENTRY_PROFILES_SAMPLE_RATE:-1.0} +PLUGIN_MAX_PACKAGE_SIZE=${PLUGIN_MAX_PACKAGE_SIZE:-52428800} +# Set PLUGIN_DIFY_INNER_API_KEY in docker/.env; all callers must share the API's inner credential. +# Set COMPOSE_WORKER_HEALTHCHECK_DISABLED in docker/.env; Compose needs it before loading service files. +# Set COMPOSE_WORKER_HEALTHCHECK_INTERVAL in docker/.env; Compose needs it before loading service files. +# Set COMPOSE_WORKER_HEALTHCHECK_TIMEOUT in docker/.env; Compose needs it before loading service files. +DIFY_AGENT_API_TOKEN=${DIFY_AGENT_API_TOKEN:-dify-agent-run-token-for-dev-only} +AGENT_BACKEND_BASE_URL=${AGENT_BACKEND_BASE_URL:-http://agent_backend:5050} +CELERY_BROKER_URL=${CELERY_BROKER_URL:-redis://:${REDIS_PASSWORD:-difyai123456}@${REDIS_HOST:-redis}:${REDIS_PORT:-6379}/1} + +# Worker autoscaling +# CELERY_AUTO_SCALE=false +# CELERY_MAX_WORKERS= +# CELERY_MIN_WORKERS= + +# Container variable mappings; keep these expressions unchanged in the copy. +AGENT_BACKEND_API_TOKEN=${DIFY_AGENT_API_TOKEN:-dify-agent-run-token-for-dev-only} +INNER_API_KEY_FOR_PLUGIN=${PLUGIN_DIFY_INNER_API_KEY:-QaHbTe77CtuXmsfyhR7+vRjI/+XbV1AaFy691iy+kGDv2Jvy0/eAh8Y1} +SENTRY_DSN=${API_SENTRY_DSN:-} +SENTRY_PROFILES_SAMPLE_RATE=${API_SENTRY_PROFILES_SAMPLE_RATE:-1.0} +SENTRY_TRACES_SAMPLE_RATE=${API_SENTRY_TRACES_SAMPLE_RATE:-1.0} diff --git a/docker/envs/core-services/worker.env.example b/docker/envs/core-services/worker.env.example deleted file mode 100644 index 58cf4ea901b..00000000000 --- a/docker/envs/core-services/worker.env.example +++ /dev/null @@ -1,13 +0,0 @@ -# ------------------------------ -# Worker Configuration -# ------------------------------ - -MODE=worker -SENTRY_DSN= -SENTRY_TRACES_SAMPLE_RATE=1.0 -SENTRY_PROFILES_SAMPLE_RATE=1.0 -PLUGIN_MAX_PACKAGE_SIZE=52428800 -INNER_API_KEY_FOR_PLUGIN=QaHbTe77CtuXmsfyhR7+vRjI/+XbV1AaFy691iy+kGDv2Jvy0/eAh8Y1 -COMPOSE_WORKER_HEALTHCHECK_DISABLED=true -COMPOSE_WORKER_HEALTHCHECK_INTERVAL=30s -COMPOSE_WORKER_HEALTHCHECK_TIMEOUT=30s diff --git a/docker/envs/databases/db-mysql.defaults b/docker/envs/databases/db-mysql.defaults new file mode 100644 index 00000000000..54c7db22388 --- /dev/null +++ b/docker/envs/databases/db-mysql.defaults @@ -0,0 +1,15 @@ +# Defaults for db_mysql. Copy this file to db-mysql.env to customize it. +# Edit the settings below in the copy; docker/.env takes precedence. +# Optional application settings are commented to retain their application defaults. + +MYSQL_INNODB_LOG_FILE_SIZE=${MYSQL_INNODB_LOG_FILE_SIZE:-128M} +MYSQL_INNODB_FLUSH_LOG_AT_TRX_COMMIT=${MYSQL_INNODB_FLUSH_LOG_AT_TRX_COMMIT:-2} +MYSQL_MAX_CONNECTIONS=${MYSQL_MAX_CONNECTIONS:-1000} +MYSQL_INNODB_BUFFER_POOL_SIZE=${MYSQL_INNODB_BUFFER_POOL_SIZE:-512M} +# Set MYSQL_HOST_VOLUME in docker/.env; Compose needs it before loading service files. +DB_DATABASE=${DB_DATABASE:-dify} +DB_PASSWORD=${DB_PASSWORD:-difyai123456} + +# Container variable mappings; keep these expressions unchanged in the copy. +MYSQL_DATABASE=${DB_DATABASE:-dify} +MYSQL_ROOT_PASSWORD=${DB_PASSWORD:-difyai123456} diff --git a/docker/envs/databases/db-mysql.env.example b/docker/envs/databases/db-mysql.env.example deleted file mode 100644 index b3ea6801feb..00000000000 --- a/docker/envs/databases/db-mysql.env.example +++ /dev/null @@ -1,9 +0,0 @@ -# ------------------------------ -# Db Mysql Configuration -# ------------------------------ - -MYSQL_INNODB_LOG_FILE_SIZE=128M -MYSQL_INNODB_FLUSH_LOG_AT_TRX_COMMIT=2 -MYSQL_MAX_CONNECTIONS=1000 -MYSQL_INNODB_BUFFER_POOL_SIZE=512M -MYSQL_HOST_VOLUME=./volumes/mysql/data diff --git a/docker/envs/databases/db-postgres.defaults b/docker/envs/databases/db-postgres.defaults new file mode 100644 index 00000000000..6b56ab34c83 --- /dev/null +++ b/docker/envs/databases/db-postgres.defaults @@ -0,0 +1,31 @@ +# Defaults for db_postgres. Copy this file to db-postgres.env to customize it. +# Edit the settings below in the copy; docker/.env takes precedence. +# Optional application settings are commented to retain their application defaults. + +PGDATA=${PGDATA:-/var/lib/postgresql/data/pgdata} +# DB_TYPE=postgresql +DB_USERNAME=${DB_USERNAME:-postgres} +DB_PASSWORD=${DB_PASSWORD:-difyai123456} +# DB_HOST=db_postgres +# DB_PORT=5432 +DB_DATABASE=${DB_DATABASE:-dify} +# SQLALCHEMY_POOL_SIZE=30 +# SQLALCHEMY_MAX_OVERFLOW=10 +# SQLALCHEMY_POOL_RECYCLE=3600 +# SQLALCHEMY_ECHO=false +# SQLALCHEMY_POOL_PRE_PING=false +# SQLALCHEMY_POOL_USE_LIFO=false +# SQLALCHEMY_POOL_TIMEOUT=30 +# SQLALCHEMY_POOL_RESET_ON_RETURN=rollback +POSTGRES_MAX_CONNECTIONS=${POSTGRES_MAX_CONNECTIONS:-200} +POSTGRES_SHARED_BUFFERS=${POSTGRES_SHARED_BUFFERS:-128MB} +POSTGRES_WORK_MEM=${POSTGRES_WORK_MEM:-4MB} +POSTGRES_MAINTENANCE_WORK_MEM=${POSTGRES_MAINTENANCE_WORK_MEM:-64MB} +POSTGRES_EFFECTIVE_CACHE_SIZE=${POSTGRES_EFFECTIVE_CACHE_SIZE:-4096MB} +POSTGRES_STATEMENT_TIMEOUT=${POSTGRES_STATEMENT_TIMEOUT:-0} +POSTGRES_IDLE_IN_TRANSACTION_SESSION_TIMEOUT=${POSTGRES_IDLE_IN_TRANSACTION_SESSION_TIMEOUT:-0} + +# Container variable mappings; keep these expressions unchanged in the copy. +POSTGRES_DB=${DB_DATABASE:-dify} +POSTGRES_PASSWORD=${DB_PASSWORD:-difyai123456} +POSTGRES_USER=${DB_USERNAME:-postgres} diff --git a/docker/envs/databases/db-postgres.env.example b/docker/envs/databases/db-postgres.env.example deleted file mode 100644 index 14cefb6bee6..00000000000 --- a/docker/envs/databases/db-postgres.env.example +++ /dev/null @@ -1,26 +0,0 @@ -# ------------------------------ -# Db Postgres Configuration -# ------------------------------ - -PGDATA=/var/lib/postgresql/data/pgdata -DB_TYPE=postgresql -DB_USERNAME=postgres -DB_PASSWORD=difyai123456 -DB_HOST=db_postgres -DB_PORT=5432 -DB_DATABASE=dify -SQLALCHEMY_POOL_SIZE=30 -SQLALCHEMY_MAX_OVERFLOW=10 -SQLALCHEMY_POOL_RECYCLE=3600 -SQLALCHEMY_ECHO=false -SQLALCHEMY_POOL_PRE_PING=false -SQLALCHEMY_POOL_USE_LIFO=false -SQLALCHEMY_POOL_TIMEOUT=30 -SQLALCHEMY_POOL_RESET_ON_RETURN=rollback -POSTGRES_MAX_CONNECTIONS=100 -POSTGRES_SHARED_BUFFERS=128MB -POSTGRES_WORK_MEM=4MB -POSTGRES_MAINTENANCE_WORK_MEM=64MB -POSTGRES_EFFECTIVE_CACHE_SIZE=4096MB -POSTGRES_STATEMENT_TIMEOUT=0 -POSTGRES_IDLE_IN_TRANSACTION_SESSION_TIMEOUT=0 diff --git a/docker/envs/databases/redis.defaults b/docker/envs/databases/redis.defaults new file mode 100644 index 00000000000..7cfcd0ff868 --- /dev/null +++ b/docker/envs/databases/redis.defaults @@ -0,0 +1,42 @@ +# Defaults for redis. Copy this file to redis.env to customize it. +# Edit the settings below in the copy; docker/.env takes precedence. +# Optional application settings are commented to retain their application defaults. + +# REDIS_HOST=redis +# REDIS_PORT=6379 +# REDIS_USERNAME= +REDIS_PASSWORD=${REDIS_PASSWORD:-difyai123456} +# REDIS_USE_SSL=false +# REDIS_SSL_CERT_REQS=CERT_NONE +# REDIS_SSL_CA_CERTS= +# REDIS_SSL_CERTFILE= +# REDIS_SSL_KEYFILE= +# REDIS_DB=0 +# REDIS_KEY_PREFIX= +# REDIS_MAX_CONNECTIONS= +# REDIS_USE_SENTINEL=false +# REDIS_SENTINELS= +# REDIS_SENTINEL_SERVICE_NAME= +# REDIS_SENTINEL_USERNAME= +# REDIS_SENTINEL_PASSWORD= +# REDIS_SENTINEL_SOCKET_TIMEOUT=0.1 +# REDIS_USE_CLUSTERS=false +# REDIS_CLUSTERS= +# REDIS_CLUSTERS_PASSWORD= +# REDIS_RETRY_RETRIES=3 +# REDIS_RETRY_BACKOFF_BASE=1.0 +# REDIS_RETRY_BACKOFF_CAP=10.0 +# REDIS_SOCKET_TIMEOUT=5.0 +# REDIS_SOCKET_CONNECT_TIMEOUT=5.0 +# REDIS_HEALTH_CHECK_INTERVAL=30 +# REDIS_KEEPALIVE=true +# REDIS_KEEPALIVE_IDLE=30 +# REDIS_KEEPALIVE_INTERVAL=10 +# REDIS_KEEPALIVE_COUNT=10 +# EVENT_BUS_REDIS_URL= +# EVENT_BUS_REDIS_CHANNEL_TYPE=pubsub +# EVENT_BUS_REDIS_USE_CLUSTERS=false +# BROKER_USE_SSL=false + +# Container variable mappings; keep these expressions unchanged in the copy. +REDISCLI_AUTH=${REDIS_PASSWORD:-difyai123456} diff --git a/docker/envs/databases/redis.env.example b/docker/envs/databases/redis.env.example deleted file mode 100644 index 56be6f48ac7..00000000000 --- a/docker/envs/databases/redis.env.example +++ /dev/null @@ -1,39 +0,0 @@ -# ------------------------------ -# Redis Configuration -# ------------------------------ - -REDIS_HOST=redis -REDIS_PORT=6379 -REDIS_USERNAME= -REDIS_PASSWORD=difyai123456 -REDIS_USE_SSL=false -REDIS_SSL_CERT_REQS=CERT_NONE -REDIS_SSL_CA_CERTS= -REDIS_SSL_CERTFILE= -REDIS_SSL_KEYFILE= -REDIS_DB=0 -REDIS_KEY_PREFIX= -REDIS_MAX_CONNECTIONS= -REDIS_USE_SENTINEL=false -REDIS_SENTINELS= -REDIS_SENTINEL_SERVICE_NAME= -REDIS_SENTINEL_USERNAME= -REDIS_SENTINEL_PASSWORD= -REDIS_SENTINEL_SOCKET_TIMEOUT=0.1 -REDIS_USE_CLUSTERS=false -REDIS_CLUSTERS= -REDIS_CLUSTERS_PASSWORD= -REDIS_RETRY_RETRIES=3 -REDIS_RETRY_BACKOFF_BASE=1.0 -REDIS_RETRY_BACKOFF_CAP=10.0 -REDIS_SOCKET_TIMEOUT=5.0 -REDIS_SOCKET_CONNECT_TIMEOUT=5.0 -REDIS_HEALTH_CHECK_INTERVAL=30 -REDIS_KEEPALIVE=true -REDIS_KEEPALIVE_IDLE=30 -REDIS_KEEPALIVE_INTERVAL=10 -REDIS_KEEPALIVE_COUNT=10 -EVENT_BUS_REDIS_URL= -EVENT_BUS_REDIS_CHANNEL_TYPE=pubsub -EVENT_BUS_REDIS_USE_CLUSTERS=false -BROKER_USE_SSL=false diff --git a/docker/envs/infrastructure/certbot.defaults b/docker/envs/infrastructure/certbot.defaults new file mode 100644 index 00000000000..1d435e7aab0 --- /dev/null +++ b/docker/envs/infrastructure/certbot.defaults @@ -0,0 +1,7 @@ +# Defaults for certbot. Copy this file to certbot.env to customize it. +# Edit the settings below in the copy; docker/.env takes precedence. +# Optional application settings are commented to retain their application defaults. + +CERTBOT_EMAIL=${CERTBOT_EMAIL:-} +CERTBOT_DOMAIN=${CERTBOT_DOMAIN:-} +CERTBOT_OPTIONS=${CERTBOT_OPTIONS:-} diff --git a/docker/envs/infrastructure/certbot.env.example b/docker/envs/infrastructure/certbot.env.example deleted file mode 100644 index c654fbe02fc..00000000000 --- a/docker/envs/infrastructure/certbot.env.example +++ /dev/null @@ -1,7 +0,0 @@ -# ------------------------------ -# Certbot Configuration -# ------------------------------ - -CERTBOT_EMAIL=your_email@example.com -CERTBOT_DOMAIN=your_domain.com -CERTBOT_OPTIONS= diff --git a/docker/envs/infrastructure/etcd.defaults b/docker/envs/infrastructure/etcd.defaults new file mode 100644 index 00000000000..9ae12659307 --- /dev/null +++ b/docker/envs/infrastructure/etcd.defaults @@ -0,0 +1,8 @@ +# Defaults for etcd. Copy this file to etcd.env to customize it. +# Edit the settings below in the copy; docker/.env takes precedence. +# Optional application settings are commented to retain their application defaults. + +ETCD_AUTO_COMPACTION_MODE=${ETCD_AUTO_COMPACTION_MODE:-revision} +ETCD_AUTO_COMPACTION_RETENTION=${ETCD_AUTO_COMPACTION_RETENTION:-1000} +ETCD_QUOTA_BACKEND_BYTES=${ETCD_QUOTA_BACKEND_BYTES:-4294967296} +ETCD_SNAPSHOT_COUNT=${ETCD_SNAPSHOT_COUNT:-50000} diff --git a/docker/envs/infrastructure/etcd.env.example b/docker/envs/infrastructure/etcd.env.example deleted file mode 100644 index 4dca26671a7..00000000000 --- a/docker/envs/infrastructure/etcd.env.example +++ /dev/null @@ -1,4 +0,0 @@ -# ------------------------------ -# Etcd Configuration -# ------------------------------ - diff --git a/docker/envs/infrastructure/milvus-standalone.defaults b/docker/envs/infrastructure/milvus-standalone.defaults new file mode 100644 index 00000000000..46ecb6d0cc4 --- /dev/null +++ b/docker/envs/infrastructure/milvus-standalone.defaults @@ -0,0 +1,10 @@ +# Defaults for milvus-standalone. Copy this file to milvus-standalone.env to customize it. +# Edit the settings below in the copy; docker/.env takes precedence. +# Optional application settings are commented to retain their application defaults. + +ETCD_ENDPOINTS=${ETCD_ENDPOINTS:-etcd:2379} +MINIO_ADDRESS=${MINIO_ADDRESS:-minio:9000} +MILVUS_AUTHORIZATION_ENABLED=${MILVUS_AUTHORIZATION_ENABLED:-true} + +# Container variable mappings; keep these expressions unchanged in the copy. +common.security.authorizationEnabled=${MILVUS_AUTHORIZATION_ENABLED:-true} diff --git a/docker/envs/infrastructure/milvus-standalone.env.example b/docker/envs/infrastructure/milvus-standalone.env.example deleted file mode 100644 index 7e87ed26489..00000000000 --- a/docker/envs/infrastructure/milvus-standalone.env.example +++ /dev/null @@ -1,4 +0,0 @@ -# ------------------------------ -# Milvus Standalone Configuration -# ------------------------------ - diff --git a/docker/envs/infrastructure/minio.defaults b/docker/envs/infrastructure/minio.defaults new file mode 100644 index 00000000000..a66f0d60659 --- /dev/null +++ b/docker/envs/infrastructure/minio.defaults @@ -0,0 +1,6 @@ +# Defaults for minio. Copy this file to minio.env to customize it. +# Edit the settings below in the copy; docker/.env takes precedence. +# Optional application settings are commented to retain their application defaults. + +MINIO_ACCESS_KEY=${MINIO_ACCESS_KEY:-minioadmin} +MINIO_SECRET_KEY=${MINIO_SECRET_KEY:-minioadmin} diff --git a/docker/envs/infrastructure/minio.env.example b/docker/envs/infrastructure/minio.env.example deleted file mode 100644 index 7c8e1fa35ab..00000000000 --- a/docker/envs/infrastructure/minio.env.example +++ /dev/null @@ -1,4 +0,0 @@ -# ------------------------------ -# Minio Configuration -# ------------------------------ - diff --git a/docker/envs/infrastructure/nginx.defaults b/docker/envs/infrastructure/nginx.defaults new file mode 100644 index 00000000000..d0a683e6266 --- /dev/null +++ b/docker/envs/infrastructure/nginx.defaults @@ -0,0 +1,20 @@ +# Defaults for nginx. Copy this file to nginx.env to customize it. +# Edit the settings below in the copy; docker/.env takes precedence. +# Optional application settings are commented to retain their application defaults. + +NGINX_SERVER_NAME=${NGINX_SERVER_NAME:-_} +# Supply certificates in docker/nginx/ssl or enable the Certbot challenge before enabling HTTPS. +NGINX_HTTPS_ENABLED=${NGINX_HTTPS_ENABLED:-false} +# Set NGINX_PORT in docker/.env; Compose needs it before loading service files. +# Set NGINX_SSL_PORT in docker/.env; Compose needs it before loading service files. +NGINX_SSL_CERT_FILENAME=${NGINX_SSL_CERT_FILENAME:-dify.crt} +NGINX_SSL_CERT_KEY_FILENAME=${NGINX_SSL_CERT_KEY_FILENAME:-dify.key} +NGINX_SSL_PROTOCOLS=${NGINX_SSL_PROTOCOLS:-TLSv1.2 TLSv1.3} +NGINX_WORKER_PROCESSES=${NGINX_WORKER_PROCESSES:-auto} +NGINX_CLIENT_MAX_BODY_SIZE=${NGINX_CLIENT_MAX_BODY_SIZE:-100M} +NGINX_KEEPALIVE_TIMEOUT=${NGINX_KEEPALIVE_TIMEOUT:-65} +NGINX_PROXY_READ_TIMEOUT=${NGINX_PROXY_READ_TIMEOUT:-3600s} +NGINX_PROXY_SEND_TIMEOUT=${NGINX_PROXY_SEND_TIMEOUT:-3600s} +NGINX_ENABLE_CERTBOT_CHALLENGE=${NGINX_ENABLE_CERTBOT_CHALLENGE:-false} +NGINX_SOCKET_IO_UPSTREAM=${NGINX_SOCKET_IO_UPSTREAM:-api_websocket:5001} +CERTBOT_DOMAIN=${CERTBOT_DOMAIN:-} diff --git a/docker/envs/infrastructure/nginx.env.example b/docker/envs/infrastructure/nginx.env.example deleted file mode 100644 index fcb369a47dd..00000000000 --- a/docker/envs/infrastructure/nginx.env.example +++ /dev/null @@ -1,18 +0,0 @@ -# ------------------------------ -# Nginx Configuration -# ------------------------------ - -NGINX_SERVER_NAME=_ -NGINX_HTTPS_ENABLED=false -NGINX_PORT=80 -NGINX_SSL_PORT=443 -NGINX_SSL_CERT_FILENAME=dify.crt -NGINX_SSL_CERT_KEY_FILENAME=dify.key -NGINX_SSL_PROTOCOLS=TLSv1.2 TLSv1.3 -NGINX_WORKER_PROCESSES=auto -NGINX_CLIENT_MAX_BODY_SIZE=100M -NGINX_KEEPALIVE_TIMEOUT=65 -NGINX_PROXY_READ_TIMEOUT=3600s -NGINX_PROXY_SEND_TIMEOUT=3600s -NGINX_ENABLE_CERTBOT_CHALLENGE=false -NGINX_SOCKET_IO_UPSTREAM=api_websocket:5001 diff --git a/docker/envs/infrastructure/ssrf-proxy.defaults b/docker/envs/infrastructure/ssrf-proxy.defaults new file mode 100644 index 00000000000..ad9a0f89924 --- /dev/null +++ b/docker/envs/infrastructure/ssrf-proxy.defaults @@ -0,0 +1,30 @@ +# Defaults for ssrf_proxy. Copy this file to ssrf-proxy.env to customize it. +# Edit the settings below in the copy; docker/.env takes precedence. +# Optional application settings are commented to retain their application defaults. + +# SSRF_PROXY_HTTP_URL=http://ssrf_proxy:3128 +# SSRF_PROXY_HTTPS_URL=http://ssrf_proxy:3128 +SSRF_HTTP_PORT=${SSRF_HTTP_PORT:-3128} +SSRF_COREDUMP_DIR=${SSRF_COREDUMP_DIR:-/var/spool/squid} +# Comma-separated CIDR ranges that the SSRF proxy should allow even when they +# resolve to private, loopback, link-local, or otherwise non-public addresses. +# Leave empty (the default) to keep the deny-by-default policy. Required when +# Dify needs to reach internal HTTP endpoints (e.g. an internal API on +# http://172.21.x.x) from an HTTP Request node, a tool, or a plugin. +# Example: 172.21.0.0/16,10.0.0.0/8 +SSRF_PROXY_ALLOW_PRIVATE_IPS=${SSRF_PROXY_ALLOW_PRIVATE_IPS:-} +# Comma-separated domain suffixes that the SSRF proxy should allow even when +# they would otherwise resolve to a denied range. Leave empty to keep the +# deny-by-default policy. +SSRF_PROXY_ALLOW_PRIVATE_DOMAINS=${SSRF_PROXY_ALLOW_PRIVATE_DOMAINS:-} +# SSRF_DEFAULT_TIME_OUT=5 +# SSRF_DEFAULT_CONNECT_TIME_OUT=5 +# SSRF_DEFAULT_READ_TIME_OUT=5 +# SSRF_DEFAULT_WRITE_TIME_OUT=5 +# SSRF_POOL_MAX_CONNECTIONS=100 +# SSRF_POOL_MAX_KEEPALIVE_CONNECTIONS=20 +# SSRF_POOL_KEEPALIVE_EXPIRY=5.0 + +# Container variable mappings; keep these expressions unchanged in the copy. +COREDUMP_DIR=${SSRF_COREDUMP_DIR:-/var/spool/squid} +HTTP_PORT=${SSRF_HTTP_PORT:-3128} diff --git a/docker/envs/infrastructure/ssrf-proxy.env.example b/docker/envs/infrastructure/ssrf-proxy.env.example deleted file mode 100644 index 9dedcc35c90..00000000000 --- a/docker/envs/infrastructure/ssrf-proxy.env.example +++ /dev/null @@ -1,26 +0,0 @@ -# ------------------------------ -# Ssrf Proxy Configuration -# ------------------------------ - -SSRF_PROXY_HTTP_URL=http://ssrf_proxy:3128 -SSRF_PROXY_HTTPS_URL=http://ssrf_proxy:3128 -SSRF_HTTP_PORT=3128 -SSRF_COREDUMP_DIR=/var/spool/squid -# Comma-separated CIDR ranges that the SSRF proxy should allow even when they -# resolve to private, loopback, link-local, or otherwise non-public addresses. -# Leave empty (the default) to keep the deny-by-default policy. Required when -# Dify needs to reach internal HTTP endpoints (e.g. an internal API on -# http://172.21.x.x) from an HTTP Request node, a tool, or a plugin. -# Example: 172.21.0.0/16,10.0.0.0/8 -SSRF_PROXY_ALLOW_PRIVATE_IPS= -# Comma-separated domain suffixes that the SSRF proxy should allow even when -# they would otherwise resolve to a denied range. Leave empty to keep the -# deny-by-default policy. -SSRF_PROXY_ALLOW_PRIVATE_DOMAINS= -SSRF_DEFAULT_TIME_OUT=5 -SSRF_DEFAULT_CONNECT_TIME_OUT=5 -SSRF_DEFAULT_READ_TIME_OUT=5 -SSRF_DEFAULT_WRITE_TIME_OUT=5 -SSRF_POOL_MAX_CONNECTIONS=100 -SSRF_POOL_MAX_KEEPALIVE_CONNECTIONS=20 -SSRF_POOL_KEEPALIVE_EXPIRY=5.0 diff --git a/docker/envs/vectorstores/chroma.defaults b/docker/envs/vectorstores/chroma.defaults new file mode 100644 index 00000000000..e28ce786cff --- /dev/null +++ b/docker/envs/vectorstores/chroma.defaults @@ -0,0 +1,16 @@ +# Defaults for chroma. Copy this file to chroma.env to customize it. +# Edit the settings below in the copy; docker/.env takes precedence. +# Optional application settings are commented to retain their application defaults. + +# CHROMA_DATABASE=default_database +# CHROMA_AUTH_PROVIDER=chromadb.auth.token_authn.TokenAuthClientProvider +# CHROMA_AUTH_CREDENTIALS= +# CHROMA_HOST=127.0.0.1 +# CHROMA_PORT=8000 +# CHROMA_TENANT=default_tenant +CHROMA_SERVER_AUTHN_CREDENTIALS=${CHROMA_SERVER_AUTHN_CREDENTIALS:-difyai123456} +CHROMA_SERVER_AUTHN_PROVIDER=${CHROMA_SERVER_AUTHN_PROVIDER:-chromadb.auth.token_authn.TokenAuthenticationServerProvider} +CHROMA_IS_PERSISTENT=${CHROMA_IS_PERSISTENT:-TRUE} + +# Container variable mappings; keep these expressions unchanged in the copy. +IS_PERSISTENT=${CHROMA_IS_PERSISTENT:-TRUE} diff --git a/docker/envs/vectorstores/chroma.env.example b/docker/envs/vectorstores/chroma.env.example deleted file mode 100644 index 2a15375a3da..00000000000 --- a/docker/envs/vectorstores/chroma.env.example +++ /dev/null @@ -1,13 +0,0 @@ -# ------------------------------ -# Chroma Configuration -# ------------------------------ - -CHROMA_DATABASE=default_database -CHROMA_AUTH_PROVIDER=chromadb.auth.token_authn.TokenAuthClientProvider -CHROMA_AUTH_CREDENTIALS= -CHROMA_HOST=127.0.0.1 -CHROMA_PORT=8000 -CHROMA_TENANT=default_tenant -CHROMA_SERVER_AUTHN_CREDENTIALS=difyai123456 -CHROMA_SERVER_AUTHN_PROVIDER=chromadb.auth.token_authn.TokenAuthenticationServerProvider -CHROMA_IS_PERSISTENT=TRUE diff --git a/docker/envs/vectorstores/couchbase.defaults b/docker/envs/vectorstores/couchbase.defaults new file mode 100644 index 00000000000..a5137e6f6a0 --- /dev/null +++ b/docker/envs/vectorstores/couchbase.defaults @@ -0,0 +1,14 @@ +# Defaults for couchbase-server. Copy this file to couchbase.env to customize it. +# Edit the settings below in the copy; docker/.env takes precedence. +# Optional application settings are commented to retain their application defaults. + +COUCHBASE_PASSWORD=${COUCHBASE_PASSWORD:-password} +COUCHBASE_BUCKET_NAME=${COUCHBASE_BUCKET_NAME:-Embeddings} +# COUCHBASE_SCOPE_NAME=_default +# COUCHBASE_CONNECTION_STRING=couchbase://couchbase-server +COUCHBASE_USER=${COUCHBASE_USER:-Administrator} + +# Container variable mappings; keep these expressions unchanged in the copy. +COUCHBASE_ADMINISTRATOR_USERNAME=${COUCHBASE_USER:-Administrator} +COUCHBASE_ADMINISTRATOR_PASSWORD=${COUCHBASE_PASSWORD:-password} +COUCHBASE_BUCKET=${COUCHBASE_BUCKET_NAME:-Embeddings} diff --git a/docker/envs/vectorstores/couchbase.env.example b/docker/envs/vectorstores/couchbase.env.example deleted file mode 100644 index 4329d9c723c..00000000000 --- a/docker/envs/vectorstores/couchbase.env.example +++ /dev/null @@ -1,9 +0,0 @@ -# ------------------------------ -# Couchbase Configuration -# ------------------------------ - -COUCHBASE_PASSWORD=password -COUCHBASE_BUCKET_NAME=Embeddings -COUCHBASE_SCOPE_NAME=_default -COUCHBASE_CONNECTION_STRING=couchbase://couchbase-server -COUCHBASE_USER=Administrator diff --git a/docker/envs/vectorstores/elasticsearch.defaults b/docker/envs/vectorstores/elasticsearch.defaults new file mode 100644 index 00000000000..39314377f35 --- /dev/null +++ b/docker/envs/vectorstores/elasticsearch.defaults @@ -0,0 +1,21 @@ +# Defaults for elasticsearch. Copy this file to elasticsearch.env to customize it. +# Edit the settings below in the copy; docker/.env takes precedence. +# Optional application settings are commented to retain their application defaults. + +# ELASTICSEARCH_CLOUD_URL=YOUR-ELASTICSEARCH_CLOUD_URL +ELASTICSEARCH_PASSWORD=${ELASTICSEARCH_PASSWORD:-elastic} +# Set KIBANA_PORT in docker/.env; Compose needs it before loading service files. +# ELASTICSEARCH_USE_CLOUD=false +# ELASTICSEARCH_API_KEY=YOUR-ELASTICSEARCH_API_KEY +# ELASTICSEARCH_VERIFY_CERTS=False +# ELASTICSEARCH_CA_CERTS= +# ELASTICSEARCH_REQUEST_TIMEOUT=100000 +# ELASTICSEARCH_RETRY_ON_TIMEOUT=True +# ELASTICSEARCH_MAX_RETRIES=10 +# ELASTICSEARCH_HOST=0.0.0.0 +# Set ELASTICSEARCH_PORT in docker/.env; Compose needs it before loading service files. +# ELASTICSEARCH_USERNAME=elastic +# Set VECTOR_STORE in docker/.env to select the backend and its Compose profile together. + +# Container variable mappings; keep these expressions unchanged in the copy. +ELASTIC_PASSWORD=${ELASTICSEARCH_PASSWORD:-elastic} diff --git a/docker/envs/vectorstores/elasticsearch.env.example b/docker/envs/vectorstores/elasticsearch.env.example deleted file mode 100644 index 2aaa965cd7d..00000000000 --- a/docker/envs/vectorstores/elasticsearch.env.example +++ /dev/null @@ -1,17 +0,0 @@ -# ------------------------------ -# Elasticsearch Configuration -# ------------------------------ - -ELASTICSEARCH_CLOUD_URL=YOUR-ELASTICSEARCH_CLOUD_URL -ELASTICSEARCH_PASSWORD=elastic -KIBANA_PORT=5601 -ELASTICSEARCH_USE_CLOUD=false -ELASTICSEARCH_API_KEY=YOUR-ELASTICSEARCH_API_KEY -ELASTICSEARCH_VERIFY_CERTS=False -ELASTICSEARCH_CA_CERTS= -ELASTICSEARCH_REQUEST_TIMEOUT=100000 -ELASTICSEARCH_RETRY_ON_TIMEOUT=True -ELASTICSEARCH_MAX_RETRIES=10 -ELASTICSEARCH_HOST=0.0.0.0 -ELASTICSEARCH_PORT=9200 -ELASTICSEARCH_USERNAME=elastic diff --git a/docker/envs/vectorstores/iris.defaults b/docker/envs/vectorstores/iris.defaults new file mode 100644 index 00000000000..c2dfeacb0a3 --- /dev/null +++ b/docker/envs/vectorstores/iris.defaults @@ -0,0 +1,20 @@ +# Defaults for iris. Copy this file to iris.env to customize it. +# Edit the settings below in the copy; docker/.env takes precedence. +# Optional application settings are commented to retain their application defaults. + +# IRIS_CONNECTION_URL= +# IRIS_MIN_CONNECTION=1 +# IRIS_MAX_CONNECTION=3 +# IRIS_TEXT_INDEX=true +# IRIS_TEXT_INDEX_LANGUAGE=en +IRIS_TIMEZONE=${IRIS_TIMEZONE:-UTC} +# IRIS_PASSWORD=Dify@1234 +# IRIS_DATABASE=USER +# IRIS_SCHEMA=dify +# IRIS_HOST=iris +# Set IRIS_SUPER_SERVER_PORT in docker/.env; Compose needs it before loading service files. +# Set IRIS_WEB_SERVER_PORT in docker/.env; Compose needs it before loading service files. +# IRIS_USER=_SYSTEM + +# Container variable mappings; keep these expressions unchanged in the copy. +TZ=${IRIS_TIMEZONE:-UTC} diff --git a/docker/envs/vectorstores/iris.env.example b/docker/envs/vectorstores/iris.env.example deleted file mode 100644 index b1eb39bff81..00000000000 --- a/docker/envs/vectorstores/iris.env.example +++ /dev/null @@ -1,17 +0,0 @@ -# ------------------------------ -# Iris Configuration -# ------------------------------ - -IRIS_CONNECTION_URL= -IRIS_MIN_CONNECTION=1 -IRIS_MAX_CONNECTION=3 -IRIS_TEXT_INDEX=true -IRIS_TEXT_INDEX_LANGUAGE=en -IRIS_TIMEZONE=UTC -IRIS_PASSWORD=Dify@1234 -IRIS_DATABASE=USER -IRIS_SCHEMA=dify -IRIS_HOST=iris -IRIS_SUPER_SERVER_PORT=1972 -IRIS_WEB_SERVER_PORT=52773 -IRIS_USER=_SYSTEM diff --git a/docker/envs/vectorstores/milvus.env.example b/docker/envs/vectorstores/milvus.env.example index c52feb7aa08..e7cc77919ab 100644 --- a/docker/envs/vectorstores/milvus.env.example +++ b/docker/envs/vectorstores/milvus.env.example @@ -14,3 +14,12 @@ MILVUS_AUTHORIZATION_ENABLED=true MILVUS_SECURE=false MILVUS_SERVER_PEM_PATH= MILVUS_SERVER_NAME= + +# Dify vector client settings (API, workers and Beat) +# MILVUS_URI=http://host.docker.internal:19530 +# MILVUS_TOKEN= +# MILVUS_USER= +# MILVUS_PASSWORD= +# MILVUS_ANALYZER_PARAMS= +# MILVUS_DATABASE= +# MILVUS_ENABLE_HYBRID_SEARCH=False diff --git a/docker/envs/vectorstores/oceanbase.defaults b/docker/envs/vectorstores/oceanbase.defaults new file mode 100644 index 00000000000..f3ab862bbac --- /dev/null +++ b/docker/envs/vectorstores/oceanbase.defaults @@ -0,0 +1,21 @@ +# Defaults for oceanbase. Copy this file to oceanbase.env to customize it. +# Edit the settings below in the copy; docker/.env takes precedence. +# Optional application settings are commented to retain their application defaults. + +OCEANBASE_CLUSTER_NAME=${OCEANBASE_CLUSTER_NAME:-difyai} +OCEANBASE_MEMORY_LIMIT=${OCEANBASE_MEMORY_LIMIT:-6G} +OCEANBASE_VECTOR_PASSWORD=${OCEANBASE_VECTOR_PASSWORD:-difyai123456} + +# Dify vector client settings (API, workers and Beat) +# Set OCEANBASE_VECTOR_PORT in docker/.env to keep the client and published port aligned. +# OCEANBASE_VECTOR_HOST=oceanbase +# OCEANBASE_VECTOR_USER=root@test +# OCEANBASE_VECTOR_DATABASE=test +# OCEANBASE_ENABLE_HYBRID_SEARCH=false +# OCEANBASE_FULLTEXT_PARSER=ik + +# Container variable mappings; keep these expressions unchanged in the copy. +OB_CLUSTER_NAME=${OCEANBASE_CLUSTER_NAME:-difyai} +OB_MEMORY_LIMIT=${OCEANBASE_MEMORY_LIMIT:-6G} +OB_SYS_PASSWORD=${OCEANBASE_VECTOR_PASSWORD:-difyai123456} +OB_TENANT_PASSWORD=${OCEANBASE_VECTOR_PASSWORD:-difyai123456} diff --git a/docker/envs/vectorstores/oceanbase.env.example b/docker/envs/vectorstores/oceanbase.env.example deleted file mode 100644 index 42bed8df6a5..00000000000 --- a/docker/envs/vectorstores/oceanbase.env.example +++ /dev/null @@ -1,6 +0,0 @@ -# ------------------------------ -# Oceanbase Configuration -# ------------------------------ - -OCEANBASE_CLUSTER_NAME=difyai -OCEANBASE_MEMORY_LIMIT=6G diff --git a/docker/envs/vectorstores/opengauss.defaults b/docker/envs/vectorstores/opengauss.defaults new file mode 100644 index 00000000000..b2049164b29 --- /dev/null +++ b/docker/envs/vectorstores/opengauss.defaults @@ -0,0 +1,17 @@ +# Defaults for opengauss. Copy this file to opengauss.env to customize it. +# Edit the settings below in the copy; docker/.env takes precedence. +# Optional application settings are commented to retain their application defaults. + +OPENGAUSS_PASSWORD=${OPENGAUSS_PASSWORD:-Dify@123} +OPENGAUSS_DATABASE=${OPENGAUSS_DATABASE:-dify} +# OPENGAUSS_MIN_CONNECTION=1 +# OPENGAUSS_MAX_CONNECTION=5 +# OPENGAUSS_ENABLE_PQ=false +# OPENGAUSS_HOST=opengauss +# Set OPENGAUSS_PORT in docker/.env; Compose needs it before loading service files. +OPENGAUSS_USER=${OPENGAUSS_USER:-postgres} + +# Container variable mappings; keep these expressions unchanged in the copy. +GS_DB=${OPENGAUSS_DATABASE:-dify} +GS_PASSWORD=${OPENGAUSS_PASSWORD:-Dify@123} +GS_USERNAME=${OPENGAUSS_USER:-postgres} diff --git a/docker/envs/vectorstores/opengauss.env.example b/docker/envs/vectorstores/opengauss.env.example deleted file mode 100644 index 9f58499b642..00000000000 --- a/docker/envs/vectorstores/opengauss.env.example +++ /dev/null @@ -1,12 +0,0 @@ -# ------------------------------ -# Opengauss Configuration -# ------------------------------ - -OPENGAUSS_PASSWORD=Dify@123 -OPENGAUSS_DATABASE=dify -OPENGAUSS_MIN_CONNECTION=1 -OPENGAUSS_MAX_CONNECTION=5 -OPENGAUSS_ENABLE_PQ=false -OPENGAUSS_HOST=opengauss -OPENGAUSS_PORT=6600 -OPENGAUSS_USER=postgres diff --git a/docker/envs/vectorstores/opensearch.defaults b/docker/envs/vectorstores/opensearch.defaults new file mode 100644 index 00000000000..1c73b004724 --- /dev/null +++ b/docker/envs/vectorstores/opensearch.defaults @@ -0,0 +1,27 @@ +# Defaults for opensearch. Copy this file to opensearch.env to customize it. +# Edit the settings below in the copy; docker/.env takes precedence. +# Optional application settings are commented to retain their application defaults. + +# OPENSEARCH_PASSWORD=admin +# OPENSEARCH_AWS_REGION=ap-southeast-1 +# OPENSEARCH_AWS_SERVICE=aoss +OPENSEARCH_INITIAL_ADMIN_PASSWORD=${OPENSEARCH_INITIAL_ADMIN_PASSWORD:-Qazwsxedc!@#123} +# Set OPENSEARCH_MEMLOCK_SOFT in docker/.env; Compose needs it before loading service files. +# Set OPENSEARCH_MEMLOCK_HARD in docker/.env; Compose needs it before loading service files. +# Set OPENSEARCH_NOFILE_SOFT in docker/.env; Compose needs it before loading service files. +# Set OPENSEARCH_NOFILE_HARD in docker/.env; Compose needs it before loading service files. +# OPENSEARCH_HOST=opensearch +# OPENSEARCH_PORT=9200 +# OPENSEARCH_SECURE=true +# OPENSEARCH_VERIFY_CERTS=true +# OPENSEARCH_AUTH_METHOD=basic +# OPENSEARCH_USER=admin +OPENSEARCH_DISCOVERY_TYPE=${OPENSEARCH_DISCOVERY_TYPE:-single-node} +OPENSEARCH_BOOTSTRAP_MEMORY_LOCK=${OPENSEARCH_BOOTSTRAP_MEMORY_LOCK:-true} +OPENSEARCH_JAVA_OPTS_MIN=${OPENSEARCH_JAVA_OPTS_MIN:-512m} +OPENSEARCH_JAVA_OPTS_MAX=${OPENSEARCH_JAVA_OPTS_MAX:-1024m} + +# Container variable mappings; keep these expressions unchanged in the copy. +OPENSEARCH_JAVA_OPTS=-Xms${OPENSEARCH_JAVA_OPTS_MIN:-512m} -Xmx${OPENSEARCH_JAVA_OPTS_MAX:-1024m} +bootstrap.memory_lock=${OPENSEARCH_BOOTSTRAP_MEMORY_LOCK:-true} +discovery.type=${OPENSEARCH_DISCOVERY_TYPE:-single-node} diff --git a/docker/envs/vectorstores/opensearch.env.example b/docker/envs/vectorstores/opensearch.env.example deleted file mode 100644 index a6a92833783..00000000000 --- a/docker/envs/vectorstores/opensearch.env.example +++ /dev/null @@ -1,22 +0,0 @@ -# ------------------------------ -# Opensearch Configuration -# ------------------------------ - -OPENSEARCH_PASSWORD=admin -OPENSEARCH_AWS_REGION=ap-southeast-1 -OPENSEARCH_AWS_SERVICE=aoss -OPENSEARCH_INITIAL_ADMIN_PASSWORD=Qazwsxedc!@#123 -OPENSEARCH_MEMLOCK_SOFT=-1 -OPENSEARCH_MEMLOCK_HARD=-1 -OPENSEARCH_NOFILE_SOFT=65536 -OPENSEARCH_NOFILE_HARD=65536 -OPENSEARCH_HOST=opensearch -OPENSEARCH_PORT=9200 -OPENSEARCH_SECURE=true -OPENSEARCH_VERIFY_CERTS=true -OPENSEARCH_AUTH_METHOD=basic -OPENSEARCH_USER=admin -OPENSEARCH_DISCOVERY_TYPE=single-node -OPENSEARCH_BOOTSTRAP_MEMORY_LOCK=true -OPENSEARCH_JAVA_OPTS_MIN=512m -OPENSEARCH_JAVA_OPTS_MAX=1024m diff --git a/docker/envs/vectorstores/oracle.defaults b/docker/envs/vectorstores/oracle.defaults new file mode 100644 index 00000000000..7055e1e73e0 --- /dev/null +++ b/docker/envs/vectorstores/oracle.defaults @@ -0,0 +1,13 @@ +# Defaults for oracle. Copy this file to oracle.env to customize it. +# Edit the settings below in the copy; docker/.env takes precedence. +# Optional application settings are commented to retain their application defaults. + +# ORACLE_PASSWORD=dify +# ORACLE_DSN=oracle:1521/FREEPDB1 +# ORACLE_CONFIG_DIR=/app/api/storage/wallet +# ORACLE_WALLET_LOCATION=/app/api/storage/wallet +# ORACLE_WALLET_PASSWORD=dify +# ORACLE_IS_AUTONOMOUS=false +# ORACLE_USER=dify +ORACLE_PWD=${ORACLE_PWD:-Dify123456} +ORACLE_CHARACTERSET=${ORACLE_CHARACTERSET:-AL32UTF8} diff --git a/docker/envs/vectorstores/oracle.env.example b/docker/envs/vectorstores/oracle.env.example deleted file mode 100644 index c8f24db41ad..00000000000 --- a/docker/envs/vectorstores/oracle.env.example +++ /dev/null @@ -1,13 +0,0 @@ -# ------------------------------ -# Oracle Configuration -# ------------------------------ - -ORACLE_PASSWORD=dify -ORACLE_DSN=oracle:1521/FREEPDB1 -ORACLE_CONFIG_DIR=/app/api/storage/wallet -ORACLE_WALLET_LOCATION=/app/api/storage/wallet -ORACLE_WALLET_PASSWORD=dify -ORACLE_IS_AUTONOMOUS=false -ORACLE_USER=dify -ORACLE_PWD=Dify123456 -ORACLE_CHARACTERSET=AL32UTF8 diff --git a/docker/envs/vectorstores/pgvecto-rs.defaults b/docker/envs/vectorstores/pgvecto-rs.defaults new file mode 100644 index 00000000000..11c6b7c0479 --- /dev/null +++ b/docker/envs/vectorstores/pgvecto-rs.defaults @@ -0,0 +1,19 @@ +# Defaults for pgvecto-rs. Copy this file to pgvecto-rs.env to customize it. +# Edit the settings below in the copy; docker/.env takes precedence. +# Optional application settings are commented to retain their application defaults. + +# PGVECTO_RS_HOST=pgvecto-rs +# PGVECTO_RS_PORT=5432 +# PGVECTO_RS_USER=postgres +# PGVECTO_RS_PASSWORD=difyai123456 +# PGVECTO_RS_DATABASE=dify +PGVECTOR_PGDATA=${PGVECTOR_PGDATA:-/var/lib/postgresql/data/pgdata} +PGVECTOR_PGUSER=${PGVECTOR_PGUSER:-postgres} +PGVECTOR_POSTGRES_DB=${PGVECTOR_POSTGRES_DB:-dify} +PGVECTOR_POSTGRES_PASSWORD=${PGVECTOR_POSTGRES_PASSWORD:-difyai123456} + +# Container variable mappings; keep these expressions unchanged in the copy. +PGDATA=${PGVECTOR_PGDATA:-/var/lib/postgresql/data/pgdata} +PGUSER=${PGVECTOR_PGUSER:-postgres} +POSTGRES_DB=${PGVECTOR_POSTGRES_DB:-dify} +POSTGRES_PASSWORD=${PGVECTOR_POSTGRES_PASSWORD:-difyai123456} diff --git a/docker/envs/vectorstores/pgvecto-rs.env.example b/docker/envs/vectorstores/pgvecto-rs.env.example deleted file mode 100644 index 6428e5dd675..00000000000 --- a/docker/envs/vectorstores/pgvecto-rs.env.example +++ /dev/null @@ -1,9 +0,0 @@ -# ------------------------------ -# Pgvecto Rs Configuration -# ------------------------------ - -PGVECTO_RS_HOST=pgvecto-rs -PGVECTO_RS_PORT=5432 -PGVECTO_RS_USER=postgres -PGVECTO_RS_PASSWORD=difyai123456 -PGVECTO_RS_DATABASE=dify diff --git a/docker/envs/vectorstores/pgvector.defaults b/docker/envs/vectorstores/pgvector.defaults new file mode 100644 index 00000000000..da0e4dea62f --- /dev/null +++ b/docker/envs/vectorstores/pgvector.defaults @@ -0,0 +1,27 @@ +# Defaults for pgvector. Copy this file to pgvector.env to customize it. +# Edit the settings below in the copy; docker/.env takes precedence. +# Optional application settings are commented to retain their application defaults. + +PGVECTOR_PGUSER=${PGVECTOR_PGUSER:-postgres} +PGVECTOR_POSTGRES_PASSWORD=${PGVECTOR_POSTGRES_PASSWORD:-difyai123456} +PGVECTOR_POSTGRES_DB=${PGVECTOR_POSTGRES_DB:-dify} +PGVECTOR_PGDATA=${PGVECTOR_PGDATA:-/var/lib/postgresql/data/pgdata} +PGVECTOR_PG_BIGM=${PGVECTOR_PG_BIGM:-false} +PGVECTOR_PG_BIGM_VERSION=${PGVECTOR_PG_BIGM_VERSION:-1.2-20240606} + +# Dify vector client settings (API, workers and Beat) +# PGVECTOR_HOST=pgvector +# PGVECTOR_PORT=5432 +# PGVECTOR_USER=postgres +# PGVECTOR_PASSWORD=difyai123456 +# PGVECTOR_DATABASE=dify +# PGVECTOR_MIN_CONNECTION=1 +# PGVECTOR_MAX_CONNECTION=5 + +# Container variable mappings; keep these expressions unchanged in the copy. +PGDATA=${PGVECTOR_PGDATA:-/var/lib/postgresql/data/pgdata} +PGUSER=${PGVECTOR_PGUSER:-postgres} +PG_BIGM=${PGVECTOR_PG_BIGM:-false} +PG_BIGM_VERSION=${PGVECTOR_PG_BIGM_VERSION:-1.2-20240606} +POSTGRES_DB=${PGVECTOR_POSTGRES_DB:-dify} +POSTGRES_PASSWORD=${PGVECTOR_POSTGRES_PASSWORD:-difyai123456} diff --git a/docker/envs/vectorstores/pgvector.env.example b/docker/envs/vectorstores/pgvector.env.example deleted file mode 100644 index 9fd1dbf9628..00000000000 --- a/docker/envs/vectorstores/pgvector.env.example +++ /dev/null @@ -1,8 +0,0 @@ -# ------------------------------ -# Pgvector Configuration -# ------------------------------ - -PGVECTOR_PGUSER=postgres -PGVECTOR_POSTGRES_PASSWORD=difyai123456 -PGVECTOR_POSTGRES_DB=dify -PGVECTOR_PGDATA=/var/lib/postgresql/data/pgdata diff --git a/docker/envs/vectorstores/qdrant.defaults b/docker/envs/vectorstores/qdrant.defaults new file mode 100644 index 00000000000..1ebd9c91490 --- /dev/null +++ b/docker/envs/vectorstores/qdrant.defaults @@ -0,0 +1,12 @@ +# Defaults for qdrant. Copy this file to qdrant.env to customize it. +# Edit the settings below in the copy; docker/.env takes precedence. +# Optional application settings are commented to retain their application defaults. + +QDRANT_API_KEY=${QDRANT_API_KEY:-difyai123456} + +# Dify vector client settings (API, workers and Beat) +# QDRANT_URL=http://qdrant:6333 +# QDRANT_CLIENT_TIMEOUT=20 +# QDRANT_GRPC_ENABLED=false +# QDRANT_GRPC_PORT=6334 +# QDRANT_REPLICATION_FACTOR=1 diff --git a/docker/envs/vectorstores/qdrant.env.example b/docker/envs/vectorstores/qdrant.env.example deleted file mode 100644 index a3555fe5477..00000000000 --- a/docker/envs/vectorstores/qdrant.env.example +++ /dev/null @@ -1,4 +0,0 @@ -# ------------------------------ -# Qdrant Configuration -# ------------------------------ - diff --git a/docker/envs/vectorstores/seekdb.defaults b/docker/envs/vectorstores/seekdb.defaults new file mode 100644 index 00000000000..56faf95284f --- /dev/null +++ b/docker/envs/vectorstores/seekdb.defaults @@ -0,0 +1,10 @@ +# Defaults for seekdb. Copy this file to seekdb.env to customize it. +# Edit the settings below in the copy; docker/.env takes precedence. +# Optional application settings are commented to retain their application defaults. + +SEEKDB_MEMORY_LIMIT=${SEEKDB_MEMORY_LIMIT:-2G} +OCEANBASE_VECTOR_PASSWORD=${OCEANBASE_VECTOR_PASSWORD:-difyai123456} + +# Container variable mappings; keep these expressions unchanged in the copy. +MEMORY_LIMIT=${SEEKDB_MEMORY_LIMIT:-2G} +ROOT_PASSWORD=${OCEANBASE_VECTOR_PASSWORD:-difyai123456} diff --git a/docker/envs/vectorstores/seekdb.env.example b/docker/envs/vectorstores/seekdb.env.example deleted file mode 100644 index 4307fbede20..00000000000 --- a/docker/envs/vectorstores/seekdb.env.example +++ /dev/null @@ -1,4 +0,0 @@ -# ------------------------------ -# Seekdb Configuration -# ------------------------------ - diff --git a/docker/envs/vectorstores/weaviate.defaults b/docker/envs/vectorstores/weaviate.defaults new file mode 100644 index 00000000000..fceab9a881b --- /dev/null +++ b/docker/envs/vectorstores/weaviate.defaults @@ -0,0 +1,37 @@ +# Defaults for weaviate. Copy this file to weaviate.env to customize it. +# Edit the settings below in the copy; docker/.env takes precedence. +# Optional application settings are commented to retain their application defaults. + +WEAVIATE_PERSISTENCE_DATA_PATH=${WEAVIATE_PERSISTENCE_DATA_PATH:-/var/lib/weaviate} +WEAVIATE_QUERY_DEFAULTS_LIMIT=${WEAVIATE_QUERY_DEFAULTS_LIMIT:-25} +WEAVIATE_AUTHENTICATION_ANONYMOUS_ACCESS_ENABLED=${WEAVIATE_AUTHENTICATION_ANONYMOUS_ACCESS_ENABLED:-true} +WEAVIATE_DEFAULT_VECTORIZER_MODULE=${WEAVIATE_DEFAULT_VECTORIZER_MODULE:-none} +WEAVIATE_CLUSTER_HOSTNAME=${WEAVIATE_CLUSTER_HOSTNAME:-node1} +WEAVIATE_AUTHENTICATION_APIKEY_ENABLED=${WEAVIATE_AUTHENTICATION_APIKEY_ENABLED:-true} +WEAVIATE_AUTHENTICATION_APIKEY_ALLOWED_KEYS=${WEAVIATE_AUTHENTICATION_APIKEY_ALLOWED_KEYS:-WVF5YThaHlkYwhGUSmCRgsX3tD5ngdN8pkih} +WEAVIATE_AUTHENTICATION_APIKEY_USERS=${WEAVIATE_AUTHENTICATION_APIKEY_USERS:-hello@dify.ai} +WEAVIATE_AUTHORIZATION_ADMINLIST_ENABLED=${WEAVIATE_AUTHORIZATION_ADMINLIST_ENABLED:-true} +WEAVIATE_AUTHORIZATION_ADMINLIST_USERS=${WEAVIATE_AUTHORIZATION_ADMINLIST_USERS:-hello@dify.ai} +WEAVIATE_DISABLE_TELEMETRY=${WEAVIATE_DISABLE_TELEMETRY:-false} +WEAVIATE_ENABLE_TOKENIZER_GSE=${WEAVIATE_ENABLE_TOKENIZER_GSE:-false} +WEAVIATE_ENABLE_TOKENIZER_KAGOME_JA=${WEAVIATE_ENABLE_TOKENIZER_KAGOME_JA:-false} +WEAVIATE_ENABLE_TOKENIZER_KAGOME_KR=${WEAVIATE_ENABLE_TOKENIZER_KAGOME_KR:-false} + +# Dify vector client settings (API, workers and Beat) +# WEAVIATE_TOKENIZATION=word + +# Container variable mappings; keep these expressions unchanged in the copy. +AUTHENTICATION_ANONYMOUS_ACCESS_ENABLED=${WEAVIATE_AUTHENTICATION_ANONYMOUS_ACCESS_ENABLED:-true} +AUTHENTICATION_APIKEY_ALLOWED_KEYS=${WEAVIATE_AUTHENTICATION_APIKEY_ALLOWED_KEYS:-WVF5YThaHlkYwhGUSmCRgsX3tD5ngdN8pkih} +AUTHENTICATION_APIKEY_ENABLED=${WEAVIATE_AUTHENTICATION_APIKEY_ENABLED:-true} +AUTHENTICATION_APIKEY_USERS=${WEAVIATE_AUTHENTICATION_APIKEY_USERS:-hello@dify.ai} +AUTHORIZATION_ADMINLIST_ENABLED=${WEAVIATE_AUTHORIZATION_ADMINLIST_ENABLED:-true} +AUTHORIZATION_ADMINLIST_USERS=${WEAVIATE_AUTHORIZATION_ADMINLIST_USERS:-hello@dify.ai} +CLUSTER_HOSTNAME=${WEAVIATE_CLUSTER_HOSTNAME:-node1} +DEFAULT_VECTORIZER_MODULE=${WEAVIATE_DEFAULT_VECTORIZER_MODULE:-none} +DISABLE_TELEMETRY=${WEAVIATE_DISABLE_TELEMETRY:-false} +ENABLE_TOKENIZER_GSE=${WEAVIATE_ENABLE_TOKENIZER_GSE:-false} +ENABLE_TOKENIZER_KAGOME_JA=${WEAVIATE_ENABLE_TOKENIZER_KAGOME_JA:-false} +ENABLE_TOKENIZER_KAGOME_KR=${WEAVIATE_ENABLE_TOKENIZER_KAGOME_KR:-false} +PERSISTENCE_DATA_PATH=${WEAVIATE_PERSISTENCE_DATA_PATH:-/var/lib/weaviate} +QUERY_DEFAULTS_LIMIT=${WEAVIATE_QUERY_DEFAULTS_LIMIT:-25} diff --git a/docker/envs/vectorstores/weaviate.env.example b/docker/envs/vectorstores/weaviate.env.example deleted file mode 100644 index 82a3ccb1728..00000000000 --- a/docker/envs/vectorstores/weaviate.env.example +++ /dev/null @@ -1,18 +0,0 @@ -# ------------------------------ -# Weaviate Configuration -# ------------------------------ - -WEAVIATE_PERSISTENCE_DATA_PATH=/var/lib/weaviate -WEAVIATE_QUERY_DEFAULTS_LIMIT=25 -WEAVIATE_AUTHENTICATION_ANONYMOUS_ACCESS_ENABLED=true -WEAVIATE_DEFAULT_VECTORIZER_MODULE=none -WEAVIATE_CLUSTER_HOSTNAME=node1 -WEAVIATE_AUTHENTICATION_APIKEY_ENABLED=true -WEAVIATE_AUTHENTICATION_APIKEY_ALLOWED_KEYS=WVF5YThaHlkYwhGUSmCRgsX3tD5ngdN8pkih -WEAVIATE_AUTHENTICATION_APIKEY_USERS=hello@dify.ai -WEAVIATE_AUTHORIZATION_ADMINLIST_ENABLED=true -WEAVIATE_AUTHORIZATION_ADMINLIST_USERS=hello@dify.ai -WEAVIATE_DISABLE_TELEMETRY=false -WEAVIATE_ENABLE_TOKENIZER_GSE=false -WEAVIATE_ENABLE_TOKENIZER_KAGOME_JA=false -WEAVIATE_ENABLE_TOKENIZER_KAGOME_KR=false diff --git a/docker/generate_docker_compose b/docker/generate_docker_compose index 206cd89b44f..cd871f1b85d 100755 --- a/docker/generate_docker_compose +++ b/docker/generate_docker_compose @@ -64,50 +64,20 @@ def generate_shared_env_block(env_vars, anchor_name="shared-api-worker-env"): return "\n".join(lines) -def create_env_files_from_example(env_example_path): - """ - Creates actual env files from .env.example by copying the categorized .env.example files. - This allows docker-compose to use env_file references. - Supports per-module structure with subdirectories. - """ +def create_env_file_from_example(env_example_path): + """Create the required root .env without activating optional provider settings.""" base_dir = os.path.dirname(os.path.abspath(env_example_path)) root_env_file = os.path.join(base_dir, ".env") if not os.path.exists(root_env_file): - with open(env_example_path, "r", encoding="utf-8") as src, open( - root_env_file, "w", encoding="utf-8", newline="\n" - ) as dst: + with ( + open(env_example_path, "r", encoding="utf-8") as src, + open(root_env_file, "w", encoding="utf-8", newline="\n") as dst, + ): dst.write(src.read()) print(f"Created {root_env_file}") else: print(f"{root_env_file} already exists, skipping") - envs_dir = os.path.join(base_dir, "envs") - if not os.path.isdir(envs_dir): - print(f"No envs directory found at {envs_dir}, skipping split env files") - return [] - - created_files = [] - # Walk through all .env.example files in subdirectories - for root, dirs, files in os.walk(envs_dir): - for file in files: - if file.endswith('.env.example'): - example_file = os.path.join(root, file) - env_file = example_file.replace('.env.example', '.env') - - if os.path.exists(env_file): - print(f"{env_file} already exists, skipping") - continue - - # Copy .example to actual file - with open(example_file, "r", encoding="utf-8") as src, open( - env_file, "w", encoding="utf-8", newline="\n" - ) as dst: - dst.write(src.read()) - created_files.append(env_file) - print(f"Created {env_file}") - - return created_files - def insert_shared_env(template_path, output_path, header_comments): """ @@ -146,11 +116,8 @@ def main(): print(f"Error: File {path} does not exist.") sys.exit(1) - # Create env files from categorized .env.example files - # These files are used by docker-compose's env_file directive - # This ensures .env files exist even in CI/CD environments if "--compose-only" not in sys.argv: - create_env_files_from_example(env_example_path) + create_env_file_from_example(env_example_path) # Copy template to output with header comments # The template now uses env_file references instead of a huge YAML anchor diff --git a/docker/tests/test_compose_environment.py b/docker/tests/test_compose_environment.py new file mode 100644 index 00000000000..8de74992ea6 --- /dev/null +++ b/docker/tests/test_compose_environment.py @@ -0,0 +1,573 @@ +"""Exercise copied Docker defaults without starting containers.""" + +import json +import os +import re +import shutil +import subprocess +import sys +import tempfile +import unittest +from pathlib import Path + +DOCKER_DIR = Path(__file__).resolve().parents[1] + + +class ComposeEnvironmentTests(unittest.TestCase): + def setUp(self): + self.temp = tempfile.TemporaryDirectory(prefix="dify-compose-env-") + self.addCleanup(self.temp.cleanup) + self.project = Path(self.temp.name) + shutil.copyfile(DOCKER_DIR / "docker-compose.yaml", self.project / "docker-compose.yaml") + for source in (DOCKER_DIR / "envs").rglob("*.defaults"): + target = self.project / source.relative_to(DOCKER_DIR) + target.parent.mkdir(parents=True, exist_ok=True) + shutil.copyfile(source, target) + self.copy_template( + ".env.example", + ".env", + {"DIFY_AGENT_LOCAL_SANDBOX_AUTH_TOKEN": "test-sandbox-token"}, + ) + + def copy_template(self, source, target, values=None): + content = (DOCKER_DIR / source).read_text() + for key, value in (values or {}).items(): + content, count = re.subn( + rf"(?m)^(?:#\s*)?{re.escape(key)}=.*$", + lambda _, setting=f"{key}={value}": setting, + content, + ) + self.assertEqual(count, 1, f"Expected one setting for {key} in {source}") + destination = self.project / target + destination.parent.mkdir(parents=True, exist_ok=True) + destination.write_text(content) + + def override_root(self, values): + with (self.project / ".env").open("a") as target: + target.write("\n" + "\n".join(f"{key}={value}" for key, value in values.items()) + "\n") + + def render(self, *compose_args): + inputs = set(re.findall(r"\$\{(\w+)", (DOCKER_DIR / "docker-compose.yaml").read_text())) + for source in (DOCKER_DIR / "envs").rglob("*.defaults"): + inputs.update(re.findall(r"\$\{(\w+)", source.read_text())) + env = {key: value for key, value in os.environ.items() if key not in inputs and not key.startswith("COMPOSE_")} + result = subprocess.run( + [ + "docker", + "compose", + *compose_args, + "--profile", + "*", + "config", + "--format", + "json", + ], + cwd=self.project, + env=env, + text=True, + capture_output=True, + check=True, + ) + return json.loads(result.stdout)["services"] + + def test_minimal_setup_has_working_connection_defaults(self): + services = self.render() + for name in ("api", "worker", "worker_beat", "plugin_daemon"): + values = services[name]["environment"] + for key, expected in { + "DB_HOST": "db_postgres", + "DB_USERNAME": "postgres", + "DB_PASSWORD": "difyai123456", + "DB_PORT": "5432", + "REDIS_HOST": "redis", + "REDIS_PORT": "6379", + "REDIS_PASSWORD": "difyai123456", + }.items(): + self.assertEqual(values[key], expected, f"{name}.{key}") + self.assertEqual(services["plugin_daemon"]["environment"]["DB_DATABASE"], "dify_plugin") + self.assertEqual(services["web"]["environment"]["SERVER_CONSOLE_API_URL"], "http://api:5001") + self.assertEqual(services["web"]["environment"]["NEXT_PUBLIC_ENABLE_AGENT_V2"], "true") + self.assertEqual( + services["local_sandbox"]["environment"]["SHELLCTL_AUTH_TOKEN"], + "test-sandbox-token", + ) + self.assertEqual( + services["agent_backend"]["environment"]["DIFY_AGENT_LOCAL_SANDBOX_AUTH_TOKEN"], + "test-sandbox-token", + ) + self.assertFalse((DOCKER_DIR / ".env.custom.example").exists()) + self.assertEqual(services["elasticsearch"]["environment"]["xpack.security.enabled"], "true") + self.assertEqual(services["elasticsearch"]["environment"]["discovery.type"], "single-node") + + def test_copying_defaults_without_changes_preserves_service_values(self): + baseline = self.render() + for source in (DOCKER_DIR / "envs").rglob("*.defaults"): + # This KnowledgeFS parser file deliberately has a different override basename. + if source.name == "knowledge-fs-unstructured-service.defaults": + continue + self.copy_template( + str(source.relative_to(DOCKER_DIR)), + str(source.relative_to(DOCKER_DIR).with_suffix(".env")), + ) + actual = self.render() + for name, service in baseline.items(): + for key, value in service.get("environment", {}).items(): + self.assertEqual(actual[name]["environment"].get(key), value, f"{name}.{key}") + + def test_api_and_weaviate_defaults_and_isolated_overrides(self): + cases = [ + ("core-services/shared", "worker", "CELERY_WORKER_AMOUNT", "CELERY_WORKER_AMOUNT", "4", "2", "3"), + ("core-services/shared", "api", "SERVER_WORKER_CLASS", "SERVER_WORKER_CLASS", "gevent", "sync", "gthread"), + ("core-services/shared", "api", "GUNICORN_TIMEOUT", "GUNICORN_TIMEOUT", "360", "120", "240"), + ( + "vectorstores/weaviate", + "weaviate", + "WEAVIATE_AUTHENTICATION_ANONYMOUS_ACCESS_ENABLED", + "AUTHENTICATION_ANONYMOUS_ACCESS_ENABLED", + "true", + "false", + "true", + ), + ] + root = self.project / ".env" + initial_root = root.read_text() + for path, service, key, native, default, custom, root_value in cases: + with self.subTest(setting=key): + source = f"envs/{path}.defaults" + target = f"envs/{path}.env" + try: + self.assertEqual(self.render()[service]["environment"].get(native), default, "fresh deployment") + + # Exercise root customization without any service override file. + self.override_root({key: custom}) + self.assertEqual(self.render()[service]["environment"][native], custom, "root only") + root.write_text(initial_root) + self.assertEqual(self.render()[service]["environment"][native], default, "restored root") + + self.copy_template(source, target) + self.assertEqual(self.render()[service]["environment"][native], default, "unchanged copy") + self.copy_template(source, target, {key: custom}) + self.assertEqual(self.render()[service]["environment"][native], custom, "service only") + + # Leave the service file unchanged while changing only the root file. + self.override_root({key: root_value}) + self.assertEqual(self.render()[service]["environment"][native], root_value, "root precedence") + finally: + (self.project / target).unlink(missing_ok=True) + root.write_text(initial_root) + + def test_copying_shared_defaults_does_not_activate_optional_settings(self): + baseline = self.render() + self.copy_template("envs/core-services/shared.defaults", "envs/core-services/shared.env") + # Compare the entire configuration, including extra variables introduced by an unsafe copy. + self.assertEqual(self.render(), baseline) + + def test_consolidated_settings_reach_consumers_and_respect_root_precedence(self): + api_services = ("api", "api_websocket") + worker_services = ( + "worker", + "worker_knowledge_fs_upgrade", + "knowledge_fs_document_worker", + "knowledge_fs_source_worker", + "knowledge_fs_research_worker", + "knowledge_fs_maintenance_worker", + "knowledge_fs_dispatch_worker", + ) + vector_clients = (*api_services, "worker", "worker_knowledge_fs_upgrade", "worker_beat") + cases = [ + ( + "core-services/shared.defaults", + (*api_services, *worker_services, "worker_beat"), + { + "CODE_MAX_STRING_LENGTH": ("500000", "600000"), + "ENABLE_OTEL": ("true", "false"), + "OTLP_BASE_ENDPOINT": ("http://collector:4318", "http://root-collector:4318"), + "CELERY_BROKER_VISIBILITY_TIMEOUT": ("10800", "14400"), + "OPS_TRACE_QUEUE": ("custom_traces", "root_traces"), + "KNOWLEDGE_FS_BACKGROUND_POLL_INTERVAL_SECONDS": ("7", "9"), + "AGENT_BACKEND_HOME_SNAPSHOT_TIMEOUT_SECONDS": ("60", "90"), + "UNSTRUCTURED_API_URL": ("http://parser:8000", "http://root-parser:8000"), + "S3_ENDPOINT": ("https://storage.example.test", "https://root-storage.example.test"), + "TENCENT_COS_SECRET_KEY": ("app-storage-key", "root-storage-key"), + }, + ), + ( + "core-services/api.defaults", + api_services, + {"GUNICORN_KEEP_ALIVE": ("20", "30"), "RESPECT_XFORWARD_HEADERS_ENABLED": ("false", "true")}, + ), + ( + "core-services/worker.defaults", + worker_services, + {"CELERY_AUTO_SCALE": ("true", "false"), "CELERY_MIN_WORKERS": ("2", "3")}, + ), + ("vectorstores/weaviate.defaults", vector_clients, {"WEAVIATE_TOKENIZATION": ("field", "word")}), + ("vectorstores/qdrant.defaults", vector_clients, {"QDRANT_CLIENT_TIMEOUT": ("42", "60")}), + ( + "vectorstores/oceanbase.defaults", + vector_clients, + {"OCEANBASE_VECTOR_HOST": ("custom-oceanbase", "root-oceanbase")}, + ), + ("vectorstores/pgvector.defaults", vector_clients, {"PGVECTOR_MIN_CONNECTION": ("2", "3")}), + ("vectorstores/milvus.env.example", vector_clients, {"MILVUS_DATABASE": ("custom_dify", "root_dify")}), + ] + root = self.project / ".env" + initial_root = root.read_text() + for template, consumers, values in cases: + source = f"envs/{template}" + target = source.removesuffix(".env.example").removesuffix(".defaults") + ".env" + custom = {key: pair[0] for key, pair in values.items()} + root_values = {key: pair[1] for key, pair in values.items()} + with self.subTest(template=template): + try: + # Each stage changes one file; no other service override is present. + self.override_root(custom) + services = self.render() + for name in consumers: + for key, value in custom.items(): + self.assertEqual(services[name]["environment"][key], value, f"root only: {name}.{key}") + root.write_text(initial_root) + + self.copy_template(source, target, custom) + services = self.render() + for name in consumers: + for key, value in custom.items(): + self.assertEqual(services[name]["environment"][key], value, f"service only: {name}.{key}") + + self.override_root(root_values) + services = self.render() + for name in consumers: + for key, value in root_values.items(): + self.assertEqual( + services[name]["environment"][key], value, f"root precedence: {name}.{key}" + ) + finally: + (self.project / target).unlink(missing_ok=True) + root.write_text(initial_root) + + def test_existing_shared_overrides_and_empty_value_cleanup(self): + values = { + "API_WEBSOCKET_WORKER_AMOUNT": "3", + "CELERY_MAX_WORKERS": "5", + "QDRANT_URL": "http://existing-qdrant:6333", + "PGVECTOR_HOST": "existing-pgvector", + "MILVUS_DATABASE": "existing_dify", + "CELERY_WORKER_AMOUNT": "", + "SERVER_WORKER_CLASS": "", + "GUNICORN_TIMEOUT": "", + } + override = self.project / "envs/core-services/shared.env" + override.write_text("\n".join(f"{key}={value}" for key, value in values.items()) + "\n") + services = self.render() + for name in ("api", "api_websocket", "worker", "worker_knowledge_fs_upgrade", "worker_beat"): + for key, value in values.items(): + self.assertEqual(services[name]["environment"][key], value, f"existing override: {name}.{key}") + + # Cleaning accidental blanks in the existing file restores Docker defaults without replacing it. + override.write_text("\n".join(f"{key}={value}" for key, value in values.items() if value) + "\n") + services = self.render() + self.assertEqual(services["worker"]["environment"]["CELERY_WORKER_AMOUNT"], "4") + self.assertEqual(services["api"]["environment"]["SERVER_WORKER_CLASS"], "gevent") + self.assertEqual(services["api"]["environment"]["GUNICORN_TIMEOUT"], "360") + self.assertEqual(services["api_websocket"]["environment"]["API_WEBSOCKET_WORKER_AMOUNT"], "3") + self.assertEqual(services["worker"]["environment"]["QDRANT_URL"], values["QDRANT_URL"]) + + def test_service_overrides_and_root_precedence(self): + cases = [ + ( + "infrastructure/nginx", + "nginx", + "NGINX_CLIENT_MAX_BODY_SIZE", + "NGINX_CLIENT_MAX_BODY_SIZE", + "200M", + "300M", + ), + ( + "core-services/web", + "web", + "TOP_K_MAX_VALUE", + "TOP_K_MAX_VALUE", + "23", + "24", + ), + ( + "core-services/sandbox", + "sandbox", + "SANDBOX_WORKER_TIMEOUT", + "WORKER_TIMEOUT", + "120", + "121", + ), + ( + "core-services/plugin-daemon", + "plugin_daemon", + "PLUGIN_DAEMON_PORT", + "SERVER_PORT", + "5998", + "5999", + ), + ( + "vectorstores/weaviate", + "weaviate", + "WEAVIATE_QUERY_DEFAULTS_LIMIT", + "QUERY_DEFAULTS_LIMIT", + "66", + "67", + ), + ( + "core-services/api", + "api", + "API_SENTRY_DSN", + "SENTRY_DSN", + "https://api.example.test/1", + "https://api.example.test/2", + ), + ( + "core-services/worker", + "worker", + "API_SENTRY_DSN", + "SENTRY_DSN", + "https://worker.example.test/1", + "https://api.example.test/2", + ), + ( + "infrastructure/certbot", + "certbot", + "CERTBOT_DOMAIN", + "CERTBOT_DOMAIN", + "one.example.test", + "two.example.test", + ), + ( + "vectorstores/opensearch", + "opensearch", + "OPENSEARCH_JAVA_OPTS_MIN", + "OPENSEARCH_JAVA_OPTS", + "768m", + "896m", + ), + ] + for path, _, key, _, local, _ in cases: + self.copy_template(f"envs/{path}.defaults", f"envs/{path}.env", {key: local}) + services = self.render() + for _, service, _, native, local, _ in cases: + expected = f"-Xms{local} -Xmx1024m" if native == "OPENSEARCH_JAVA_OPTS" else local + self.assertEqual( + services[service]["environment"][native], + expected, + f"{service}.{native}", + ) + self.override_root({key: value for _, _, key, _, _, value in cases}) + services = self.render() + for _, service, _, native, _, value in cases: + expected = f"-Xms{value} -Xmx1024m" if native == "OPENSEARCH_JAVA_OPTS" else value + self.assertEqual( + services[service]["environment"][native], + expected, + f"{service}.{native}", + ) + + def test_database_redis_overrides_reach_clients_and_commands(self): + for name in ( + "shared", + "api", + "worker", + "worker-beat", + "plugin-daemon", + "dify-agent", + ): + self.copy_template(f"envs/core-services/{name}.defaults", f"envs/core-services/{name}.env") + self.copy_template( + "envs/databases/db-postgres.defaults", + "envs/databases/db-postgres.env", + { + "DB_USERNAME": "custom_user", + "DB_PASSWORD": "test-db-password", + "DB_DATABASE": "custom_dify", + "POSTGRES_MAX_CONNECTIONS": "321", + }, + ) + self.copy_template( + "envs/databases/redis.defaults", + "envs/databases/redis.env", + {"REDIS_PASSWORD": "test-redis-password"}, + ) + services = self.render() + for name in ("api", "worker", "worker_beat", "plugin_daemon"): + values = services[name]["environment"] + self.assertEqual(values["DB_USERNAME"], "custom_user", name) + self.assertEqual(values["DB_PASSWORD"], "test-db-password", name) + self.assertEqual(values["REDIS_PASSWORD"], "test-redis-password", name) + for name in ("api", "worker", "worker_beat"): + self.assertEqual( + services[name]["environment"]["CELERY_BROKER_URL"], + "redis://:test-redis-password@redis:6379/1", + name, + ) + self.assertEqual( + services["db_postgres"]["environment"]["POSTGRES_PASSWORD"], + "test-db-password", + ) + self.assertEqual(services["plugin_daemon"]["environment"]["DB_DATABASE"], "dify_plugin") + self.assertEqual(services["redis"]["environment"]["REDISCLI_AUTH"], "test-redis-password") + self.assertEqual( + services["agent_backend"]["environment"]["DIFY_AGENT_REDIS_URL"], + "redis://:test-redis-password@redis:6379/2", + ) + # Execute the command with a harmless stand-in for the image entrypoint. + executable = self.project / "docker-entrypoint.sh" + executable.write_text('#!/bin/sh\nprintf "%s\\n" "$@"\n') + executable.chmod(0o755) + for service in ("db_postgres", "db_mysql", "redis"): + config = services[service] + command = [arg.replace("$$", "$") for arg in config["command"]] + env = { + **config["environment"], + "PATH": f"{self.project}:{os.environ['PATH']}", + } + if service == "redis": + env["REDIS_PASSWORD"] = "literal $(touch should-not-exist) ; password" + result = subprocess.run( + command, + cwd=self.project, + env=env, + text=True, + capture_output=True, + check=True, + ) + args = result.stdout.splitlines() + self.assertEqual( + args[0], + { + "db_postgres": "postgres", + "db_mysql": "mysqld", + "redis": "redis-server", + }[service], + ) + if service == "db_postgres": + self.assertIn("max_connections=321", args) + if service == "redis": + self.assertEqual(args[-1], env["REDIS_PASSWORD"]) + self.assertFalse((self.project / "should-not-exist").exists()) + + def test_root_plugin_credentials_reach_every_consumer(self): + for name in ("shared", "api", "worker", "worker-beat", "plugin-daemon", "dify-agent"): + self.copy_template(f"envs/core-services/{name}.defaults", f"envs/core-services/{name}.env") + api_services = ( + "api", + "api_websocket", + "worker", + "worker_knowledge_fs_upgrade", + "knowledge_fs_document_worker", + "knowledge_fs_source_worker", + "knowledge_fs_research_worker", + "knowledge_fs_maintenance_worker", + "knowledge_fs_dispatch_worker", + ) + cases = { + "PLUGIN_DAEMON_KEY": { + **dict.fromkeys((*api_services, "worker_beat"), "PLUGIN_DAEMON_KEY"), + "plugin_daemon": "SERVER_KEY", + "agent_backend": "DIFY_AGENT_PLUGIN_DAEMON_API_KEY", + }, + "PLUGIN_DIFY_INNER_API_KEY": { + **dict.fromkeys(api_services, "INNER_API_KEY_FOR_PLUGIN"), + "plugin_daemon": "DIFY_INNER_API_KEY", + "agent_backend": "DIFY_AGENT_INNER_API_KEY", + "knowledge_fs": "DIFY_INNER_API_KEY", + }, + } + for key, consumers in cases.items(): + value = f"custom-{key.lower()}" + self.override_root({key: value}) + services = self.render() + for name, native in consumers.items(): + self.assertEqual(services[name]["environment"][native], value, f"{name}.{native}") + if key == "PLUGIN_DIFY_INNER_API_KEY": + for name in api_services: + if name.startswith("knowledge_fs_"): + self.assertEqual(services[name]["environment"]["DIFY_INNER_API_KEY"], value, name) + + def test_websocket_uses_copied_api_settings(self): + self.copy_template( + "envs/core-services/api.defaults", + "envs/core-services/api.env", + {"API_WEBSOCKET_WORKER_AMOUNT": "3"}, + ) + config = self.render()["api_websocket"] + # Stand in for the image's entrypoint; exercise the actual shell expansion. + command = [arg.replace("$$", "$") for arg in config["entrypoint"]] + command[-1] = command[-1].replace("exec /bin/bash /entrypoint.sh", 'printf "%s" "$SERVER_WORKER_AMOUNT"') + result = subprocess.run( + command, + env=config["environment"], + text=True, + capture_output=True, + check=True, + ) + self.assertEqual(result.stdout, "3") + + def test_e2b_overlay_preserves_service_credentials(self): + self.copy_template("docker-compose.e2b.yaml", "docker-compose.e2b.yaml") + self.copy_template( + "envs/core-services/dify-agent.defaults", + "envs/core-services/dify-agent.env", + { + "DIFY_AGENT_E2B_API_KEY": "service-api-key", + "DIFY_AGENT_E2B_TEMPLATE": "custom-template", + }, + ) + values = self.render("-f", "docker-compose.yaml", "-f", "docker-compose.e2b.yaml")["agent_backend"][ + "environment" + ] + self.assertEqual(values["DIFY_AGENT_RUNTIME_BACKEND"], "e2b") + self.assertEqual(values["DIFY_AGENT_E2B_API_KEY"], "service-api-key") + self.assertEqual(values["DIFY_AGENT_E2B_TEMPLATE"], "custom-template") + + def test_generator_creates_only_root_env_and_preserves_edits(self): + for source in ( + "generate_docker_compose", + ".env.example", + "docker-compose-template.yaml", + "envs/security.env.example", + ): + self.copy_template(source, source) + (self.project / ".env").unlink() + command = [sys.executable, str(self.project / "generate_docker_compose")] + subprocess.run(command, check=True, capture_output=True) + self.assertEqual( + (self.project / ".env").read_text(), + (DOCKER_DIR / ".env.example").read_text(), + ) + self.assertEqual(list((self.project / "envs").rglob("*.env")), []) + (self.project / ".env").write_text("COMPOSE_PROFILES=custom\n") + subprocess.run(command, check=True, capture_output=True) + self.assertEqual((self.project / ".env").read_text(), "COMPOSE_PROFILES=custom\n") + + def test_runtime_secrets_stay_in_their_service(self): + self.copy_template( + "envs/core-services/api.defaults", + "envs/core-services/api.env", + {"PASSWORD_PEPPER": "api-only-test-secret"}, + ) + self.copy_template( + "envs/core-services/web.defaults", + "envs/core-services/web.env", + {"NEXT_PUBLIC_ENABLE_HITL_IM": "true"}, + ) + services = self.render() + self.assertEqual(services["web"]["environment"]["NEXT_PUBLIC_ENABLE_HITL_IM"], "true") + self.assertEqual(services["api"]["environment"]["PASSWORD_PEPPER"], "api-only-test-secret") + for name in ("web", "plugin_daemon", "nginx", "db_postgres", "agent_backend"): + self.assertNotIn("PASSWORD_PEPPER", services[name]["environment"], name) + + def test_generated_compose_matches_template(self): + self.assertTrue( + (DOCKER_DIR / "docker-compose.yaml") + .read_text() + .endswith((DOCKER_DIR / "docker-compose-template.yaml").read_text()) + ) + + +if __name__ == "__main__": + unittest.main()