Skip to main content

Configuration Files

Docker Compose reads Dify’s settings from three kinds of files.
  • docker/.env: deployment-wide settings, such as Compose profiles and published ports, and the shared credentials. For a new deployment, create it from docker/.env.example.
  • .defaults files under docker/envs/: the bundled defaults, which services load automatically. Leave them unedited: each release ships new versions of them.
  • Local .env files under docker/envs/: your overrides for the services that load them. Git ignores them, so checking out a new release leaves them in place.
When you run Dify from source code, set the variables in api/.env, web/.env.local, and docker/middleware.env for the middleware containers.

Set a Variable

1

Find the File

Set most variables in a local .env file under docker/envs/, and these two kinds in docker/.env:
  • Settings Compose reads before it loads any service file, such as COMPOSE_PROFILES and VECTOR_STORE.
  • Shared credentials, so every service that uses one gets the same value:
    • PLUGIN_DAEMON_KEY and PLUGIN_DIFY_INNER_API_KEY: use a different strong value for each.
    • DIFY_AGENT_API_TOKEN and DIFY_AGENT_LOCAL_SANDBOX_AUTH_TOKEN: use a different strong value for each.
    • SANDBOX_API_KEY and CODE_EXECUTION_API_KEY: set both to the same value; if they differ, Code and Template nodes fail with HTTP 401.
For any other variable, search docker/envs/ for its name, then act on what the search finds:
  • One .defaults file: edit the local file named in its first comment. If that file doesn’t exist yet, copy the .defaults file to that name first.
  • Several .defaults files: edit the local file that every service using the variable loads; otherwise the services end up with different values.
  • An .env.example template, such as envs/security.env.example: copy it to the same name without .example. Skip envs/middleware.env.example, which only the separate middleware stack for source-code development reads.
When docker/.env and a local file set the same variable, the docker/.env value wins, with the exceptions in Precedence.
2

Edit the Value

In the local file, change the values you need and uncomment only the optional settings you want.
Leave the lines under Container variable mappings unchanged. Each passes the setting you edit to the variable name the container reads.
To go back to a bundled default, delete the line or comment it out; don’t leave it blank. A blank line such as CELERY_WORKER_AMOUNT= can still override the default with an empty value, and a blank line in docker/.env hides the value in your local file.
3

Apply the Change

From dify/docker, validate the configuration and recreate the affected containers:
Use up -d, not docker compose restart: restart keeps the containers’ old environment. To see the resolved values, run docker compose config. Its output contains secrets, so keep it private.For a worked example that ends by checking the running service, see Customize. To update to a new release without losing your local files, secrets, or data, follow Upgrade.

Precedence

When a variable is set in more than one place, which value a service uses depends on whether the service loads docker/.env:
The API, WebSocket API, workers, Beat, web, Code sandbox, plugin daemon, and agent backend use the first match, in this order:
  1. docker/.env
  2. The service’s local .env files
  3. Its .defaults files
  4. The application default
Variables under the Container variable mappings comment in a .defaults file always copy their value from another setting. Setting them directly has no effect, even in docker/.env, so change the setting they copy from instead.
If a value doesn’t come from any of these files, check two more places:
  • A variable exported in the shell where you run Compose, which wins over docker/.env for ${...} values
  • The service’s environment: entry in the Compose file, such as MODE: api, which wins over every env file

Common Variables

These URL variables configure the addresses of Dify’s various services. For single-domain deployments behind Nginx (the default Docker Compose setup), these can be left empty—the system auto-detects from the incoming request. Configure them when using custom domains, split-domain deployments, or a reverse proxy.

CONSOLE_API_URL

Default: (empty) The public URL of Dify’s backend API. Set this if you use OAuth login (GitHub, Google), Notion integration, or any plugin that requires OAuth—these features need an absolute callback URL to redirect users back after authorization. Also determines whether secure (HTTPS-only) cookies are used. Example: https://api.console.dify.ai

SERVER_CONSOLE_API_URL

Default: http://api:5001 The console API address the web frontend container uses for server-side requests, reaching the API directly over the internal Docker network rather than your public domain. Only the web frontend container uses this variable, not the Python backend; browser requests still go to CONSOLE_API_URL. If left empty, it falls back to CONSOLE_API_URL. Change it only if the API service is reachable at a different internal address.

CONSOLE_WEB_URL

Default: (empty) The public URL of Dify’s console frontend. Used to build links in all system emails (invitations, password resets, notifications) and to redirect users back to the console after OAuth login. Also serves as the default CORS allowed origin if CONSOLE_CORS_ALLOW_ORIGINS is not set. If empty, email links will be broken—even in single-domain setups, set this if you use email features. Example: https://console.dify.ai

SERVICE_API_URL

Default: (empty) The API Base URL shown to developers in the Dify console—the URL they copy into their code to call the Dify API. If empty, auto-detects from the current request (e.g., http://localhost/v1). Set this to ensure a consistent URL when your server is accessible via multiple addresses. Example: https://api.dify.ai

APP_API_URL

Default: (empty) The backend API URL for the web app frontend (published apps). This variable is only used by the web frontend container, not the Python backend. If empty, the Docker image defaults to http://127.0.0.1:5001. Example: https://api.app.dify.ai

APP_WEB_URL

Default: (empty) The public URL where published web apps are accessible. Required for the Human Input node in workflows—form links in email notifications are built as {APP_WEB_URL}/form/{token}. If empty, Human Input email delivery will not include valid form links. Example: https://app.dify.ai

TRIGGER_URL

Default: http://localhost The publicly accessible URL for webhook and integration trigger endpoints. External systems use this address to invoke your workflows. Dify builds trigger callback URLs like {TRIGGER_URL}/triggers/webhook/{id} and displays them in the console. For triggers to work from external systems, this must point to a public domain or IP address they can reach.

FILES_URL

Default: (empty; falls back to CONSOLE_API_URL) The base URL for file preview and download links. Dify generates signed, time-limited URLs for all files (uploaded documents, tool outputs, workspace logos) and serves them to the frontend and multi-modal models. Set this if you use file processing plugins, or if you want file URLs on a dedicated domain. If both FILES_URL and CONSOLE_API_URL are empty, file previews will not work. Example: https://upload.example.com or http://<your-ip>:5001

INTERNAL_FILES_URL

Default: (empty; falls back to FILES_URL) The file access URL used for communication between services inside the Docker network (e.g., plugin daemon, PDF/Word extractors). These internal services may not be able to reach the external FILES_URL if it routes through Nginx or a public domain. If empty, internal services use FILES_URL. Set this when internal services can’t reach the external URL. Example: http://api:5001

FILES_ACCESS_TIMEOUT

Default: 300 (5 minutes) How long signed file URLs remain valid, in seconds. After this time, the URL is rejected and the file must be re-requested. Increase for long-running processes; decrease for tighter security.

ENABLE_COLLABORATION_MODE

Default: true Enables real-time collaboration on the workflow canvas: simultaneous editing, in-canvas comments, and @mentions. If you access Dify on a host other than localhost, set NEXT_PUBLIC_SOCKET_URL to a WebSocket URL your browser can reach (see CORS Configuration). The default ws://localhost only works for local-only setups. To disable collaboration, set this to false. On Docker Compose, also remove collaboration from COMPOSE_PROFILES. Otherwise the dedicated api_websocket container keeps running but receives no traffic. If you use your own reverse proxy, forward Upgrade and Connection headers on /socket.io/ (the bundled nginx template already does this). Running multiple API replicas requires sticky sessions. If the editor keeps dropping and reconnecting while collaborators work on a large workflow, raise WEBSOCKET_MAX_HTTP_BUFFER_SIZE.

System Encoding

Server Configuration

Logging

General

SECRET_KEY

Default: (empty; generated and saved in configured storage) Signs authentication tokens and file URLs, and encrypts system-level OAuth client credentials. When this variable is empty, Dify reuses the key in .dify_secret_key in its configured storage, or generates and saves one if none exists. With the bundled file system storage, the host path is docker/volumes/app/storage/.dify_secret_key. A nonempty SECRET_KEY takes precedence over the stored key. To supply your own key, generate a strong random value and set it before first launch:
If Dify cannot save a generated key, initialization fails. Make the configured storage writable or provide an explicit key.
Preserve the same key through upgrades and restores, whether you supply it explicitly or keep it in persistent storage. Changing it invalidates existing authentication tokens and signed file URLs, and prevents Dify from decrypting existing system-level OAuth client credentials. Restore the original key to recover access to those credentials.

PASSWORD_PEPPER

Default: (empty) A server-side secret mixed into the passwords members use to sign in to Dify, so that a leaked copy of the database isn’t enough to crack them. Leave it empty and nothing changes. To use a pepper, set it before you create the admin account during first-time setup. Generate a high-entropy random value, for example:
On Docker Compose, set it in docker/envs/core-services/api.env. Keep it out of docker/.env, which Compose also passes to the worker, plugin daemon, agent backend, web frontend, and code sandbox. On source deployments, set it in api/.env.
Setting, changing, or removing the pepper once accounts exist makes their current passwords fail at sign-in as invalid. To undo a mistake, put back the previous value, and every password that wasn’t reset in the meantime works again. Keep the value as safe as SECRET_KEY.
To turn it on for a deployment that already has accounts, give every member a way back in afterward:
  • Reset each account’s password with flask reset-password in the API container. See Reset admin password for the full command.
  • Have members reset their own password through the forgot-password email, which requires mail delivery.
  • If ENABLE_EMAIL_CODE_LOGIN is on, members can sign in with an email code instead.

KEY_PROVIDER_TYPE

Default: local Where Dify keeps the per-workspace keys that encrypt stored credentials—model provider API keys, tool credentials, and other workspace secrets:
  • local: generates an RSA key pair per workspace and keeps the private key in the STORAGE_TYPE backend.
  • azure-keyvault: keeps each workspace’s RSA key in Azure Key Vault instead, so the private key never leaves the vault; Dify authenticates with DefaultAzureCredential (managed identity, environment variables, or Azure CLI login).
Choose the provider before the first workspace is created. Credentials encrypted by one provider cannot be decrypted by the other, so switching on a deployment that already stores credentials makes them unreadable until re-entered.

INIT_PASSWORD

Default: (empty) Optional security gate for first-time setup. When set, the /install page requires this password before the admin account can be created—preventing unauthorized setup if your server is exposed. Once setup is complete, this variable has no further effect. Maximum length: 30 characters.

Token & Request Limits

Container Startup Configuration

Only effective when starting with Docker image or Docker Compose.

MODE

Default: service-specific (api, worker, or beat in Docker Compose) Selects what the dify-api image runs at container start. The bundled Compose file sets MODE in each service’s environment, which takes precedence over env-file values. Accepted values:
  • api (and any empty or unrecognized value): the API server
  • worker: the Celery task consumer
  • beat: the scheduled-task dispatcher
  • job: run one Flask CLI command, then exit
  • migration: with MIGRATION_ENABLED=true, migrate the database and exit
Set this only in custom setups such as Kubernetes. A wrong value runs the wrong process—with no worker, indexing and mail tasks queue forever.

API Tool Configuration

Database Configuration

The database uses PostgreSQL by default. OceanBase, MySQL, and seekdb are also supported. Choose one before the first start: Dify doesn’t move stored data when you change database types. For a new deployment with bundled MySQL and Weaviate, set these values in docker/.env:
If you use another vector store, put its profile in place of weaviate. Set a custom DB_PASSWORD before the first start. To change the password later, change it on the database server first, then update DB_PASSWORD to match.

Connection Pool

These control how Dify manages its pool of database connections. The defaults work well for most deployments.

PostgreSQL Performance Tuning

These are passed as startup arguments to the PostgreSQL container—they configure the database server, not the Dify application.

MySQL Performance Tuning

These are passed as startup arguments to the MySQL container—they configure the database server, not the Dify application.

Redis Configuration

Configure these to connect Dify to your Redis instance. Dify supports three deployment modes: standalone (default), Sentinel, and Cluster.

Redis SSL Configuration

Only applies when REDIS_USE_SSL=true. These same settings are also used by the Celery broker when its URL uses the rediss:// scheme.

Redis Sentinel Mode

Sentinel provides automatic master discovery and failover for high availability. Mutually exclusive with Cluster mode.

Redis Cluster Mode

Cluster mode provides automatic sharding across multiple Redis nodes. Mutually exclusive with Sentinel mode.

Redis Connection Resilience

These tune client-side retry, timeout, and health-check behavior for Redis connections. Defaults are conservative; increase timeouts for cloud or WAN deployments where network latency is higher.

Celery Configuration

Configure the background task queue used for dataset indexing, email sending, and scheduled jobs.

CELERY_BROKER_URL

Default: redis://:difyai123456@redis:6379/1 Redis connection URL for the Celery message broker. Direct connection format:
Sentinel mode format (separate multiple nodes with semicolons):

CORS Configuration

Controls cross-domain access policies for the frontend.

Dify CLI and Programmatic API

The /openapi/v1/* surface is the user-scoped programmatic API that the Dify CLI (difyctl) and other clients authenticate against with OAuth bearer tokens. It ships disabled: set both OPENAPI_ENABLED and ENABLE_OAUTH_BEARER to true to use the CLI against your deployment.

File Storage Configuration

Configure where Dify stores uploaded files, dataset documents, and encryption keys. Each storage type has its own credential variables—configure only the one you’re using.

STORAGE_TYPE

Default: opendal Selects the file storage backend. Supported values: opendal, s3, azure-blob, aliyun-oss, google-storage, huawei-obs, volcengine-tos, tencent-cos, baidu-obs, oci-storage, supabase, clickzetta-volume, local (deprecated; internally uses OpenDAL with filesystem scheme).
Default storage backend using Apache OpenDAL, a unified interface supporting many storage services. Dify automatically scans environment variables matching OPENDAL_<SCHEME>_* and passes them to OpenDAL. For example, with OPENDAL_SCHEME=s3, set OPENDAL_S3_ACCESS_KEY_ID, OPENDAL_S3_SECRET_ACCESS_KEY, etc.For the default fs scheme:For all available schemes and their configuration options, see the OpenDAL services documentation.
ClickZetta Volume reuses the CLICKZETTA_* connection parameters configured in the Vector Database section.

Archive Storage

Separate S3-compatible storage for archiving workflow run logs. The archive-workflow-runs maintenance command packs runs older than a chosen cutoff into monthly Parquet bundles; CSV exports generated from those archives are staged in the export bucket.

Vector Database Configuration

Configure the vector databases that store knowledge base embeddings for similarity search.

VECTOR_STORE

Default: weaviate Selects the vector database for new knowledge bases. Existing knowledge bases keep using the one they were indexed in. Supported values: weaviate, oceanbase, seekdb, qdrant, milvus, myscale, relyt, pgvector, pgvecto-rs, chroma, opensearch, oracle, tencent, elasticsearch, elasticsearch-ja, analyticdb, couchbase, vikingdb, opengauss, tablestore, vastbase, tidb_vector, tidb_on_qdrant, baidu, lindorm, huawei_cloud, upstash, matrixone, clickzetta, alibabacloud_mysql, iris, hologres. Bundled providers, each with a Compose profile of the same name: weaviate, qdrant, oceanbase, seekdb, couchbase, pgvector, pgvecto-rs, vastbase, chroma, iris, oracle, milvus, opensearch, opengauss, myscale, matrixone, elasticsearch, and elasticsearch-ja. For any other provider, or an external server, omit the bundled profile. Set the connection settings in the provider’s local .env file, or in envs/core-services/api-worker.env for a provider without one. To switch to another bundled provider:
1

Keep What Existing Knowledge Bases Use

Leave the old provider’s profile in COMPOSE_PROFILES. If it isn’t Weaviate, also copy its .defaults file to its local .env file, such as qdrant.defaults to qdrant.env. elasticsearch-ja uses the same elasticsearch.env as elasticsearch. Only the selected provider’s defaults load automatically; without the copy, existing knowledge bases lose their connection.
2

Select the New Provider

In docker/.env, set VECTOR_STORE and add the provider’s profile to COMPOSE_PROFILES, then run docker compose up -d.
OceanBase and seekdb share the OCEANBASE_* settings, so Dify connects to only one of them at a time. To switch between these two, migrate existing knowledge bases instead of keeping both. To move existing knowledge bases’ data to the new provider, see Vector Database Migration. To change a provider’s connection settings, follow Set a Variable. For providers that store user passwords, change the password in the database before updating the matching client setting.
Both providers use the OCEANBASE_* variables. Set VECTOR_STORE to oceanbase or seekdb to load that provider’s connection defaults.
For the bundled PGVector, leave the client username, password, and database unset: they follow the container settings under Vector Database Service Configuration. Set them for an external server.
For the bundled Vastbase, keep the defaults of VASTBASE_USER and VASTBASE_PASSWORD: they match VB_USERNAME and VB_PASSWORD in docker-compose.yaml. For an external server, set them to its credentials.
For the bundled PGVecto.RS, leave the client username, password, and database unset: they follow the container settings under Vector Database Service Configuration. Set them for an external server.
To use the bundled OpenSearch for local development, set OPENSEARCH_VERIFY_CERTS=false in envs/vectorstores/opensearch.env: the image ships demo certificates. For production and external servers, use certificates the client trusts and keep verification on.
For both VECTOR_STORE=elasticsearch and VECTOR_STORE=elasticsearch-ja, customize the connection in envs/vectorstores/elasticsearch.env, copied from elasticsearch.defaults.

Knowledge Configuration

Annotation Import

Model Configuration

Multi-modal Configuration

All upload size limits are also gated by NGINX_CLIENT_MAX_BODY_SIZE (default 100M). If you increase any upload limit above 100 MB, also increase NGINX_CLIENT_MAX_BODY_SIZE to match—otherwise Nginx rejects the upload with a 413 error.

Sentry Configuration

Sentry provides error tracking and performance monitoring. Each service has its own DSN to separate error reporting.

Notion Integration Configuration

Connect Dify to Notion as a knowledge base data source. Get integration credentials at https://www.notion.so/my-integrations.

Mail Configuration

Dify sends emails for account invitations, password resets, login codes, and Human Input node notifications. Configure one of the three supported providers. Email links require CONSOLE_WEB_URL to be set—see Common Variables.
Three TLS modes: implicit TLS (SMTP_USE_TLS=true, SMTP_OPPORTUNISTIC_TLS=false, port 465), STARTTLS (SMTP_USE_TLS=true, SMTP_OPPORTUNISTIC_TLS=true, port 587), or plain (SMTP_USE_TLS=false, port 25).
For more details, see the SendGrid documentation.

Other Configuration

Console Features

Authentication and Accounts

By default, sign-in is email + password, and new accounts come from workspace invitations or the initial setup wizard. The toggles below relax or tighten that.

LLMOps Trace Delivery

Traces of app runs are delivered to the configured LLMOps provider (Langfuse, LangSmith, Arize/Phoenix, and so on) by a background task. For Docker Compose deployments, set these variables in docker/envs/core-services/api-worker.env.

Indexing

New User Defaults

Workflow Generation

Token & Invitation

All token expiry variables control how long a one-time-use token stored in Redis remains valid. After expiry, the user must request a new token.

Code Execution Sandbox

These settings tell the API and workers how to connect to the sandbox for Code nodes, Template nodes, Jinja2 prompts, and the built-in Code tool.

Code and Template Output Limits

The API and worker processes apply these limits to the output returned by Code and Template nodes; set them in envs/core-services/api-worker.env. The sandbox service doesn’t read them.

Workflow Runtime

Workflow Storage Repository

These select which backend implementation handles workflow execution data. The default SQLAlchemy repositories store everything in the database. Alternative implementations (e.g., Celery, Logstore) can be used for different storage strategies.

Workflow Log Cleanup

HTTP Request Node

These configure the HTTP Request node used in workflows to call external APIs.

Webhook

SSRF Protection

All outbound HTTP requests from Dify (HTTP nodes, image downloads, etc.) are routed through a proxy that blocks requests to internal/private IP ranges, preventing Server-Side Request Forgery (SSRF) attacks.

Agent Configuration

Web Frontend Service

These variables are used by the Next.js web frontend container only, and do not affect the Python backend.

NEXT_PUBLIC_ENABLE_FEATURE_PREVIEW

Default: true Enables the /create and /refine slash commands in the Go to Anything command palette, which generate and refine Workflow and Chatflow apps from a natural-language description. Set to false to hide them.

Agent (New)

The new Agent runs as extra services in the Docker Compose deployment: an agent backend, a sandbox, and a dedicated egress proxy for the sandbox. All start with docker compose up once you set DIFY_AGENT_LOCAL_SANDBOX_AUTH_TOKEN. The feature is enabled by default, and most of its settings derive from your existing Compose values, so only the variables below usually need attention.

DIFY_AGENT_SERVER_SECRET_KEY

Default: (development key in the bundled defaults; must be replaced for production) Root secret the agent backend uses to derive the keys for its internal tokens, including the bearer tokens that authenticate the sandboxed agent’s calls back into the backend. The bundled default is a publicly known development key. Anyone who knows it can forge those tokens, so replace it before production use. The value must be unpadded base64url text that decodes to exactly 32 bytes; the agent backend refuses to start on a malformed value. Generate one with:

DIFY_AGENT_API_TOKEN

Default: (development token in the bundled defaults; must be replaced for production) Bearer token the Dify API and workers present when calling the agent backend’s run API. Set it in docker/.env so the API, workers, and agent backend use the same value. The bundled default is a publicly known development token. Anyone who can reach the agent backend and knows the token can start runs, so replace it with your own random value before production use.

DIFY_AGENT_LOCAL_SANDBOX_AUTH_TOKEN

Default: (empty; required with the default local runtime backend) Shared secret that authenticates the agent backend to the bundled sandbox. Without a token, both services exit at startup and keep restarting, so set it before you start Dify. Generate one:
Set it in docker/.env so the agent backend and the bundled sandbox (local_sandbox) use the same value. It replaces the former DIFY_AGENT_SHELLCTL_AUTH_TOKEN, which Compose still reads from docker/.env when this variable is unset or empty. A value set only in envs/core-services/dify-agent.env doesn’t take effect. If yours is there, move it to docker/.env and run docker compose up -d from dify/docker.

NEXT_PUBLIC_ENABLE_AGENT_V2

Default: true (Docker Compose) Shows the new Agent in the web UI: the Agents item in the main navigation and the new Agent node in the node picker of Workflow apps. It replaces the former ENABLE_AGENT_V2, which has no effect with the current Compose files. Set it to false in docker/envs/core-services/web.env to hide the new Agent and fall back to the classic Agent node; set it back to true to show it again. Either way, recreate the web container, then hard-refresh your browser:

NEXT_PUBLIC_ENABLE_AGENT_V2_IN_CHATFLOW

Default: false Set to true to add the new Agent node to the node picker of Chatflow apps too. Works only while NEXT_PUBLIC_ENABLE_AGENT_V2 is true.

UPLOAD_SKILL_FILE_SIZE_LIMIT

Default: 50 Maximum size in MB for a skill package (.zip or .skill), whether uploaded to an agent or imported into the skill library. Larger packages are rejected with a size error. If you raise it above 100, also raise NGINX_CLIENT_MAX_BODY_SIZE to match; otherwise Nginx rejects the upload with a 413 error. This variable limits only the packed file. What’s inside the package has its own limits, set in bytes:

Agent and App Package Limits

These limits apply to every app package (.ifpkg), not only to agents: every app type exports to this format by default. They’re checked on export and on import, whether the package is uploaded as a file or imported from a URL. A package over any of them fails the whole export or import until you raise the limit it exceeds. For package uploads above 100 MB, also raise NGINX_CLIENT_MAX_BODY_SIZE; otherwise Nginx rejects them first. Each skill bundled in a package must also fit the skill limits:
  • On import, a skill over UPLOAD_SKILL_FILE_SIZE_LIMIT fails the whole import. A skill over a SKILL_PACKAGE_* limit is skipped instead, and the import finishes with a warning. To bring that skill in, raise the limit it exceeds and import the package again.
  • On export, a skill over a SKILL_PACKAGE_* limit fails the whole export until you raise that limit.

AGENT_BACKEND_BASE_URL

Default: http://agent_backend:5050 Address the API uses to reach the agent backend. Keep the default on Docker Compose. The remaining variables configure the bundled services or derive from your existing Compose values, and don’t normally need changes.

DIFY_AGENT_RUNTIME_BACKEND

Default: local Selects where agent sandboxes run:
  • local: the bundled sandbox container.
  • e2b: an E2B cloud VM for each agent’s sandbox, for stronger isolation than the bundled container provides.
  • openshell: an NVIDIA OpenShell gateway you operate, so sandboxes run on your own infrastructure.
Set this and the two callback addresses, DIFY_AGENT_STUB_API_BASE_URL and DIFY_AGENT_SANDBOX_FILES_BASE_URL, in envs/core-services/dify-agent.env. Each remote backend reads its own group of variables:
The docker/docker-compose.e2b.yaml overlay is for testing E2B with a separate, empty PostgreSQL volume; don’t apply it to an existing deployment.
Before switching to openshell, have these in place:
  • An OpenShell gateway, version 0.0.106 or later, reachable from the agent_backend container.
  • Gateway credentials: a bearer token, an mTLS client certificate pair, or both. Compose ships no certificate mount, so mount the files into agent_backend yourself, read-only and readable by UID 1001.
  • A runtime image that the gateway can start sandboxes from.
  • A shared volume for the persistent files agents keep across conversations, created outside Compose so that docker compose down -v can’t delete it. If the deployment serves tenants that don’t trust each other, give each its own OpenShell workspace and volume. A shared volume can’t reliably keep one tenant out of another’s persistent files.
  • A network path from the sandboxes back to Dify:
    • Callback addresses the sandboxes can reach.
    • A route to the agent backend’s /agent-stub path. The base Compose file doesn’t publish the agent backend’s port 5050, and that port also serves the API that starts agent runs, so expose only /agent-stub, for example through a reverse proxy.
To switch to OpenShell:
  1. See the OpenShell setup guide to set up the gateway and everything above: building the runtime image, initializing the volume, mounting certificates, Kubernetes setup, and a smoke test.
  2. In envs/core-services/dify-agent.env, set DIFY_AGENT_RUNTIME_BACKEND to openshell and fill in the variables below.
  3. Recreate the agent backend with docker compose up -d. It refuses to start if a required value is missing or malformed.
  4. Run an agent to confirm the setup. The agent backend first contacts the gateway when an agent runs, or when you click Apply in an Agent’s build chat, so a wrong endpoint, credential, or image shows up only then, as a failed run or a failed apply.
Sandboxes keep running on the gateway after a run ends, until Dify removes them, so plan gateway capacity for idle sandboxes as well as active ones.The bundled sandbox’s egress proxy doesn’t cover OpenShell sandboxes. To restrict their network access, set DIFY_AGENT_OPENSHELL_EGRESS_ALLOW.

Agent Trajectory Export

To see what your agents did in a tracing backend you already run, turn on trajectory export. The agent backend then sends traces of agent runs, with their model requests and tool calls, to an OpenTelemetry collector over OTLP/HTTP. One collector receives the runs of every workspace on the deployment. Every span carries the IDs of its workspace, app, agent, end user, conversation, workflow run, and node, so you can narrow traces to one app or one conversation by attributes such as dify.app_id or dify.conversation.id. These IDs are exported even with content export off. Trajectory export is separate from the OpenTelemetry instrumentation of the API and Worker. It isn’t separate from LLMOps Trace Delivery, which app builders set up per app with providers such as Langfuse. With Agent tracing turned on there, a run that its app traces goes only to the app’s tracing provider, and every other Agent run reaches the collector. To turn it on:
  1. In docker/envs/core-services/dify-agent.env, set DIFY_AGENT_TRAJECTORY_ENABLED to true and DIFY_AGENT_TRAJECTORY_OTLP_TRACES_ENDPOINT to your collector’s traces URL. Put any credentials in DIFY_AGENT_TRAJECTORY_OTLP_HEADERS. These variables have no effect in api-worker.env, which the agent backend doesn’t read.
  2. Recreate the agent backend with docker compose up -d. If a value is invalid, the agent backend won’t start, and agent runs fail until you fix it.
  3. Run an agent on a task that calls a tool, then look in your tracing backend for a tool-call span under the service name dify-agent-trajectory.
With DIFY_AGENT_TRAJECTORY_INCLUDE_CONTENT set to true, the collector receives prompts, system instructions, model replies, and tool arguments and results from every workspace. Restrict access to the collector as you would to the conversations themselves.

Database Service

These configure the database and Redis containers directly in Docker Compose. Rows marked middleware stack apply only to the stack that source-code deployments start, and are set in its middleware.env; the full Compose stack keeps those services on an internal network.

Sandbox Service

The sandbox service runs Code nodes, Template nodes, Jinja2 prompts, and the built-in Code tool. To replace the default API key, set SANDBOX_API_KEY and CODE_EXECUTION_API_KEY to the same strong secret in docker/.env, then run docker compose up -d. If they differ, Code and Template nodes fail with HTTP 401. Keep SANDBOX_ENABLE_NETWORK=true. Dify requests network access for every sandbox run, even when the code makes no network calls. With false, these features fail with network is disabled, please enable it in the configuration. Code in the sandbox can’t reach the internet directly: outbound HTTP and HTTPS requests go through the SSRF proxy. It can still connect directly to the API, workers, plugin daemon, and other services on the same internal network (ssrf_proxy_network). If code must not reach them, isolate the sandbox’s network yourself.

Nginx Reverse Proxy

After enabling HTTPS, also update the URL variables in Common Variables (e.g., CONSOLE_API_URL, CONSOLE_WEB_URL) to use https://.

Certbot Configuration

RESPECT_XFORWARD_HEADERS_ENABLED

Default: true on Docker Compose; false on source deployments When on, Dify takes each visitor’s IP address from the X-Forwarded-For header sent by the proxy directly in front of the API. On Docker Compose behind Cloudflare, a load balancer, or another proxy, leave this on and configure the bundled Nginx to trust your proxy. Until you do, every visitor appears with the proxy’s address and shares its limits, such as the one on sign-in emails.
With this on, anything that connects to the API directly can choose the address Dify sees. A forged address can get around per-IP rate limits and ends up in records such as an account’s last sign-in IP.Docker Compose doesn’t publish the API port, but containers on the same Docker networks, such as the code sandbox, can still connect directly. Marking a Docker network internal doesn’t stop them.Allow direct connections only from callers you trust. If those containers run code you don’t trust, isolate them with network or firewall rules. Any API calls they still need to make should go through a gateway that rewrites X-Forwarded-For.If you have published the API port, close it rather than turning this off: with this off, every visitor appears with Nginx’s address.
On source deployments, turn it on when the API runs behind a single proxy that sets X-Forwarded-For and is the only way to reach it.

SSRF Proxy

These configure the Squid-based SSRF proxy container that blocks requests to internal/private networks.

Docker Compose

Keep Compose settings in docker/.env: Compose reads them before it starts any container. When you change DB_TYPE or VECTOR_STORE, update COMPOSE_PROFILES to match; see Database Configuration and VECTOR_STORE for the steps.

ModelProvider & Tool Position Configuration

Customize which tools and model providers are available in the app interface and their display order. Use comma-separated values with no spaces between items.

Plugin Daemon Configuration

The plugin daemon is a separate service that manages plugin lifecycle (installation, execution, upgrades). The API communicates with it via HTTP.

Creator Center Configuration

Creator Center (creators.dify.ai) is the submission portal where users upload Dify apps as templates. Submitted templates are reviewed and, once approved, listed on Dify Marketplace. The Dify console opens Creator Center pages in a new browser tab. In Studio, the Publish to Marketplace action exports the app as an app package (.ifpkg) and uploads it to Creator Center, where the user completes the submission. See Publish Apps to Marketplace for the user-facing flow.

OTLP / OpenTelemetry Configuration

OpenTelemetry provides distributed tracing and metrics collection. When enabled, Dify instruments Flask and exports telemetry data to an OTLP collector.

Miscellaneous

Scheduled Tasks Configuration

Dify uses Celery Beat to run background maintenance tasks on configurable schedules.

Record Retention & Cleanup

These control the scheduled cleanup of old messages and workflow run records: anything older than the retention window is deleted in batches.

Aliyun SLS Logstore Configuration

Optional integration with Aliyun Simple Log Service for storing workflow execution logs externally instead of in the database. Enable by setting the repository configuration variables to use logstore implementations.

Event Bus Configuration

Redis-based event transport between API and Celery workers.

Vector Database Service Configuration

These variables configure the vector database containers themselves.
To require authentication, set WEAVIATE_AUTHENTICATION_ANONYMOUS_ACCESS_ENABLED=false in docker/envs/vectorstores/weaviate.env. In the same file, set WEAVIATE_API_KEY to a strong secret and include it in WEAVIATE_AUTHENTICATION_APIKEY_ALLOWED_KEYS.Replacing keys alone leaves anonymous access enabled.
Set OPENSEARCH_MEMLOCK_* and OPENSEARCH_NOFILE_* in docker/.env: Compose reads them before it loads service files.
Set these in docker/.env: Compose reads them before it loads service files.

Plugin Daemon Storage Configuration

The plugin daemon can store plugin packages in different storage backends. Configure only the provider matching PLUGIN_STORAGE_TYPE.
Last modified on November 4, 2026