mirror of
https://github.com/langgenius/dify.git
synced 2026-05-08 20:08:36 +08:00
chore: easier and simpler deploy (#35708)
This commit is contained in:
parent
a24ec60e51
commit
1e2d309122
@ -76,10 +76,11 @@ The easiest way to start the Dify server is through [Docker Compose](docker/dock
|
||||
```bash
|
||||
cd dify
|
||||
cd docker
|
||||
cp .env.example .env
|
||||
docker compose up -d
|
||||
./dify-compose up -d
|
||||
```
|
||||
|
||||
On Windows PowerShell, run `.\dify-compose.ps1 up -d` from the `docker` directory.
|
||||
|
||||
After running, you can access the Dify dashboard in your browser at [http://localhost/install](http://localhost/install) and start the initialization process.
|
||||
|
||||
#### Seeking help
|
||||
@ -137,7 +138,7 @@ Star Dify on GitHub and be instantly notified of new releases.
|
||||
|
||||
### Custom configurations
|
||||
|
||||
If you need to customize the configuration, please refer to the comments in our [.env.example](docker/.env.example) file and update the corresponding values in your `.env` file. Additionally, you might need to make adjustments to the `docker-compose.yaml` file itself, such as changing image versions, port mappings, or volume mounts, based on your specific deployment environment and requirements. After making any changes, please re-run `docker compose up -d`. You can find the full list of available environment variables [here](https://docs.dify.ai/getting-started/install-self-hosted/environments).
|
||||
If you need to customize the configuration, add only the values you want to override to `docker/.env`. The default values live in [`docker/.env.default`](docker/.env.default), and the full reference remains in [`docker/.env.example`](docker/.env.example). After making any changes, re-run `./dify-compose up -d` or `.\dify-compose.ps1 up -d` from the `docker` directory. You can find the full list of available environment variables [here](https://docs.dify.ai/getting-started/install-self-hosted/environments).
|
||||
|
||||
### Metrics Monitoring with Grafana
|
||||
|
||||
|
||||
51
docker/.env.default
Normal file
51
docker/.env.default
Normal file
@ -0,0 +1,51 @@
|
||||
# ------------------------------------------------------------------
|
||||
# Minimal defaults for Docker Compose deployments.
|
||||
#
|
||||
# Keep local changes in .env. Use .env.example as the full reference
|
||||
# for advanced and service-specific settings.
|
||||
# ------------------------------------------------------------------
|
||||
|
||||
# Public URLs used when Dify generates links. Change these together when
|
||||
# exposing Dify under another hostname, IP address, or port.
|
||||
CONSOLE_WEB_URL=http://localhost
|
||||
SERVICE_API_URL=http://localhost
|
||||
APP_WEB_URL=http://localhost
|
||||
FILES_URL=http://localhost
|
||||
INTERNAL_FILES_URL=http://api:5001
|
||||
TRIGGER_URL=http://localhost
|
||||
ENDPOINT_URL_TEMPLATE=http://localhost/e/{hook_id}
|
||||
NEXT_PUBLIC_SOCKET_URL=ws://localhost
|
||||
EXPOSE_PLUGIN_DEBUGGING_HOST=localhost
|
||||
EXPOSE_PLUGIN_DEBUGGING_PORT=5003
|
||||
|
||||
# Built-in metadata database defaults.
|
||||
DB_TYPE=postgresql
|
||||
DB_USERNAME=postgres
|
||||
DB_PASSWORD=difyai123456
|
||||
DB_HOST=db_postgres
|
||||
DB_PORT=5432
|
||||
DB_DATABASE=dify
|
||||
|
||||
# Built-in Redis defaults.
|
||||
REDIS_HOST=redis
|
||||
REDIS_PORT=6379
|
||||
REDIS_PASSWORD=difyai123456
|
||||
|
||||
# Default file storage.
|
||||
STORAGE_TYPE=opendal
|
||||
OPENDAL_SCHEME=fs
|
||||
OPENDAL_FS_ROOT=storage
|
||||
|
||||
# Default vector database.
|
||||
VECTOR_STORE=weaviate
|
||||
|
||||
# Internal service authentication. Paired values must match.
|
||||
PLUGIN_DAEMON_KEY=lYkiYYT6owG+71oLerGzA7GXCgOT++6ovaezWAjpCjf+Sjc3ZtU+qUEi
|
||||
PLUGIN_DIFY_INNER_API_KEY=QaHbTe77CtuXmsfyhR7+vRjI/+XbV1AaFy691iy+kGDv2Jvy0/eAh8Y1
|
||||
|
||||
# Host ports.
|
||||
EXPOSE_NGINX_PORT=80
|
||||
EXPOSE_NGINX_SSL_PORT=443
|
||||
|
||||
# Docker Compose profiles for bundled services.
|
||||
COMPOSE_PROFILES=${VECTOR_STORE:-weaviate},${DB_TYPE:-postgresql}
|
||||
@ -1003,7 +1003,7 @@ NOTION_INTERNAL_SECRET=
|
||||
# ------------------------------
|
||||
|
||||
# Mail type, support: resend, smtp, sendgrid
|
||||
MAIL_TYPE=resend
|
||||
MAIL_TYPE=
|
||||
|
||||
# Default send from email address, if not specified
|
||||
# If using SendGrid, use the 'from' field for authentication if necessary.
|
||||
@ -1011,7 +1011,7 @@ MAIL_DEFAULT_SEND_FROM=
|
||||
|
||||
# API-Key for the Resend email provider, used when MAIL_TYPE is `resend`.
|
||||
RESEND_API_URL=https://api.resend.com
|
||||
RESEND_API_KEY=your-resend-api-key
|
||||
RESEND_API_KEY=
|
||||
|
||||
|
||||
# SMTP server configuration, used when MAIL_TYPE is `smtp`
|
||||
@ -1359,10 +1359,10 @@ NGINX_ENABLE_CERTBOT_CHALLENGE=false
|
||||
# ------------------------------
|
||||
|
||||
# Email address (required to get certificates from Let's Encrypt)
|
||||
CERTBOT_EMAIL=your_email@example.com
|
||||
CERTBOT_EMAIL=
|
||||
|
||||
# Domain name
|
||||
CERTBOT_DOMAIN=your_domain.com
|
||||
CERTBOT_DOMAIN=
|
||||
|
||||
# certbot command options
|
||||
# i.e: --force-renewal --dry-run --test-cert --debug
|
||||
|
||||
@ -7,28 +7,28 @@ Welcome to the new `docker` directory for deploying Dify using Docker Compose. T
|
||||
- **Certbot Container**: `docker-compose.yaml` now contains `certbot` for managing SSL certificates. This container automatically renews certificates and ensures secure HTTPS connections.\
|
||||
For more information, refer `docker/certbot/README.md`.
|
||||
|
||||
- **Persistent Environment Variables**: Environment variables are now managed through a `.env` file, ensuring that your configurations persist across deployments.
|
||||
- **Persistent Environment Variables**: Default environment variables are managed through `.env.default`, while local overrides are stored in `.env`, ensuring that your configurations persist across deployments.
|
||||
|
||||
> What is `.env`? </br> </br>
|
||||
> The `.env` file is a crucial component in Docker and Docker Compose environments, serving as a centralized configuration file where you can define environment variables that are accessible to the containers at runtime. This file simplifies the management of environment settings across different stages of development, testing, and production, providing consistency and ease of configuration to deployments.
|
||||
> The `.env` file is a local override file. Keep it small by adding only the values that differ from `.env.default`. Use `.env.example` as the full reference when you need advanced configuration.
|
||||
|
||||
- **Unified Vector Database Services**: All vector database services are now managed from a single Docker Compose file `docker-compose.yaml`. You can switch between different vector databases by setting the `VECTOR_STORE` environment variable in your `.env` file.
|
||||
|
||||
- **Mandatory .env File**: A `.env` file is now required to run `docker compose up`. This file is crucial for configuring your deployment and for any custom settings to persist through upgrades.
|
||||
- **Local .env Overrides**: The `dify-compose` and `dify-compose.ps1` wrappers create `.env` if it is missing and generate a persistent `SECRET_KEY` for this deployment.
|
||||
|
||||
### How to Deploy Dify with `docker-compose.yaml`
|
||||
|
||||
1. **Prerequisites**: Ensure Docker and Docker Compose are installed on your system.
|
||||
1. **Environment Setup**:
|
||||
- Navigate to the `docker` directory.
|
||||
- Copy the `.env.example` file to a new file named `.env` by running `cp .env.example .env`.
|
||||
- Customize the `.env` file as needed. Refer to the `.env.example` file for detailed configuration options.
|
||||
- **Optional (Recommended for upgrades)**:
|
||||
You may use the environment synchronization tool to help keep your `.env` file aligned with the latest `.env.example` updates, while preserving your custom settings.
|
||||
This is especially useful when upgrading Dify or managing a large, customized `.env` file.
|
||||
- No copy step is required. The `dify-compose` wrappers create `.env` if it is missing and write a generated `SECRET_KEY` to it.
|
||||
- When prompted on first run, press Enter to use the default deployment, or answer `y` to stop and edit `.env` first.
|
||||
- Customize `.env` only when you need to override defaults from `.env.default`. Refer to `.env.example` for the full list of available variables.
|
||||
- **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.
|
||||
1. **Running the Services**:
|
||||
- Execute `docker compose up` from the `docker` directory to start the services.
|
||||
- Execute `./dify-compose up -d` from the `docker` directory to start the services. On Windows PowerShell, run `.\dify-compose.ps1 up -d`.
|
||||
- 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`.
|
||||
1. **SSL Certificate Setup**:
|
||||
- Refer `docker/certbot/README.md` to set up SSL certificates using Certbot.
|
||||
@ -58,7 +58,13 @@ For users migrating from the `docker-legacy` setup:
|
||||
1. **Data Migration**:
|
||||
- Ensure that data from services like databases and caches is backed up and migrated appropriately to the new structure if necessary.
|
||||
|
||||
### Overview of `.env`
|
||||
### Overview of `.env.default`, `.env`, and `.env.example`
|
||||
|
||||
- `.env.default` contains the minimal default configuration for Docker Compose deployments.
|
||||
- `.env` contains the generated `SECRET_KEY` plus any local overrides.
|
||||
- `.env.example` is the full reference for advanced configuration.
|
||||
|
||||
The `dify-compose` wrappers merge `.env.default` and `.env` into a temporary environment file, append paired internal service keys when needed, and remove the temporary file after Docker Compose starts.
|
||||
|
||||
#### Key Modules and Customization
|
||||
|
||||
@ -118,9 +124,11 @@ The `.env.example` file provided in the Docker setup is extensive and covers a w
|
||||
|
||||
### Environment Variables Synchronization
|
||||
|
||||
When upgrading Dify or pulling the latest changes, new environment variables may be introduced in `.env.example`.
|
||||
When upgrading Dify or pulling the latest changes, new environment variables may be introduced in `.env.default` or `.env.example`.
|
||||
|
||||
To help keep your existing `.env` file up to date **without losing your custom values**, an optional environment variables synchronization tool is provided.
|
||||
If you use the default override-only workflow, review `.env.default` and add only the values you need to override to `.env`.
|
||||
|
||||
If you maintain a full `.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.
|
||||
@ -143,9 +151,9 @@ Before synchronization, the current `.env` file is saved to the `env-backup/` di
|
||||
|
||||
**When to use**
|
||||
|
||||
- After upgrading Dify to a newer version
|
||||
- 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
|
||||
- When managing a large or heavily customized `.env` file copied from `.env.example`
|
||||
|
||||
**Usage**
|
||||
|
||||
|
||||
334
docker/dify-compose
Executable file
334
docker/dify-compose
Executable file
@ -0,0 +1,334 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
cd "$SCRIPT_DIR"
|
||||
|
||||
DEFAULT_ENV_FILE=".env.default"
|
||||
USER_ENV_FILE=".env"
|
||||
|
||||
log() {
|
||||
printf '%s\n' "$*" >&2
|
||||
}
|
||||
|
||||
die() {
|
||||
printf 'Error: %s\n' "$*" >&2
|
||||
exit 1
|
||||
}
|
||||
|
||||
detect_compose() {
|
||||
if docker compose version >/dev/null 2>&1; then
|
||||
COMPOSE_CMD=(docker compose)
|
||||
return
|
||||
fi
|
||||
|
||||
if command -v docker-compose >/dev/null 2>&1; then
|
||||
COMPOSE_CMD=(docker-compose)
|
||||
return
|
||||
fi
|
||||
|
||||
die "Docker Compose is not available. Install Docker Compose, then run this command again."
|
||||
}
|
||||
|
||||
generate_secret_key() {
|
||||
if command -v openssl >/dev/null 2>&1; then
|
||||
openssl rand -base64 42
|
||||
return
|
||||
fi
|
||||
|
||||
if command -v dd >/dev/null 2>&1 && command -v base64 >/dev/null 2>&1; then
|
||||
dd if=/dev/urandom bs=42 count=1 2>/dev/null | base64 | tr -d '\n'
|
||||
printf '\n'
|
||||
return
|
||||
fi
|
||||
|
||||
return 1
|
||||
}
|
||||
|
||||
ensure_env_files() {
|
||||
[[ -f "$DEFAULT_ENV_FILE" ]] || die "$DEFAULT_ENV_FILE is missing."
|
||||
|
||||
if [[ -f "$USER_ENV_FILE" ]]; then
|
||||
return
|
||||
fi
|
||||
|
||||
: >"$USER_ENV_FILE"
|
||||
|
||||
if [[ ! -t 0 ]]; then
|
||||
log "Created $USER_ENV_FILE for local overrides."
|
||||
return
|
||||
fi
|
||||
|
||||
printf 'Created %s for local overrides.\n' "$USER_ENV_FILE"
|
||||
printf 'Do you need a custom deployment now? (Most users can press Enter to skip.) [y/N] '
|
||||
read -r answer
|
||||
|
||||
case "${answer:-}" in
|
||||
y | Y | yes | YES | Yes)
|
||||
cat <<'EOF'
|
||||
Edit .env with the settings you want to override, using .env.example as the full reference.
|
||||
Run ./dify-compose up -d again when you are ready.
|
||||
EOF
|
||||
exit 0
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
user_env_value() {
|
||||
local key="$1"
|
||||
awk -F= -v target="$key" '
|
||||
/^[[:space:]]*#/ || !/=/{ next }
|
||||
{
|
||||
key = $1
|
||||
gsub(/^[[:space:]]+|[[:space:]]+$/, "", key)
|
||||
if (key == target) {
|
||||
value = substr($0, index($0, "=") + 1)
|
||||
gsub(/^[[:space:]]+|[[:space:]]+$/, "", value)
|
||||
if ((value ~ /^".*"$/) || (value ~ /^'\''.*'\''$/)) {
|
||||
value = substr(value, 2, length(value) - 2)
|
||||
}
|
||||
result = value
|
||||
}
|
||||
}
|
||||
END { print result }
|
||||
' "$USER_ENV_FILE"
|
||||
}
|
||||
|
||||
set_user_env_value() {
|
||||
local key="$1"
|
||||
local value="$2"
|
||||
local temp_file
|
||||
|
||||
temp_file="$(mktemp "${TMPDIR:-/tmp}/dify-env.XXXXXX")"
|
||||
awk -F= -v target="$key" -v replacement="$key=$value" '
|
||||
BEGIN { replaced = 0 }
|
||||
/^[[:space:]]*#/ || !/=/{ print; next }
|
||||
{
|
||||
key = $1
|
||||
gsub(/^[[:space:]]+|[[:space:]]+$/, "", key)
|
||||
if (key == target) {
|
||||
if (!replaced) {
|
||||
print replacement
|
||||
replaced = 1
|
||||
}
|
||||
next
|
||||
}
|
||||
print
|
||||
}
|
||||
END {
|
||||
if (!replaced) {
|
||||
print replacement
|
||||
}
|
||||
}
|
||||
' "$USER_ENV_FILE" >"$temp_file"
|
||||
mv "$temp_file" "$USER_ENV_FILE"
|
||||
}
|
||||
|
||||
ensure_secret_key() {
|
||||
local current_secret_key
|
||||
local secret_key
|
||||
|
||||
current_secret_key="$(user_env_value SECRET_KEY)"
|
||||
if [[ -n "$current_secret_key" ]]; then
|
||||
return
|
||||
fi
|
||||
|
||||
secret_key="$(generate_secret_key)" || die "Unable to generate SECRET_KEY. Install openssl or configure SECRET_KEY in .env."
|
||||
set_user_env_value SECRET_KEY "$secret_key"
|
||||
log "Generated SECRET_KEY in $USER_ENV_FILE."
|
||||
}
|
||||
|
||||
env_value() {
|
||||
local key="$1"
|
||||
awk -F= -v target="$key" '
|
||||
/^[[:space:]]*#/ || !/=/{ next }
|
||||
{
|
||||
key = $1
|
||||
gsub(/^[[:space:]]+|[[:space:]]+$/, "", key)
|
||||
if (key == target) {
|
||||
value = substr($0, index($0, "=") + 1)
|
||||
gsub(/^[[:space:]]+|[[:space:]]+$/, "", value)
|
||||
if ((value ~ /^".*"$/) || (value ~ /^'\''.*'\''$/)) {
|
||||
value = substr(value, 2, length(value) - 2)
|
||||
}
|
||||
result = value
|
||||
}
|
||||
}
|
||||
END { print result }
|
||||
' "$DEFAULT_ENV_FILE" "$USER_ENV_FILE"
|
||||
}
|
||||
|
||||
user_overrides() {
|
||||
local key="$1"
|
||||
grep -Eq "^[[:space:]]*${key}[[:space:]]*=" "$USER_ENV_FILE"
|
||||
}
|
||||
|
||||
write_merged_env() {
|
||||
awk '
|
||||
function trim(s) {
|
||||
sub(/^[[:space:]]+/, "", s)
|
||||
sub(/[[:space:]]+$/, "", s)
|
||||
return s
|
||||
}
|
||||
|
||||
/^[[:space:]]*#/ || !/=/{ next }
|
||||
|
||||
{
|
||||
key = $0
|
||||
sub(/=.*/, "", key)
|
||||
key = trim(key)
|
||||
if (key == "") {
|
||||
next
|
||||
}
|
||||
|
||||
value = substr($0, index($0, "=") + 1)
|
||||
value = trim(value)
|
||||
|
||||
if (!(key in seen)) {
|
||||
order[++count] = key
|
||||
seen[key] = 1
|
||||
}
|
||||
|
||||
values[key] = value
|
||||
}
|
||||
|
||||
END {
|
||||
for (i = 1; i <= count; i++) {
|
||||
key = order[i]
|
||||
print key "=" values[key]
|
||||
}
|
||||
}
|
||||
' "$DEFAULT_ENV_FILE" "$USER_ENV_FILE" >"$MERGED_ENV_FILE"
|
||||
}
|
||||
|
||||
set_merged_env_value() {
|
||||
local key="$1"
|
||||
local value="$2"
|
||||
local temp_file
|
||||
|
||||
temp_file="$(mktemp "${TMPDIR:-/tmp}/dify-compose-env.XXXXXX")"
|
||||
awk -F= -v target="$key" -v replacement="$key=$value" '
|
||||
BEGIN { replaced = 0 }
|
||||
/^[[:space:]]*#/ || !/=/{ print; next }
|
||||
{
|
||||
key = $1
|
||||
gsub(/^[[:space:]]+|[[:space:]]+$/, "", key)
|
||||
if (key == target) {
|
||||
if (!replaced) {
|
||||
print replacement
|
||||
replaced = 1
|
||||
}
|
||||
next
|
||||
}
|
||||
print
|
||||
}
|
||||
END {
|
||||
if (!replaced) {
|
||||
print replacement
|
||||
}
|
||||
}
|
||||
' "$MERGED_ENV_FILE" >"$temp_file"
|
||||
mv "$temp_file" "$MERGED_ENV_FILE"
|
||||
}
|
||||
|
||||
set_if_not_overridden() {
|
||||
local key="$1"
|
||||
local value="$2"
|
||||
|
||||
if user_overrides "$key"; then
|
||||
return
|
||||
fi
|
||||
|
||||
set_merged_env_value "$key" "$value"
|
||||
}
|
||||
|
||||
metadata_db_host() {
|
||||
case "$1" in
|
||||
mysql) printf 'db_mysql' ;;
|
||||
postgresql | '') printf 'db_postgres' ;;
|
||||
*) printf '%s' "$(env_value DB_HOST)" ;;
|
||||
esac
|
||||
}
|
||||
|
||||
metadata_db_port() {
|
||||
case "$1" in
|
||||
mysql) printf '3306' ;;
|
||||
postgresql | '') printf '5432' ;;
|
||||
*) printf '%s' "$(env_value DB_PORT)" ;;
|
||||
esac
|
||||
}
|
||||
|
||||
metadata_db_user() {
|
||||
case "$1" in
|
||||
mysql) printf 'root' ;;
|
||||
postgresql | '') printf 'postgres' ;;
|
||||
*) printf '%s' "$(env_value DB_USERNAME)" ;;
|
||||
esac
|
||||
}
|
||||
|
||||
build_merged_env() {
|
||||
MERGED_ENV_FILE="$(mktemp "${TMPDIR:-/tmp}/dify-compose.XXXXXX")"
|
||||
trap 'rm -f "$MERGED_ENV_FILE"' EXIT
|
||||
|
||||
write_merged_env
|
||||
|
||||
local db_type
|
||||
local redis_host
|
||||
local redis_port
|
||||
local redis_username
|
||||
local redis_password
|
||||
local redis_auth
|
||||
local code_execution_api_key
|
||||
local weaviate_api_key
|
||||
|
||||
db_type="$(env_value DB_TYPE)"
|
||||
|
||||
set_if_not_overridden DB_HOST "$(metadata_db_host "$db_type")"
|
||||
set_if_not_overridden DB_PORT "$(metadata_db_port "$db_type")"
|
||||
set_if_not_overridden DB_USERNAME "$(metadata_db_user "$db_type")"
|
||||
|
||||
if ! user_overrides CELERY_BROKER_URL; then
|
||||
redis_host="$(env_value REDIS_HOST)"
|
||||
redis_port="$(env_value REDIS_PORT)"
|
||||
redis_username="$(env_value REDIS_USERNAME)"
|
||||
redis_password="$(env_value REDIS_PASSWORD)"
|
||||
redis_auth=""
|
||||
|
||||
if [[ -n "$redis_username" && -n "$redis_password" ]]; then
|
||||
redis_auth="${redis_username}:${redis_password}@"
|
||||
elif [[ -n "$redis_password" ]]; then
|
||||
redis_auth=":${redis_password}@"
|
||||
elif [[ -n "$redis_username" ]]; then
|
||||
redis_auth="${redis_username}@"
|
||||
fi
|
||||
|
||||
set_merged_env_value CELERY_BROKER_URL "redis://${redis_auth}${redis_host:-redis}:${redis_port:-6379}/1"
|
||||
fi
|
||||
|
||||
if ! user_overrides SANDBOX_API_KEY; then
|
||||
code_execution_api_key="$(env_value CODE_EXECUTION_API_KEY)"
|
||||
set_if_not_overridden SANDBOX_API_KEY "${code_execution_api_key:-dify-sandbox}"
|
||||
fi
|
||||
|
||||
if ! user_overrides WEAVIATE_AUTHENTICATION_APIKEY_ALLOWED_KEYS; then
|
||||
weaviate_api_key="$(env_value WEAVIATE_API_KEY)"
|
||||
set_if_not_overridden WEAVIATE_AUTHENTICATION_APIKEY_ALLOWED_KEYS \
|
||||
"${weaviate_api_key:-WVF5YThaHlkYwhGUSmCRgsX3tD5ngdN8pkih}"
|
||||
fi
|
||||
}
|
||||
|
||||
main() {
|
||||
detect_compose
|
||||
ensure_env_files
|
||||
ensure_secret_key
|
||||
build_merged_env
|
||||
|
||||
if [[ "$#" -eq 0 ]]; then
|
||||
set -- up -d
|
||||
fi
|
||||
|
||||
"${COMPOSE_CMD[@]}" --env-file "$MERGED_ENV_FILE" "$@"
|
||||
}
|
||||
|
||||
main "$@"
|
||||
317
docker/dify-compose.ps1
Normal file
317
docker/dify-compose.ps1
Normal file
@ -0,0 +1,317 @@
|
||||
$ErrorActionPreference = "Stop"
|
||||
Set-StrictMode -Version Latest
|
||||
|
||||
$ScriptDir = Split-Path -Parent $MyInvocation.MyCommand.Path
|
||||
Set-Location $ScriptDir
|
||||
|
||||
$DefaultEnvFile = ".env.default"
|
||||
$UserEnvFile = ".env"
|
||||
$MergedEnvFile = $null
|
||||
$Utf8NoBom = New-Object System.Text.UTF8Encoding -ArgumentList $false
|
||||
|
||||
function Write-Info {
|
||||
param([string]$Message)
|
||||
[Console]::Error.WriteLine($Message)
|
||||
}
|
||||
|
||||
function Fail {
|
||||
param([string]$Message)
|
||||
[Console]::Error.WriteLine("Error: $Message")
|
||||
exit 1
|
||||
}
|
||||
|
||||
function Test-CommandSuccess {
|
||||
param([string[]]$Command)
|
||||
|
||||
try {
|
||||
$Executable = $Command[0]
|
||||
$CommandArgs = @()
|
||||
if ($Command.Length -gt 1) {
|
||||
$CommandArgs = @($Command[1..($Command.Length - 1)])
|
||||
}
|
||||
|
||||
& $Executable @CommandArgs *> $null
|
||||
return $LASTEXITCODE -eq 0
|
||||
}
|
||||
catch {
|
||||
return $false
|
||||
}
|
||||
}
|
||||
|
||||
function Get-ComposeCommand {
|
||||
if (Test-CommandSuccess @("docker", "compose", "version")) {
|
||||
return @("docker", "compose")
|
||||
}
|
||||
|
||||
if ((Get-Command "docker-compose" -ErrorAction SilentlyContinue) -and (Test-CommandSuccess @("docker-compose", "version"))) {
|
||||
return @("docker-compose")
|
||||
}
|
||||
|
||||
Fail "Docker Compose is not available. Install Docker Compose, then run this command again."
|
||||
}
|
||||
|
||||
function New-SecretKey {
|
||||
$Bytes = New-Object byte[] 42
|
||||
$Generator = [System.Security.Cryptography.RandomNumberGenerator]::Create()
|
||||
|
||||
try {
|
||||
$Generator.GetBytes($Bytes)
|
||||
}
|
||||
finally {
|
||||
$Generator.Dispose()
|
||||
}
|
||||
|
||||
return [Convert]::ToBase64String($Bytes)
|
||||
}
|
||||
|
||||
function Ensure-EnvFiles {
|
||||
if (-not (Test-Path $DefaultEnvFile -PathType Leaf)) {
|
||||
Fail "$DefaultEnvFile is missing."
|
||||
}
|
||||
|
||||
if (Test-Path $UserEnvFile -PathType Leaf) {
|
||||
return
|
||||
}
|
||||
|
||||
New-Item -ItemType File -Path $UserEnvFile | Out-Null
|
||||
|
||||
if ([Console]::IsInputRedirected) {
|
||||
Write-Info "Created $UserEnvFile for local overrides."
|
||||
return
|
||||
}
|
||||
|
||||
Write-Info "Created $UserEnvFile for local overrides."
|
||||
$Answer = Read-Host "Do you need a custom deployment now? (Most users can press Enter to skip.) [y/N]"
|
||||
|
||||
if ($Answer -match "^(y|yes)$") {
|
||||
Write-Output "Edit .env with the settings you want to override, using .env.example as the full reference."
|
||||
Write-Output "Run .\dify-compose.ps1 up -d again when you are ready."
|
||||
exit 0
|
||||
}
|
||||
}
|
||||
|
||||
function Read-EnvFile {
|
||||
param([string]$Path)
|
||||
|
||||
$Values = [ordered]@{}
|
||||
|
||||
if (-not (Test-Path $Path -PathType Leaf)) {
|
||||
return $Values
|
||||
}
|
||||
|
||||
foreach ($Line in Get-Content -Path $Path) {
|
||||
if ($Line -match "^\s*#" -or $Line -notmatch "=") {
|
||||
continue
|
||||
}
|
||||
|
||||
$SeparatorIndex = $Line.IndexOf("=")
|
||||
$Key = $Line.Substring(0, $SeparatorIndex).Trim()
|
||||
$Value = $Line.Substring($SeparatorIndex + 1).Trim()
|
||||
|
||||
if (($Value.StartsWith('"') -and $Value.EndsWith('"')) -or ($Value.StartsWith("'") -and $Value.EndsWith("'"))) {
|
||||
$Value = $Value.Substring(1, $Value.Length - 2)
|
||||
}
|
||||
|
||||
if ($Key.Length -gt 0) {
|
||||
$Values[$Key] = $Value
|
||||
}
|
||||
}
|
||||
|
||||
return $Values
|
||||
}
|
||||
|
||||
function Set-UserEnvValue {
|
||||
param(
|
||||
[string]$Key,
|
||||
[string]$Value
|
||||
)
|
||||
|
||||
$Path = [string](Resolve-Path $UserEnvFile)
|
||||
$Lines = [System.IO.File]::ReadAllLines($Path, [System.Text.Encoding]::UTF8)
|
||||
$Output = New-Object System.Collections.Generic.List[string]
|
||||
$Replaced = $false
|
||||
|
||||
foreach ($Line in $Lines) {
|
||||
if ($Line -match "^\s*#" -or $Line -notmatch "=") {
|
||||
$Output.Add($Line)
|
||||
continue
|
||||
}
|
||||
|
||||
$SeparatorIndex = $Line.IndexOf("=")
|
||||
$CurrentKey = $Line.Substring(0, $SeparatorIndex).Trim()
|
||||
|
||||
if ($CurrentKey -eq $Key) {
|
||||
if (-not $Replaced) {
|
||||
$Output.Add("$Key=$Value")
|
||||
$Replaced = $true
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
$Output.Add($Line)
|
||||
}
|
||||
|
||||
if (-not $Replaced) {
|
||||
$Output.Add("$Key=$Value")
|
||||
}
|
||||
|
||||
[System.IO.File]::WriteAllLines($Path, $Output, $Utf8NoBom)
|
||||
}
|
||||
|
||||
function Ensure-SecretKey {
|
||||
$Values = Read-EnvFile $UserEnvFile
|
||||
|
||||
if ($Values.Contains("SECRET_KEY") -and $Values["SECRET_KEY"]) {
|
||||
return
|
||||
}
|
||||
|
||||
Set-UserEnvValue "SECRET_KEY" (New-SecretKey)
|
||||
Write-Info "Generated SECRET_KEY in $UserEnvFile."
|
||||
}
|
||||
|
||||
function Merge-EnvValues {
|
||||
$Values = [ordered]@{}
|
||||
|
||||
foreach ($Entry in (Read-EnvFile $DefaultEnvFile).GetEnumerator()) {
|
||||
$Values[$Entry.Key] = $Entry.Value
|
||||
}
|
||||
|
||||
foreach ($Entry in (Read-EnvFile $UserEnvFile).GetEnumerator()) {
|
||||
$Values[$Entry.Key] = $Entry.Value
|
||||
}
|
||||
|
||||
return $Values
|
||||
}
|
||||
|
||||
function User-Overrides {
|
||||
param([string]$Key)
|
||||
|
||||
if (-not (Test-Path $UserEnvFile -PathType Leaf)) {
|
||||
return $false
|
||||
}
|
||||
|
||||
return [bool](Select-String -Path $UserEnvFile -Pattern "^\s*$([regex]::Escape($Key))\s*=" -Quiet)
|
||||
}
|
||||
|
||||
function Metadata-DbHost {
|
||||
param([string]$DbType, $Values)
|
||||
|
||||
switch ($DbType) {
|
||||
"mysql" { return "db_mysql" }
|
||||
"postgresql" { return "db_postgres" }
|
||||
"" { return "db_postgres" }
|
||||
default { return $Values["DB_HOST"] }
|
||||
}
|
||||
}
|
||||
|
||||
function Metadata-DbPort {
|
||||
param([string]$DbType, $Values)
|
||||
|
||||
switch ($DbType) {
|
||||
"mysql" { return "3306" }
|
||||
"postgresql" { return "5432" }
|
||||
"" { return "5432" }
|
||||
default { return $Values["DB_PORT"] }
|
||||
}
|
||||
}
|
||||
|
||||
function Metadata-DbUser {
|
||||
param([string]$DbType, $Values)
|
||||
|
||||
switch ($DbType) {
|
||||
"mysql" { return "root" }
|
||||
"postgresql" { return "postgres" }
|
||||
"" { return "postgres" }
|
||||
default { return $Values["DB_USERNAME"] }
|
||||
}
|
||||
}
|
||||
|
||||
function Write-MergedEnv {
|
||||
param($Values)
|
||||
|
||||
$Output = New-Object System.Collections.Generic.List[string]
|
||||
|
||||
foreach ($Entry in $Values.GetEnumerator()) {
|
||||
$Output.Add("$($Entry.Key)=$($Entry.Value)")
|
||||
}
|
||||
|
||||
[System.IO.File]::WriteAllLines($MergedEnvFile, $Output, $Utf8NoBom)
|
||||
}
|
||||
|
||||
function Build-MergedEnv {
|
||||
$Values = Merge-EnvValues
|
||||
$script:MergedEnvFile = [System.IO.Path]::GetTempFileName()
|
||||
|
||||
$DbType = if ($Values.Contains("DB_TYPE")) { $Values["DB_TYPE"] } else { "postgresql" }
|
||||
|
||||
if (-not (User-Overrides "DB_HOST")) {
|
||||
$Values["DB_HOST"] = Metadata-DbHost $DbType $Values
|
||||
}
|
||||
|
||||
if (-not (User-Overrides "DB_PORT")) {
|
||||
$Values["DB_PORT"] = Metadata-DbPort $DbType $Values
|
||||
}
|
||||
|
||||
if (-not (User-Overrides "DB_USERNAME")) {
|
||||
$Values["DB_USERNAME"] = Metadata-DbUser $DbType $Values
|
||||
}
|
||||
|
||||
if (-not (User-Overrides "CELERY_BROKER_URL")) {
|
||||
$RedisHost = if ($Values.Contains("REDIS_HOST") -and $Values["REDIS_HOST"]) { $Values["REDIS_HOST"] } else { "redis" }
|
||||
$RedisPort = if ($Values.Contains("REDIS_PORT") -and $Values["REDIS_PORT"]) { $Values["REDIS_PORT"] } else { "6379" }
|
||||
$RedisUsername = if ($Values.Contains("REDIS_USERNAME")) { $Values["REDIS_USERNAME"] } else { "" }
|
||||
$RedisPassword = if ($Values.Contains("REDIS_PASSWORD")) { $Values["REDIS_PASSWORD"] } else { "" }
|
||||
$RedisAuth = ""
|
||||
|
||||
if ($RedisUsername -and $RedisPassword) {
|
||||
$RedisAuth = "${RedisUsername}:${RedisPassword}@"
|
||||
}
|
||||
elseif ($RedisPassword) {
|
||||
$RedisAuth = ":${RedisPassword}@"
|
||||
}
|
||||
elseif ($RedisUsername) {
|
||||
$RedisAuth = "${RedisUsername}@"
|
||||
}
|
||||
|
||||
$Values["CELERY_BROKER_URL"] = "redis://$RedisAuth${RedisHost}:${RedisPort}/1"
|
||||
}
|
||||
|
||||
if (-not (User-Overrides "SANDBOX_API_KEY")) {
|
||||
$CodeExecutionApiKey = if ($Values.Contains("CODE_EXECUTION_API_KEY") -and $Values["CODE_EXECUTION_API_KEY"]) { $Values["CODE_EXECUTION_API_KEY"] } else { "dify-sandbox" }
|
||||
$Values["SANDBOX_API_KEY"] = $CodeExecutionApiKey
|
||||
}
|
||||
|
||||
if (-not (User-Overrides "WEAVIATE_AUTHENTICATION_APIKEY_ALLOWED_KEYS")) {
|
||||
$WeaviateApiKey = if ($Values.Contains("WEAVIATE_API_KEY") -and $Values["WEAVIATE_API_KEY"]) { $Values["WEAVIATE_API_KEY"] } else { "WVF5YThaHlkYwhGUSmCRgsX3tD5ngdN8pkih" }
|
||||
$Values["WEAVIATE_AUTHENTICATION_APIKEY_ALLOWED_KEYS"] = $WeaviateApiKey
|
||||
}
|
||||
|
||||
Write-MergedEnv $Values
|
||||
}
|
||||
|
||||
$ComposeCommand = Get-ComposeCommand
|
||||
|
||||
try {
|
||||
Ensure-EnvFiles
|
||||
Ensure-SecretKey
|
||||
Build-MergedEnv
|
||||
|
||||
$ComposeArgs = @($args)
|
||||
if ($ComposeArgs.Count -eq 0) {
|
||||
$ComposeArgs = @("up", "-d")
|
||||
}
|
||||
|
||||
$ComposeCommandArgs = @()
|
||||
if ($ComposeCommand.Length -gt 1) {
|
||||
$ComposeCommandArgs = @($ComposeCommand[1..($ComposeCommand.Length - 1)])
|
||||
}
|
||||
|
||||
$ComposeExecutable = $ComposeCommand[0]
|
||||
& $ComposeExecutable @ComposeCommandArgs --env-file $MergedEnvFile @ComposeArgs
|
||||
exit $LASTEXITCODE
|
||||
}
|
||||
finally {
|
||||
if ($MergedEnvFile -and (Test-Path $MergedEnvFile -PathType Leaf)) {
|
||||
Remove-Item -Force $MergedEnvFile
|
||||
}
|
||||
}
|
||||
@ -170,8 +170,8 @@ services:
|
||||
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:-}
|
||||
INDEXING_MAX_SEGMENTATION_TOKENS_LENGTH: ${INDEXING_MAX_SEGMENTATION_TOKENS_LENGTH:-}
|
||||
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}
|
||||
@ -402,8 +402,8 @@ services:
|
||||
- ./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_EMAIL=${CERTBOT_EMAIL:-}
|
||||
- CERTBOT_DOMAIN=${CERTBOT_DOMAIN:-}
|
||||
- CERTBOT_OPTIONS=${CERTBOT_OPTIONS:-}
|
||||
entrypoint: ["/docker-entrypoint.sh"]
|
||||
command: ["tail", "-f", "/dev/null"]
|
||||
|
||||
@ -441,10 +441,10 @@ x-shared-env: &shared-api-worker-env
|
||||
NOTION_CLIENT_SECRET: ${NOTION_CLIENT_SECRET:-}
|
||||
NOTION_CLIENT_ID: ${NOTION_CLIENT_ID:-}
|
||||
NOTION_INTERNAL_SECRET: ${NOTION_INTERNAL_SECRET:-}
|
||||
MAIL_TYPE: ${MAIL_TYPE:-resend}
|
||||
MAIL_TYPE: ${MAIL_TYPE:-}
|
||||
MAIL_DEFAULT_SEND_FROM: ${MAIL_DEFAULT_SEND_FROM:-}
|
||||
RESEND_API_URL: ${RESEND_API_URL:-https://api.resend.com}
|
||||
RESEND_API_KEY: ${RESEND_API_KEY:-your-resend-api-key}
|
||||
RESEND_API_KEY: ${RESEND_API_KEY:-}
|
||||
SMTP_SERVER: ${SMTP_SERVER:-}
|
||||
SMTP_PORT: ${SMTP_PORT:-465}
|
||||
SMTP_USERNAME: ${SMTP_USERNAME:-}
|
||||
@ -586,8 +586,8 @@ x-shared-env: &shared-api-worker-env
|
||||
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}
|
||||
CERTBOT_EMAIL: ${CERTBOT_EMAIL:-your_email@example.com}
|
||||
CERTBOT_DOMAIN: ${CERTBOT_DOMAIN:-your_domain.com}
|
||||
CERTBOT_EMAIL: ${CERTBOT_EMAIL:-}
|
||||
CERTBOT_DOMAIN: ${CERTBOT_DOMAIN:-}
|
||||
CERTBOT_OPTIONS: ${CERTBOT_OPTIONS:-}
|
||||
SSRF_HTTP_PORT: ${SSRF_HTTP_PORT:-3128}
|
||||
SSRF_COREDUMP_DIR: ${SSRF_COREDUMP_DIR:-/var/spool/squid}
|
||||
@ -894,8 +894,8 @@ services:
|
||||
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:-}
|
||||
INDEXING_MAX_SEGMENTATION_TOKENS_LENGTH: ${INDEXING_MAX_SEGMENTATION_TOKENS_LENGTH:-}
|
||||
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}
|
||||
@ -1126,8 +1126,8 @@ services:
|
||||
- ./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_EMAIL=${CERTBOT_EMAIL:-}
|
||||
- CERTBOT_DOMAIN=${CERTBOT_DOMAIN:-}
|
||||
- CERTBOT_OPTIONS=${CERTBOT_OPTIONS:-}
|
||||
entrypoint: ["/docker-entrypoint.sh"]
|
||||
command: ["tail", "-f", "/dev/null"]
|
||||
|
||||
Loading…
Reference in New Issue
Block a user