#!/usr/bin/env bash set -euo pipefail SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" IMAGE_NAME="${IMAGE_NAME:-deerflow-backend-offline:latest}" CONTAINER_NAME="${CONTAINER_NAME:-deerflow-backend}" BACKEND_PORT="${1:-${BACKEND_PORT:-8001}}" CODE_DIR="${CODE_DIR:-${SCRIPT_DIR}}" DATA_DIR="${DATA_DIR:-${SCRIPT_DIR}/docker-data}" CONFIG_DIR="${CONFIG_DIR:-${DATA_DIR}/config}" RUNTIME_DIR="${RUNTIME_DIR:-${DATA_DIR}/.deer-flow}" SKILLS_DIR="${SKILLS_DIR:-${DATA_DIR}/skills}" KNOWLEDGE_DIR="${KNOWLEDGE_DIR:-${DATA_DIR}/knowledge}" LOGS_DIR="${LOGS_DIR:-${DATA_DIR}/logs}" CONFIG_FILE="${CONFIG_DIR}/config.yaml" EXTENSIONS_FILE="${CONFIG_DIR}/extensions_config.json" LANGGRAPH_FILE="${CONFIG_DIR}/langgraph.json" ENV_FILE="${CONFIG_DIR}/.env" ENV_SOURCE="${CODE_DIR}/.env" # The repository-level offline deployment keeps its tracked environment file # one directory above the backend source. Prefer a backend-local file when a # deployment provides one, otherwise use that shared file. if [[ ! -f "${ENV_SOURCE}" && -f "$(dirname "${CODE_DIR}")/.env" ]]; then ENV_SOURCE="$(dirname "${CODE_DIR}")/.env" fi copy_if_missing() { local src="$1" local dst="$2" if [[ ! -e "${dst}" && -e "${src}" ]]; then mkdir -p "$(dirname "${dst}")" cp -a "${src}" "${dst}" fi } copy_dir_if_missing() { local src="$1" local dst="$2" if [[ ! -e "${dst}" && -d "${src}" ]]; then mkdir -p "$(dirname "${dst}")" cp -a "${src}" "${dst}" fi } mkdir -p "${CONFIG_DIR}" "${RUNTIME_DIR}/data" "${LOGS_DIR}" copy_if_missing "${CODE_DIR}/config.yaml" "${CONFIG_FILE}" copy_if_missing "${CODE_DIR}/extensions_config.json" "${EXTENSIONS_FILE}" copy_if_missing "${CODE_DIR}/langgraph.json" "${LANGGRAPH_FILE}" copy_if_missing "${ENV_SOURCE}" "${ENV_FILE}" touch "${ENV_FILE}" copy_dir_if_missing "${CODE_DIR}/skills" "${SKILLS_DIR}" mkdir -p "${SKILLS_DIR}" copy_dir_if_missing "${CODE_DIR}/knowledge" "${KNOWLEDGE_DIR}" mkdir -p "${KNOWLEDGE_DIR}" copy_dir_if_missing "${CODE_DIR}/.deer-flow/agents" "${RUNTIME_DIR}/agents" copy_dir_if_missing "${CODE_DIR}/.deer-flow/channels" "${RUNTIME_DIR}/channels" copy_dir_if_missing "${CODE_DIR}/.deer-flow/users" "${RUNTIME_DIR}/users" copy_if_missing "${CODE_DIR}/.deer-flow/SOUL.md" "${RUNTIME_DIR}/SOUL.md" copy_if_missing "${CODE_DIR}/.deer-flow/USER.md" "${RUNTIME_DIR}/USER.md" copy_if_missing "${CODE_DIR}/.deer-flow/system_settings.json" "${RUNTIME_DIR}/system_settings.json" if docker ps -a --format '{{.Names}}' | grep -Fxq "${CONTAINER_NAME}"; then docker rm -f "${CONTAINER_NAME}" >/dev/null fi DOCKER_ARGS=( run -d --name "${CONTAINER_NAME}" --restart unless-stopped -p "${BACKEND_PORT}:${BACKEND_PORT}" --add-host host.docker.internal:host-gateway -e "BACKEND_PORT=${BACKEND_PORT}" -e "DEER_FLOW_GATEWAY_PORT=${BACKEND_PORT}" -e "DEER_FLOW_PROJECT_ROOT=/app/backend" -e "DEER_FLOW_HOME=/app/backend/.deer-flow" -e "DEER_FLOW_HOST_BASE_DIR=${RUNTIME_DIR}" -e "DEER_FLOW_CONFIG_PATH=/app/backend/config.yaml" -e "DEER_FLOW_EXTENSIONS_CONFIG_PATH=/app/backend/extensions_config.json" -e "DEER_FLOW_SKILLS_PATH=/app/backend/skills" -v "${CODE_DIR}:/app/backend" -v "${CONFIG_FILE}:/app/backend/config.yaml" -v "${EXTENSIONS_FILE}:/app/backend/extensions_config.json" -v "${LANGGRAPH_FILE}:/app/backend/langgraph.json" -v "${ENV_FILE}:/app/backend/.env" -v "${RUNTIME_DIR}:/app/backend/.deer-flow" -v "${SKILLS_DIR}:/app/backend/skills" -v "${KNOWLEDGE_DIR}:/app/backend/knowledge" -v "${LOGS_DIR}:/app/backend/logs" ) if [[ -S /var/run/docker.sock ]]; then DOCKER_ARGS+=(-v /var/run/docker.sock:/var/run/docker.sock) fi DOCKER_ARGS+=("${IMAGE_NAME}") docker "${DOCKER_ARGS[@]}" echo "Started ${CONTAINER_NAME}" echo "Backend: http://127.0.0.1:${BACKEND_PORT}" echo "Config: ${CONFIG_DIR}" echo "Data: ${DATA_DIR}"