Phala 部署教程

Reno 于 2025-06-01 发布

Phala 部署教程

注意查看自己的prod,截止博客更新时,prod默认为7

AList

环境变量

CLOUDFLARE_API_TOKEN=your_cloudflare_api_token_here
CERTBOT_EMAIL=your_email@example.com
DOMAIN=alist.xxx.com
POSTGRES_PASSWORD=your_password

容器编排

services:
  initial_setup:
    image: docker.io/alpine:latest
    container_name: initial_setup
    environment:
      - DOMAIN=${DOMAIN}
      - POSTGRES_PASSWORD=${POSTGRES_PASSWORD}
    networks:
      - default
    volumes:
      - alist-data:/opt/alist/data
    command:
      - sh
      - -c
      - |
        echo "🚀 初始化设置 - 域名: ${DOMAIN:-'未设置'}"
        
        # 安装必要工具
        apk add --no-cache curl > /dev/null 2>&1

        # 初始化配置文件
        echo "📦 初始化目录"
        mkdir -p /opt/alist/data
        echo "🔽 下载 config.json..."
        cd /opt/alist/data
        curl -s -L -o config.json https://raw.githubusercontent.com/Raimbaulty/freebsd/main/alist.json
        if [ $? -ne 0 ]; then
          echo "❌ config.json 下载失败!"
          exit 1
        fi

        # 替换环境变量
        if [ -n "$DOMAIN" ]; then
          sed -i "s/alist.com/$DOMAIN/g" /opt/alist/data/config.json
        else
          echo "❌ 未设置 DOMAIN 环境变量"
          exit 1
        fi
        if [ -n "$POSTGRES_PASSWORD" ]; then
          sed -i "s/alist_password/$POSTGRES_PASSWORD/g" /opt/alist/data/config.json
        else
          echo "❌ 未设置 POSTGRES_PASSWORD 环境变量"
          exit 1
        fi
        
        # 验证配置
        if [ ! -s "/opt/alist/data/config.json" ]; then
          echo "❌ 配置文件验证失败!"
          exit 1
        fi

        echo "✅ 初始化完成 - 访问地址: https://$DOMAIN"

  dstack-ingress:
    image: kvin/dstack-ingress:250428
    ports:
      - "443:443"
    networks:
      - default
    environment:
      - GATEWAY_DOMAIN=_.dstack-prod7.phala.network    
      - CLOUDFLARE_API_TOKEN=${CLOUDFLARE_API_TOKEN}
      - CERTBOT_EMAIL=${CERTBOT_EMAIL}
      - SET_CAA=true
      - DOMAIN=${DOMAIN}      
      - TARGET_ENDPOINT=http://alist:5244
    volumes:
      - cert-data:/etc/letsencrypt
      - /var/run/tappd.sock:/var/run/tappd.sock
    restart: unless-stopped

  alist_db:
    container_name: alist_db
    image: postgres:17
    networks:
      - internal
    restart: always
    environment:
      - POSTGRES_USER=alist
      - POSTGRES_PASSWORD=${POSTGRES_PASSWORD}
      - POSTGRES_DB=alist
    volumes:
      - postgres-data:/var/lib/postgresql/data

  alist:
    image: 'xhofe/alist:beta-aio'
    container_name: alist
    volumes:
      - alist-data:/opt/alist/data
    ports:
      - '5244:5244'
    networks:
      - internal
      - default
    environment:
      - PUID=0
      - PGID=0
      - UMASK=022
      - TZ=Asia/Shanghai
    depends_on:
      - alist_db
      - dstack-ingress
      - initial_setup
    restart: unless-stopped

networks:
  internal:
    driver: bridge
    internal: true

volumes:
  cert-data:
    name: cert-data
  alist-data:
    name: alist-data
  postgres-data:
    name: postgres-data

n8n

方案一

官方 custom domain 方案,但是 ws 连接有问题

环境变量

CLOUDFLARE_API_TOKEN=your_cloudflare_api_token_here
CERTBOT_EMAIL=your_email@example.com
DOMAIN=n8n.xxx.com
POSTGRES_PASSWORD=your_postgres_password
REDIS_PASSWORD=your_redis_password
WEBHOOK_URL=https://n8n.xxx.com
VUE_APP_URL_BASE_API=https://n8n.xxx.com

容器编排

services:
  initial_setup:
    image: docker.io/alpine:latest
    container_name: initial_setup
    environment:
      - DOMAIN=${DOMAIN}
    volumes:
      - nginx-data:/etc/nginx
      - dist-data:/dist
    command:
      - sh
      - -c
      - |
        echo "🚀 初始化设置 - 域名: ${DOMAIN:-'未设置'}"
        
        # 安装必要工具
        apk add --no-cache curl > /dev/null 2>&1

        # 初始化 editor-ui
        echo "📦 初始化 editor-ui..."
        mkdir -p /dist
        echo "🔽 下载 editor-ui.tar.gz..."
        cd /dist
        curl -s -L -o editor-ui.tar.gz https://github.com/other-blowsnow/n8n-i18n-chinese/releases/latest/download/editor-ui.tar.gz
        if [ $? -ne 0 ]; then
          echo "❌ editor-ui.tar.gz 下载失败!"
          exit 1
        fi
        
        echo "📂 解压 editor-ui..."
        tar -xzf editor-ui.tar.gz --strip-components=1
        if [ $? -ne 0 ]; then
          echo "❌ editor-ui 解压失败!"
          exit 1
        fi
        
        rm -f editor-ui.tar.gz
        echo "✅ editor-ui 初始化完成!"        
        
        # 配置 Nginx
        echo "🌐 配置 Nginx..."
        mkdir -p /etc/nginx/conf.d
        curl -s -L -o /etc/nginx/nginx.conf https://raw.githubusercontent.com/Raimbaulty/freebsd/main/n8n.conf
        if [ $? -ne 0 ]; then
          echo "❌ nginx.conf 下载失败!"
          exit 1
        fi
        
        # 替换域名变量
        if [ -n "$DOMAIN" ]; then
          sed -i 's/n8n.com/'"$DOMAIN"'/g' /etc/nginx/nginx.conf
          if ! grep -q "$DOMAIN" /etc/nginx/nginx.conf; then
            echo "❌ 域名替换失败!"
            exit 1
          fi
        else
          echo "❌ DOMAIN 环境变量未设置!"
          exit 1
        fi
        
        rm -f /etc/nginx/conf.d/*.conf
        
        # 验证配置
        if [ ! -s "/etc/nginx/nginx.conf" ]; then
          echo "❌ 配置文件验证失败!"
          exit 1
        fi        
        
        echo "✅ 初始化完成 - 访问地址: https://$DOMAIN"

  dstack-ingress:
    image: kvin/dstack-ingress:250428
    ports:
      - "443:443"
    environment:
      - GATEWAY_DOMAIN=_.dstack-prod7.phala.network    
      - CLOUDFLARE_API_TOKEN=${CLOUDFLARE_API_TOKEN}
      - CERTBOT_EMAIL=${CERTBOT_EMAIL}
      - SET_CAA=true
      - DOMAIN=${DOMAIN}      
      - TARGET_ENDPOINT=http://n8n:5678
    depends_on:
      initial_setup:
        condition: service_started
    volumes:
      - /var/run/tappd.sock:/var/run/tappd.sock
      - cert-data:/etc/letsencrypt
    restart: unless-stopped

  n8n-db:
    image: postgres:17
    container_name: n8n-db
    restart: unless-stopped
    environment:
      - TZ=Asia/Shanghai
      - POSTGRES_USER=n8n
      - POSTGRES_PASSWORD=${POSTGRES_PASSWORD}
      - POSTGRES_DB=n8n
    volumes:
      - postgres-data:/var/lib/postgresql/data
    healthcheck:
      test: ['CMD-SHELL', 'pg_isready -h localhost -U n8n -d n8n']
      interval: 5s
      timeout: 5s
      retries: 10
    networks:
      - internal

  n8n-redis:
    image: redis:latest
    container_name: n8n-redis
    restart: unless-stopped
    command: ["redis-server", "--requirepass", "${REDIS_PASSWORD}", "--bind", "0.0.0.0", "--protected-mode", "yes"]
    volumes:
      - redis-data:/data
    healthcheck:
      test: ["CMD", "redis-cli", "-a", "${REDIS_PASSWORD}", "ping"]
      interval: 5s
      timeout: 5s
      retries: 10
    networks:
      - internal

  n8n:
    image: docker.n8n.io/n8nio/n8n
    container_name: n8n
    restart: unless-stopped
    environment:
      - DB_TYPE=postgresdb
      - DB_POSTGRESDB_HOST=n8n-db
      - DB_POSTGRESDB_PORT=5432
      - DB_POSTGRESDB_DATABASE=n8n
      - DB_POSTGRESDB_USER=n8n
      - DB_POSTGRESDB_PASSWORD=${POSTGRES_PASSWORD}
      - N8N_SECURE_COOKIE=true
      - GENERIC_TIMEZONE=Asia/Shanghai
      - N8N_DEFAULT_LOCALE=zh-CN
      - N8N_PROXY_HOPS=1
      - N8N_RUNNERS_ENABLED=true
      - N8N_ENFORCE_SETTINGS_FILE_PERMISSIONS=true      
      - QUEUE_BULL_REDIS_HOST=n8n-redis
      - QUEUE_BULL_REDIS_PORT=6379
      - QUEUE_BULL_REDIS_PASSWORD=${REDIS_PASSWORD}
      - WEBHOOK_URL=${WEBHOOK_URL}
      - VUE_APP_URL_BASE_API=${VUE_APP_URL_BASE_API}
    ports:
      - "5678:5678"
    volumes:
      - n8n-data:/home/node/.n8n
      - dist-data:/usr/local/lib/node_modules/n8n/node_modules/n8n-editor-ui/dist
    depends_on:
      initial_setup:
        condition: service_started
      n8n-db:
        condition: service_healthy
      n8n-redis:
        condition: service_healthy
    networks:
      - internal
      - default

networks:
  internal:
    driver: bridge
    internal: true

volumes:
  nginx-data:
    name: nginx-data
  cert-data:
    name: cert-data
  dist-data:
    name: dist-data
  redis-data:
    name: redis-data
  n8n-data:
    name: n8n-data
  postgres-data:
    name: postgres-data    

方案二

cloudflared 方案

环境变量

CLOUDFLARE_TUNNEL_TOKEN=your_cloudflar_tunnel_token
POSTGRES_PASSWORD=your_postgres_password
REDIS_PASSWORD=your_redis_password
WEBHOOK_URL=https://n8n.xxx.com
VUE_APP_URL_BASE_API=https://n8n.xxx.com

容器编排

services:
  initial_setup:
    image: docker.io/alpine:latest
    container_name: initial_setup
    networks:
      - default
    volumes:
      - dist-data:/dist
    command:
      - sh
      - -c
      - |
        echo "🚀 初始化设置"
        
        # 安装必要工具
        apk add --no-cache curl > /dev/null 2>&1

        # 初始化 editor-ui
        echo "📦 初始化 editor-ui..."
        mkdir -p /dist
        echo "🔽 下载 editor-ui.tar.gz..."
        cd /dist
        curl -s -L -o editor-ui.tar.gz https://github.com/other-blowsnow/n8n-i18n-chinese/releases/latest/download/editor-ui.tar.gz
        if [ $$? -ne 0 ]; then
          echo "❌ editor-ui.tar.gz 下载失败!"
          exit 1
        fi
        
        echo "📂 解压 editor-ui..."
        tar -xzf editor-ui.tar.gz --strip-components=1
        if [ $$? -ne 0 ]; then
          echo "❌ editor-ui 解压失败!"
          exit 1
        fi
        
        rm -f editor-ui.tar.gz
        echo "✅ editor-ui 初始化完成!" 
        echo "✅ 初始化完成!"

  n8n-tunnel:
    image: milgradesec/cloudflared:latest
    container_name: n8n-tunnel
    restart: unless-stopped
    networks:
      - default
    command: ["tunnel", "--no-autoupdate", "run", "--token", "${CLOUDFLARE_TUNNEL_TOKEN}"]

  n8n-db:
    image: postgres:17
    container_name: n8n-db
    restart: unless-stopped
    environment:
      - TZ=Asia/Shanghai
      - POSTGRES_USER=n8n
      - POSTGRES_PASSWORD=${POSTGRES_PASSWORD}
      - POSTGRES_DB=n8n
    volumes:
      - postgres-data:/var/lib/postgresql/data
    healthcheck:
      test: ['CMD-SHELL', 'pg_isready -h localhost -U n8n -d n8n']
      interval: 5s
      timeout: 5s
      retries: 10
    networks:
      - internal

  n8n-redis:
    image: redis:latest
    container_name: n8n-redis
    restart: unless-stopped
    command: ["redis-server", "--requirepass", "${REDIS_PASSWORD}", "--bind", "0.0.0.0", "--protected-mode", "yes"]
    volumes:
      - redis-data:/data
    healthcheck:
      test: ["CMD", "redis-cli", "-a", "${REDIS_PASSWORD}", "ping"]
      interval: 5s
      timeout: 5s
      retries: 10
    networks:
      - internal

  n8n:
    image: docker.n8n.io/n8nio/n8n
    container_name: n8n
    restart: unless-stopped
    environment:
      - DB_TYPE=postgresdb
      - DB_POSTGRESDB_HOST=n8n-db
      - DB_POSTGRESDB_PORT=5432
      - DB_POSTGRESDB_DATABASE=n8n
      - DB_POSTGRESDB_USER=n8n
      - DB_POSTGRESDB_PASSWORD=${POSTGRES_PASSWORD}
      - N8N_SECURE_COOKIE=true
      - GENERIC_TIMEZONE=Asia/Shanghai
      - N8N_DEFAULT_LOCALE=zh-CN
      - N8N_PROXY_HOPS=1
      - N8N_RUNNERS_ENABLED=true
      - N8N_ENFORCE_SETTINGS_FILE_PERMISSIONS=true
      - QUEUE_BULL_REDIS_HOST=n8n-redis
      - QUEUE_BULL_REDIS_PORT=6379
      - QUEUE_BULL_REDIS_PASSWORD=${REDIS_PASSWORD}
      - WEBHOOK_URL=${WEBHOOK_URL}
      - VUE_APP_URL_BASE_API=${VUE_APP_URL_BASE_API}
    ports:
      - "5678:5678"
    volumes:
      - n8n-data:/home/node/.n8n
      - dist-data:/usr/local/lib/node_modules/n8n/node_modules/n8n-editor-ui/dist
    depends_on:
      initial_setup:
        condition: service_completed_successfully
      n8n-db:
        condition: service_healthy
      n8n-redis:
        condition: service_healthy
    networks:
      - internal
      - default

networks:
  internal:
    driver: bridge
    internal: true

volumes:
  dist-data:
    name: dist-data
  redis-data:
    name: redis-data
  n8n-data:
    name: n8n-data
  postgres-data:
    name: postgres-data

Calibre-web

点击下载 桌面端,下载后双击安装,使用默认设置安装后会自动打开

不要直接使用自动生成的 metadata.db,需要点击 Calibre 书库切换/建立书库,勾选 在新的位置建立空的书库,选择位置后点击 确定,然后进入新位置所在的文件夹,将 metadata.db 上传即可

环境变量

DOMAIN=calibre.xxx.com
CERTBOT_EMAIL=xxx@xxx.com
CLOUDFLARE_API_TOKEN=*************************

容器编排

services:
  initial_setup:
    image: docker.io/alpine:latest
    container_name: initial_setup
    environment:
      - DOMAIN=${DOMAIN}
    volumes:
      - calibre-data:/app/calibre-web
      - nginx-data:/etc/nginx
    command:
      - sh
      - -c
      - |
        echo "🚀 初始化设置 - 域名: ${DOMAIN:-'未设置'}"
        
        # 安装必要工具
        apk add --no-cache curl > /dev/null 2>&1
        
        # 初始化 Calibre 数据库
        if [ ! -f "/app/calibre-web/books/metadata.db" ]; then
          echo "📚 初始化 Calibre 数据库..."
          mkdir -p /app/calibre-web/books
          chown -R 1000:1000 /app/calibre-web
          cd /app/calibre-web/books 
          curl -s -L -o metadata.db https://raw.githubusercontent.com/Raimbaulty/freebsd/main/metadata.db
          if [ $? -ne 0 ]; then
            echo "❌ metadata.db 下载失败!"
            exit 1
          fi
          chmod 666 /app/calibre-web/books/metadata.db
          echo "✅ Calibre 数据库初始化完成"
        fi

        # 配置 Nginx
        echo "🌐 配置 Nginx..."
        mkdir -p /etc/nginx/conf.d
        curl -s -L -o /etc/nginx/nginx.conf https://raw.githubusercontent.com/Raimbaulty/freebsd/main/calibre.conf
        if [ $? -ne 0 ]; then
          echo "❌ nginx.conf 下载失败!"
          exit 1
        fi
        
        # 替换域名变量
        if [ -n "$DOMAIN" ]; then
          sed -i 's/calibre-web.com/'"$DOMAIN"'/g' /etc/nginx/nginx.conf
          if ! grep -q "$DOMAIN" /etc/nginx/nginx.conf; then
            echo "❌ 域名替换失败!"
            exit 1
          fi
        else
          echo "❌ DOMAIN 环境变量未设置!"
          exit 1
        fi
        
        rm -f /etc/nginx/conf.d/*.conf
        
        # 验证配置
        if [ ! -s "/etc/nginx/nginx.conf" ] || [ ! -s "/app/calibre-web/books/metadata.db" ]; then
          echo "❌ 配置文件验证失败!"
          exit 1
        fi
        
        echo "✅ 初始化完成 - 访问地址: https://$DOMAIN"
      
  calibre-web:
    image: linuxserver/calibre-web:latest
    container_name: calibre-web
    environment:
      - PUID=1000
      - PGID=1000
      - TZ=Asia/Shanghai
      - DOCKER_MODS=linuxserver/mods:universal-calibre
      - OAUTHLIB_RELAX_TOKEN_SCOPE=1
      - OAUTHLIB_INSECURE_TRANSPORT=1
      - HTTP_X_SCHEME=https
      - HTTP_X_FORWARDED_PROTO=https
      - HTTP_X_FORWARDED_PORT=443            
    ports:
      - 8083:8083      
    volumes:
      - calibre-data:/app/calibre-web
    restart: always
    depends_on:
      - initial_setup
    networks:
      - internal
      - default

  dstack-ingress:
    image: kvin/dstack-ingress:250428
    ports:
      - "443:443"
    environment:
      - GATEWAY_DOMAIN=_.dstack-prod7.phala.network    
      - CLOUDFLARE_API_TOKEN=${CLOUDFLARE_API_TOKEN}
      - CERTBOT_EMAIL=${CERTBOT_EMAIL}
      - SET_CAA=true
      - DOMAIN=${DOMAIN}      
      - TARGET_ENDPOINT=http://calibre-web:8083
    volumes:
      - /var/run/tappd.sock:/var/run/tappd.sock
      - cert-data:/etc/letsencrypt
      - nginx-data:/etc/nginx
    restart: unless-stopped
    depends_on:
      - initial_setup    
    networks:
      - default

networks:
  internal:
    driver: bridge
    internal: true      
    
volumes:
  cert-data:
    name: cert-data
  calibre-data:
    name: calibre-data
  nginx-data:
    name: nginx-data

使用配置(管理员向)

基本配置

初始用户名 admin,密码 admin123 登录 Calibre-web 站点

登录后点击 📁 图标,进入 books 目录,点击选中 metadata.db 后保存

点击顶部导航栏的 admin 菜单,依次修改用户名、用户邮箱、密码、推送邮箱,并修改语言为中文,然后点击底部保存

点击顶部导航栏的 管理权限 菜单,点击 编辑基本配置,展开 拓展程序配置Path to Calibre Binaries 输入 /usr/bin 后点击保存

用户管理

点击顶部导航栏的 管理权限 菜单,点击 编辑基本配置,展开 功能配置,勾选 启用注册 但不勾选 使用邮箱或用户名 后保存

点击顶部导航栏的 管理权限 菜单,点击 编辑界面配置,展开 新用户默认权限设置,勾选需要的权限并选择默认语言后保存

GitHub登录

点击这里 创建应用,Homepage URL 填写 https://n8n.xxx.comCallback URL 填写 http://n8n.xxx.com/login/github/authorized,取消勾选 Webhook 的 Active 点击 Save changes

回到 Calibre-web 站点,点击顶部导航栏的 管理权限 菜单,点击 编辑基本配置,展开 功能配置登录类型 选择 使用 OAuth 认证,依次填入 github OAuth 客户端 Id 和 github OAuth 客户端 Secret 后保存

点击右上角用户名,点击 github OAuth 设置 旁的 链接,使用 GitHub 账户登录完成绑定,账户就可使用 GitHub 登录了

上传书籍

右上角退出登录,使用新的用户名密码登录,点击顶部导航栏的 管理权限 菜单,点击 编辑基本配置,展开 功能配置,勾选 启用上传 后点击 底部 保存

点击顶部导航栏的 管理权限 菜单,点击 管理用户,勾选 允许上传书籍 底部的方形框,刷新页面就可以看到 上传书籍 按钮了

上传书籍后,点击底部 获取元数据,先搜索书籍名称,然后仅勾选 豆瓣,点击正确的书籍封面图自动导入元数据,然后点击 保存

如果上传报错提示 File type isn't allowed to be uploaded to this server,点击顶部导航栏的 管理权限 菜单,点击 编辑基本配置,展开 安全设置,取消勾选 Check if file extensions matches file content on upload 后重试

推送书籍

首先需要自备一台电子书设备,下面介绍的是 Kindle 的教程

点击 Calibre-web 站点顶部导航栏的 管理权限 菜单,点击 编辑邮件服务器设置SMTP 主机名 填写 SMTP 服务器地址,加密 选择 STARTTLS 协议,如果后续测试不通过可以修改为 重试,其他自行填入后点击底部 保存设置并发送测试邮件,点击顶部导航栏的 任务列表 菜单查看任务状态,也可登入你设置的服务邮箱地址确认测试邮件

点击注册 Amazon 美区账号,如果 Kindle 登录过中亚账号需要先注销,然后使用新账号在Kindle登录并获得设备验证码

点击这里 登录美区账号验证后输入设备验证码绑定设备,然后 点击这里 接着点击 Personal Document SettingsAdd a new e-mail address,添加你的服务邮箱地址

点击这里 进入绑定好的设备,可以点击 Email 旁边的 Edit 修改地址,然后使用这个地址作为转发邮箱地址

回到 Calibre-web 站点,点击头像 - 用户名,在 Send to eReader Email Address. Use comma to separate emails for multiple eReaders 填写转发邮箱地址

点击左侧 书籍 菜单,点击需要推送的书籍,点击推送

点击顶部导航栏的 任务列表 菜单查看任务状态

移动设备

IOS 应用商店搜索 KyBook 3,Android 应用商店搜索 Librera 下载,这里讲解 KyBook 3 的使用方法

进入应用后,点击 目录,然后点击 添加,标题输入 Calibre,URL输入 https://站点地址/opds 添加,然后输入用户名密码验证即可

Zenfeed

环境变量

DOMAIN=zen.xxx.com
CERTBOT_EMAIL=admin@example.com
CLOUDFLARE_API_TOKEN=hijklmnopqrst

容器编排

services:
  dstack-ingress:
    container_name: dstack-ingress
    image: kvin/dstack-ingress:250428
    ports:
      - "443:443"
    environment:
      - GATEWAY_DOMAIN=_.dstack-prod7.phala.network
      - CLOUDFLARE_API_TOKEN=${CLOUDFLARE_API_TOKEN}
      - CERTBOT_EMAIL=${CERTBOT_EMAIL}
      - SET_CAA=true
      - DOMAIN=${DOMAIN}
      - TARGET_ENDPOINT=http://zenfeed-web:1400
    volumes:
      - /var/run/tappd.sock:/var/run/tappd.sock
      - cert-data:/etc/letsencrypt
    restart: unless-stopped
    networks:
      - zenfeed-net

  zenfeed-web:
    image: glidea/zenfeed-web:latest
    ports:
      - "1400:1400"
    environment:
      - PUBLIC_DEFAULT_API_URL=http://zenfeed:1300
    depends_on:
      - zenfeed
    restart: unless-stopped
    networks:
      - zenfeed-net

  zenfeed:
    image: glidea/zenfeed:latest
    entrypoint: >
      sh -c "
      if [ ! -f /app/config/config.yaml ]; then
        echo 'Config file not found in volume, initializing from base config...'
        cp /app/config.base.yaml /app/config/config.yaml;
      else
        echo 'Existing config file found in volume.'
      fi &&
      echo 'Starting Zenfeed...' &&
      exec /app/zenfeed --config /app/config/config.yaml
      "
    configs:
      - source: zenfeed_config_base
        target: /app/config.base.yaml
    volumes:
      - zenfeed-data:/app/data
      - zenfeed-config:/app/config
    depends_on:
      - rsshub
    restart: unless-stopped
    networks:
      - zenfeed-net
    
  rsshub:
    image: diygod/rsshub:2024-12-14
    environment:
      - NODE_ENV=production
    restart: unless-stopped
    networks:
      - zenfeed-net
      
volumes:
  zenfeed-data:
    name: zenfeed_data
  zenfeed-data:
    name: zenfeed-data
  cert-data:
    name: cert-data

configs:
  zenfeed_config_base:
    content: |
      timezone: ${TZ:-Asia/Shanghai}
      llms:
        - name: general
          default: true
          provider: siliconflow
          model: Qwen/Qwen2.5-7B-Instruct
          api_key: ${API_KEY:-your-api-key}
        - name: embed
          provider: siliconflow
          embedding_model: Pro/BAAI/bge-m3
          api_key: ${API_KEY:-your-api-key}
      scrape:
        rsshub_endpoint: http://rsshub:1200
      storage:
        feed:
          rewrites:
            - transform:
                to_text:
                  prompt: |
                     Respond in ${LANG:-Chinese}
              label: summary_html_snippet
          embedding_llm: embed
      notify:
        channels:
          email:
            feed_html_snippet_template: |
              

networks:
  zenfeed-net:
    driver: bridge

Chat-Share

环境变量

DOMAIN=share.xxx.com
SECRET_KEY=1234567abcdefg
AUTHORIZATION=sk-123
CERTBOT_EMAIL=admin@example.com
CLOUDFLARE_API_TOKEN=hijklmnopqrst

容器编排

services:
  dstack-ingress:
    image: kvin/dstack-ingress:250428
    ports:
      - "443:443"
    environment:
      - GATEWAY_DOMAIN=_.dstack-prod7.phala.network    
      - CLOUDFLARE_API_TOKEN=${CLOUDFLARE_API_TOKEN}
      - CERTBOT_EMAIL=${CERTBOT_EMAIL}
      - SET_CAA=true
      - DOMAIN=${DOMAIN}      
      - TARGET_ENDPOINT=http://chat-share:5100
    volumes:
      - /var/run/tappd.sock:/var/run/tappd.sock
      - cert-data:/etc/letsencrypt
    restart: unless-stopped
    
  chat-share:
    image: ghcr.io/h88782481/chat-share:latest
    container_name: chat-share    
    restart: unless-stopped
    environment:
      - TZ=Asia/Shanghai
      - SECRET_KEY=${SECRET_KEY}
      - AUTHORIZATION=${AUTHORIZATION}
      - DOMAIN_CHATGPT=https://898u-lan.hf.space
      - DOMAIN_CLAUDE=https://mpfo-fo.hf.space
    volumes:
      - share-data:/app/data
      
volumes:
  cert-data:
  	-name: cert-data
  share-data:
  	-name: share-data

Elasticsearch/Kibana

容器编排

services:
  dstack-ingress:
    image: kvin/dstack-ingress:250428
    ports:
      - "443:443"
    environment:
      - GATEWAY_DOMAIN=_.dstack-prod7.phala.network    
      - CLOUDFLARE_API_TOKEN=${CLOUDFLARE_API_TOKEN}
      - CERTBOT_EMAIL=${CERTBOT_EMAIL}
      - SET_CAA=true
      - DOMAIN=${DOMAIN}      
      - TARGET_ENDPOINT=http://elasticsearch:65536
    volumes:
      - /var/run/tappd.sock:/var/run/tappd.sock
      - cert-data:/etc/letsencrypt
    restart: unless-stopped
      
  elasticsearch:
    image: docker.elastic.co/elasticsearch/elasticsearch:7.4.2
    container_name: elasticsearch
    environment:
      - discovery.type=single-node
      - "ES_JAVA_OPTS=-Xms512m -Xmx512m"
      - "http.cors.enabled=true"
      - "http.cors.allow-origin=*"
      - "xpack.security.enabled=false"
      - "cluster.name=es-docker-cluster"
      - "network.bind_host=0.0.0.0"
      - "network.publish_host=0.0.0.0"
    ulimits:
      memlock:
        soft: -1
        hard: -1
      nofile:
        soft: 65536
        hard: 65536
    volumes:
      - elasticsearch-data:/usr/share/elasticsearch/data
    ports:
      - 9200:9200
      - 9300:9300
    networks:
      - elk-network
      # elk-network:
      #   aliases:
      #     - elasticsearch
    restart: always

  kibana:
    image: docker.elastic.co/kibana/kibana:7.4.2
    container_name: kibana
    environment:
      - "ELASTICSEARCH_HOSTS=http://elasticsearch:9200"
      - "SERVER_NAME=kibana"
      - "SERVER_HOST=0.0.0.0"
      - "ELASTICSEARCH_REQUESTTIMEOUT=90000"
    ports:
      - 5601:5601
    networks:
      - elk-network
    restart: on-failure
    depends_on:
      - elasticsearch

  setup-plugin:
    image: docker.io/alpine:latest
    container_name: setup-plugin
    ports:
      - 9999:9999
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock
    command: >
      sh -c '
        echo "等待 Elasticsearch 启动..." &&
        sleep 30 &&
        echo "开始安装 Docker CLI 工具..." &&
        apk add --no-cache docker-cli curl &&
        echo "开始检查 Elasticsearch 健康状态..." &&
        until curl -s http://elasticsearch:9200/_cluster/health | grep -q "status"; do
          echo "等待 Elasticsearch 健康检查通过..."
          sleep 5
        done &&
        echo "Elasticsearch 已启动,开始安装分词器..." &&
        docker exec elasticsearch elasticsearch-plugin install --batch https://release.infinilabs.com/analysis-ik/stable/elasticsearch-analysis-ik-7.4.2.zip &&
        echo "IK 分词器安装完成,重启 Elasticsearch..." &&
        docker restart elasticsearch &&
        echo "Elasticsearch 重启完成,等待服务就绪..." &&
        sleep 30 &&
        echo "安装流程完成!"
      '
    depends_on:
      - elasticsearch
    networks:
      - elk-network

  watchtower:
    image: containrrr/watchtower
    container_name: watchtower
    restart: unless-stopped
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock
    command: --cleanup --interval 300

networks:
  elk-network:
    driver: bridge
  default:
    driver: bridge

volumes:
  cert-data:
  	name: cert-data
  elasticsearch-data:
    driver: local  
  	name:elasticsearch-data

参考链接