# Dify Docs Env Vars

> Rule pack for the environment variable reference — en/self-host/deploy/configuration/environments.mdx. Carries the tracing procedure, description rules, verifier, and document structure. Loaded by dify-docs-write; not an entry point. Its release-sync diff is a standalone procedure invoked by dify-docs-release-sync.

- Skill: `langgenius/dify-docs-env-vars` (Agent Skill, multi-file: 4 files)
- Install (CLI): `npx skillmds@latest add langgenius/dify-docs-env-vars`
- Raw SKILL.md: https://api.skillmd.com/api/skills/langgenius/dify-docs-env-vars/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: DevOps & Infra
- Author: langgenius (https://skillmd.com/u/langgenius)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/langgenius/dify-docs-env-vars

---


# Dify Environment Variable Documentation

Not an entry point — run under `dify-docs-write`; the procedure below implements its stages for `en/self-host/deploy/configuration/environments.mdx`. Read `references/style-overrides.md` (in this skill directory — env-var-specific style rules and description anti-patterns) together with this pack. Use the ref pinned at S1; cite it in the S4 scope report.

## Standalone procedure: release-sync diff

Invoked from `dify-docs-release-sync` only. Run this before any tracing. Per-PR detection misses vars from untagged PRs, and the verifier's Missing-from-docs list hides genuinely new vars inside old backlog.

```bash
python3 .claude/skills/dify-docs-env-vars/verify-env-docs.py \
  --compare-rev <last-release-tag> <target-release-tag> \
  --repo <path-to-dify-repo> \
  --docs en/self-host/deploy/configuration/environments.mdx
```

Pin exact tags or SHAs (e.g., `--compare-rev 1.14.1 1.15.0`), never a branch name. The script prints the vars **added / removed / default-changed** between the refs, then `=== NEW vars NOT documented and NOT in ignored-vars (<n>) — TRIAGE ===`, and exits 0. Every triage var must end the task either documented or in `ignored-vars.md` with a reason — never as silent backlog.

## Procedure (S2 → S6)

Work through in order. **Every variable goes through steps 1–4 without exception** — do not skip a variable because it seems "obvious".

### Step 1 (S2): Trace each variable in the codebase

When using subagents for tracing, assign 3–5 related variables per agent. Tracing depth depends on variable type:

| Variable type | Depth |
|---|---|
| Python config vars (defined in `api/configs/`) | Full trace (below). |
| Frontend vars (mapped in `web/docker/entrypoint.sh`) | Trace the Docker-to-`NEXT_PUBLIC_*` mapping in `entrypoint.sh`; verify the default in both `docker/.env.example` and `web/.env.example`; run `grep -rn "<VAR_NAME>" <path-to-dify-repo>/api/` — any match means the var is dual-purpose and needs a full trace. |
| Docker/container service vars (only in `docker-compose.yaml`) | `grep -rn "<VAR_NAME>" <path-to-dify-repo>/api/` must return no matches; then document from `.env.example` comments. |
| Plugin daemon vars (`PLUGIN_*` not in `api/configs/`) | Document from `.env.example` comments. |

Full trace:

1. Find the definition in `api/configs/` — Pydantic field type, default, description, and any `validation_alias` (fallback) settings.
2. Find every usage — grep both the env var name and the Python attribute (`dify_config.VARIABLE_NAME`); read the surrounding code.
3. Determine behavior when empty vs set — trace fallback chains; identify what breaks.

### Step 2 (S2): Write a plain-language explanation

Cover: what the variable does in practical terms; the specific features that depend on it (name them); what happens if left empty; what happens if set; key code file paths (no line numbers — they shift). This explanation goes into the S4 scope report.

### Step 3 (S5): Write the user-facing description

- Lead with the practical impact, not the technical mechanism
- Name the features that require the variable (e.g., "Required for the Human Input node")
- Explain what breaks if misconfigured (e.g., "If empty, email links will be broken")
- Mention fallback behavior if any (e.g., "falls back to `CONSOLE_API_URL`")
- Include relationships with other variables when relevant
- Apply every rule in `references/style-overrides.md`

### Step 4 (S4 contribution): Report

The S4 scope report presents: the plain-language explanations, the proposed descriptions, and the pinned ref. The pipeline's S4 gate applies.

### Step 5 (S5/S6): Edit the documentation

Edit `en/self-host/deploy/configuration/environments.mdx` following [Document Structure](#document-structure). Update the `zh/` and `ja/` copies in the same pass, per `tools/translate/formatting-zh.md`, `tools/translate/formatting-ja.md`, and `writing-guides/glossary.md`.

## S7 verifiers

### Run the verifier

The canonical command scans BOTH env sources — never pass only one:

```bash
python3 .claude/skills/dify-docs-env-vars/verify-env-docs.py \
  --env-example <path-to-dify-repo>/docker/.env.example \
  --env-example <path-to-dify-repo>/docker/envs \
  --docs en/self-host/deploy/configuration/environments.mdx
```

`--env-example` is repeatable; a directory argument is globbed `**/*.env.example` recursively. The script first prints the list of files it parsed — confirm it shows `docker/.env.example` plus the files under `docker/envs/`. A single-source run under-scans and produces false "extra in docs" results.

Output contract: on a fully clean doc the last line is `ALL CHECKS PASSED — documentation matches .env.example` and the script exits 0; otherwise it prints `TOTAL ISSUES: <n>` with per-category counts and exits 1.

Pass bar for every task: **Extra in docs: 0** and **Default mismatches: 0**. **Missing from docs** is standing backlog and may stay nonzero, but no variable you touched may appear in it, and every release-sync-diff triage var must be resolved.

### Update `ignored-vars.md` if needed

The verifier filters out variables listed in `ignored-vars.md` (in this skill directory). When you:

- Remove a variable from the docs as Cloud-only → add it under **Cloud-only (SaaS)**.
- Skip documenting an experimental or internal flag → add it under **Experimental / internal**.
- Document a supported variable whose `.env.example` entry is commented out (`#FOO=bar`) → add it under **Verifier false positives**. This bucket is **only** for vars present in `.env.example` in commented form; see [Source of Truth](#source-of-truth) for vars absent entirely.

Every entry must include a source reference (PR, commit, or audit date).

## Source of Truth

After Dify PR #31586, the supported self-host knob surface is split across:

- `docker/.env.example` — essential startup values
- `docker/envs/**/*.env.example` — categorized optional vars (core-services, databases, infrastructure, security, vectorstores, middleware)

The verifier reads both — always use the canonical verifier command above, which passes both sources.

| Var location | Action |
|---|---|
| In any `.env.example` file, uncommented | Document. |
| In any `.env.example` file, commented (`#FOO=bar`) | Document; add to **Verifier false positives** in `ignored-vars.md` (the verifier can't parse defaults from comments). |
| Only in `api/configs/` Pydantic, not in any `.env.example` | **Don't document.** Upstream-deferred; file a PR adding it to the appropriate `.env.example` file first. |
| In `.env.example` and still parsed, but upstream-deprecated with a replacement | Keep the row; lead the description with the deprecation and the replacement: "Deprecated; use `X`." Deprecated means still parsed — a removed var never gets a Deprecated label. |
| Removed from `.env.example` because the code no longer reads it | **Remove from docs — no tombstone rows** (a documented row implies the var still takes effect). If a successor variable replaced it, add one clause to the successor's description so the old names stay findable via search: "Replaces the former `EDITION`, ignored from 1.17.0 onward." With no successor, remove without trace; upgrader discoverability belongs in upstream Dify release notes. |

**The verifier's "extra in docs" signal is not an escape hatch. Never suppress it for Pydantic-only vars via `ignored-vars.md`.**

## Document Structure

The doc groups variables by subsystem, broadly following the `docker/.env.example` and `docker/envs/**` layout (Common Variables, Server Configuration, Web Frontend Service, Database Service, and so on). Match an existing `##` section for a new variable; don't invent one. If a variable genuinely fits no section, raise it with the user rather than guessing.

| Element | Use for |
|---|---|
| Tables | Groups of related, straightforward variables (connection settings, credentials, tuning knobs). |
| Individual headings | Important variables needing explanation — enum-type selectors (`STORAGE_TYPE`, `VECTOR_STORE`) or variables where the "why" matters (`SECRET_KEY`, `FILES_URL`). |
| Tabs | Frontend variables where Docker and source deployments use different names. Tabs cannot sit inside table cells, so tabbed variables need individual headings. |
| Accordions | Provider-specific configuration (storage backends, vector databases, mail providers) — users only need one provider. |

## Reader Persona

Same audience as `en/self-host/deploy/` documentation (see the `dify-docs-guides` pack): DevOps engineers and system administrators deploying Dify. Assume strong infrastructure knowledge. Readers are actively configuring a deployment and scanning for a specific variable, not reading linearly. They need to know what each variable does, when to change it, and what breaks if they get it wrong.

