【好玩儿的 Docker 项目】十分钟搭建一个 Obsidian 私有同步服务——Fast Note Sync,多端实时同步笔记和附件

<img src="https://blog.laoda.de/plugins/feed/assets/telemetry.gif?title=%E3%80%90%E5%A5%BD%E7%8E%A9%E5%84%BF%E7%9A%84%20Docker%20%E9%A1%B9%E7%9B%AE%E3%80%91%E5%8D%81%E5%88%86%E9%92%9F%E6%90%AD%E5%BB%BA%E4%B8%80%E4%B8%AA%20Obsidian%20%E7%A7%81%E6%9C%89%E5%90%8C%E6%AD%A5%E6%9C%8D%E5%8A%A1%E2%80%94%E2%80%94Fast%20Note%20Sync%EF%BC%8C%E5%A4%9A%E7%AB%AF%E5%AE%9E%E6%97%B6%E5%90%8C%E6%AD%A5%E7%AC%94%E8%AE%B0%E5%92%8C%E9%99%84%E4%BB%B6&amp;url=/archives/docker-compose-install-fast-note-sync" width="1" height="1" alt="" style="opacity:0;"> <h2 id="1--唠嗑">1. 唠嗑</h2> <blockquote> <p>拖更好久了,今天来更新一下。(顺便吐槽今天德国踢的太臭了)</p> </blockquote> <p>如果你平时用 Obsidian,应该多少都纠结过同步的问题。</p> <p>先简单介绍一下 Obsidian。</p> <p>Obsidian 是一个本地优先的 Markdown 笔记软件,所有笔记本质上都是一个个 <code>.md</code> 文件,放在自己的电脑或手机里。你可以用它写日记、做读书笔记、整理项目资料,也可以把它当成自己的「第二大脑」或者个人知识库。</p> <p>它和普通云笔记最大的区别是:数据不被某个平台锁死。哪天不用 Obsidian 了,这些 Markdown 文件依然可以直接打开、迁移、备份。</p> <p>这两年大模型火起来之后,Obsidian 的玩法也更有意思了。你可以把自己的笔记库整理成长期知识库,再配合 ChatGPT、Claude、Cursor、Cherry Studio,甚至 MCP 这类工具,让 AI 在你授权的范围内读取、总结、检索、改写自己的资料。</p> <p>比如:</p> <ul> <li>让 AI 帮你总结过去写过的文章</li> <li>从自己的笔记里找某个项目的资料</li> <li>把零散记录整理成博客、周报、方案</li> <li>基于自己的知识库问答,而不是每次都从零开始问 AI</li> </ul> <p>但前提是,你的笔记要能稳定同步、备份,并且在多台设备上都能访问。不然电脑上写了一半,手机上看不到;手机拍的图片附件,电脑上又没同步过来,体验就很割裂,很难顶。</p> <p>今天咕咕给大家分享一个可以私有化部署的 Obsidian 同步服务:<strong>Fast Note Sync Service</strong>。</p> <p>它搭配 Obsidian 插件 <strong>Fast Note Sync</strong> 使用,可以把自己的 Obsidian 笔记库同步到自己的服务器上,支持多端实时同步、附件同步、历史版本、回收站、分享链接,甚至还支持 REST API 和 MCP。</p> <p>简单来说,就是:</p> <blockquote> <p>自己搭一个 Obsidian 私有同步服务器,让不同系统的电脑、手机、平板之间的笔记可以实时同步。</p> </blockquote> <p>笔记软件</p> <p><img src="https://blog.laoda.de/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fimg.laoda.de%2Fi%2F2026%2F06%2F30%2F094637-1.jpeg&amp;size=m" alt="b9a5ac47d9711c175dfa4ea39df75315.png"></p> <p><img src="https://blog.laoda.de/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fimg.laoda.de%2Fi%2F2026%2F06%2F30%2F094636-1.jpeg&amp;size=m" alt="3eaa359d571e4da8a7f43f693f44cd48.png"></p> <h2 id="2--Fast-Note-Sync-简介">2. Fast Note Sync 简介</h2> <p>Fast Note Sync 分成两部分:</p> <ul> <li><strong>Fast Note Sync Service</strong>:服务端,部署在自己的 VPS 上</li> <li><strong>Fast Note Sync for Obsidian</strong>:Obsidian 插件,安装在自己的 Obsidian 里</li> </ul> <p>它具体都能干什么?</p> <ul> <li>Obsidian 笔记多端实时同步</li> <li>支持图片、音频、视频等附件同步</li> <li>支持 <code>.obsidian</code> 配置同步</li> <li>支持笔记历史版本</li> <li>支持回收站恢复</li> <li>支持离线编辑后自动合并</li> <li>支持通过网页后台管理笔记库</li> <li>支持分享笔记链接</li> <li>支持 REST API / MCP,方便后续接入 AI 工具</li> </ul> <p>适合的场景包括:</p> <ul> <li>不想订阅 Obsidian Sync,但又想多设备同步</li> <li>想把笔记数据放在自己的服务器上</li> <li>想在手机、电脑、平板之间同步同一个 Vault</li> <li>想给自己的知识库留一个 Web 管理后台</li> <li>想后续把 Obsidian 笔记接入 AI / MCP 工具</li> </ul> <blockquote> <p>注意:目前官方 Roadmap 里端到端加密还在计划中,我们都是特别注意隐私的,所以强烈建议只部署在自己信任的服务器上,不要放到来路不明的机器上。</p> </blockquote> <h2 id="2-1-和-iCloud---Obsidian-Sync-怎么选-">2.1 和 iCloud / Obsidian Sync 怎么选?</h2> <p>看到这里,可能有小伙伴可能会问:</p> <blockquote> <p>我直接用 iCloud 不就好了?或者买官方 Obsidian Sync 不就好了?为啥还要自己折腾这个?</p> </blockquote> <p>这个问题要分情况看。</p> <p>如果你所有设备都是 Apple 生态,比如 Mac + iPhone + iPad,而且笔记不多、附件不大,那 iCloud 确实是最省事的选择。系统自带,不需要额外部署,也不用折腾服务器。</p> <p>但是 iCloud 本质上是「文件夹同步」,不是专门为 Obsidian 做的同步服务。它没有 Obsidian 专用的网页后台、同步日志、Vault 管理、REST API、MCP、Git 自动化这些东西。如果你还想在 Windows、Android、Linux 上一起用,体验就没那么统一了。</p> <p>官方 Obsidian Sync 是最省心、最稳妥的方案。官方页面写得很清楚,它支持端到端加密、版本历史、跨平台同步、离线编辑后再合并,还能细粒度控制同步哪些配置和文件。</p> <p>价格方面,截至 2026 年 6 月 30 日,Obsidian Sync Standard 年付是 4 美元/月,月付是 5 美元/月,包含 1 个同步库、1GB 总存储、单文件 5MB 限制、1 个月版本历史;Sync Plus 年付是 8 美元/月,月付是 10 美元/月,包含 10 个同步库、10GB 总存储、单文件 200MB 限制、12 个月版本历史,还可以升级到 100GB 存储。</p> <p>Fast Note Sync 是给这些人准备的:</p> <ul> <li>已经有自己的 VPS 或家里服务器</li> <li>想把 Obsidian 同步服务放在自己手里</li> <li>不想按月订阅,但愿意自己维护服务</li> <li>附件比较多,想用自己的磁盘空间</li> <li>想要 Web 管理后台,可以在线查看笔记、附件和同步日志</li> <li>想把 Obsidian 接入 REST API / MCP,让 AI 工具读写自己的知识库</li> <li>想折腾 Git 自动化、远程备份、对象存储、WebDAV 等扩展玩法</li> </ul> <p>简单对比一下:</p> <table> <thead> <tr> <th>方案</th> <th>适合谁</th> <th>优点</th> <th>不足</th> </tr> </thead> <tbody> <tr> <td>iCloud</td> <td>Apple 全家桶用户</td> <td>系统自带,最省事</td> <td>跨平台一般,本质是文件夹同步</td> </tr> <tr> <td>Obsidian Sync</td> <td>想省心、重视稳定和隐私的人</td> <td>官方维护,端到端加密,体验完整</td> <td>需要订阅,有存储和单文件大小限制</td> </tr> <tr> <td>Fast Note Sync</td> <td>有 VPS、喜欢自托管的人</td> <td>私有部署、空间自己定、支持后台/API/MCP</td> <td>需要自己维护,目前端到端加密还在 Roadmap</td> </tr> </tbody> </table> <p>所以咕咕的建议是:</p> <p>如果你只是 Mac + iPhone 轻度同步,继续用 iCloud 就行。</p> <p>如果你想要最稳、最省心,而且不差钱,官方 Obsidian Sync 依然是首选。</p> <p>但如果你本来就有 VPS,又想把 Obsidian 变成一个真正属于自己的私有知识库,甚至后面还想接 AI / MCP / 自动化,那 Fast Note Sync 就很值得折腾一下。</p> <p>不过还是要提醒一句:目前 Fast Note Sync 的端到端加密还在 Roadmap 里,服务端是可以处理你的笔记内容的。所以不要部署在不信任的服务器上,也不要把后台裸奔公网。重要笔记库第一次同步前,建议先做好本地备份。</p> <h2 id="3--相关地址">3. 相关地址</h2> <p>官方 GitHub 地址:</p> <p>服务端:<a href="https://github.com/haierkeys/fast-note-sync-service">https://github.com/haierkeys/fast-note-sync-service</a></p> <p>Obsidian 插件:<a href="https://github.com/haierkeys/obsidian-fast-note-sync">https://github.com/haierkeys/obsidian-fast-note-sync</a></p> <p>Obsidian Sync 官方页面:<a href="https://obsidian.md/sync">https://obsidian.md/sync</a></p> <p>截至 2026 年 6 月 30 日,服务端大概 1.7k 个 star,插件大概 2.5k 个 star,感兴趣的小伙伴可以去给项目点个星星。</p> <p>当然如果你想自己用 Docker 搭建一个,那我们就继续往下!</p> <h2 id="4--搭建环境">4. 搭建环境</h2> <ul> <li>服务器:咕咕这边建议用 1G 内存以上的 VPS,比如<a href="http://gao.ee/lca">莱卡云服务器</a>,当然你也可以选择其他<a href="https://blog.laoda.de/vps">高性价比的服务器</a>。重点看磁盘空间,因为后面笔记附件也会存到服务器上。</li> <li>系统:Debian 11 / Debian 12 / Ubuntu 都可以。</li> <li>安装好 Docker、Docker-compose(<a href="https://blog.laoda.de/archives/hello-docker/">相关脚本</a>)</li> <li>【必需】域名一枚,并做好解析到服务器上(<a href="https://blog.laoda.de/archives/namesilo/">域名购买、域名解析</a>)</li> <li>【非必需】提前安装好宝塔面板海外版本 aapanel,并安装好 Nginx(<a href="https://forum.aapanel.com/d/9-aapanel-linux-panel-6812-installation-tutorial">安装地址</a>)</li> <li>【非必需本教程选用】安装好 Nginx Proxy Manager(<a href="https://blog.laoda.de/archives/nginxproxymanager/">相关教程</a>)</li> </ul> <h2 id="5--搭建视频-等有空了补充-----">5. 搭建视频(等有空了补充 = =)</h2> <h3 id="5-1-YouTube">5.1 YouTube</h3> <p>视频地址:</p> <h3 id="5-2-哔哩哔哩">5.2 哔哩哔哩</h3> <p>哔哩哔哩:</p> <h2 id="6--搭建方式">6. 搭建方式</h2> <h3 id="6-1-安装-Docker-与-Nginx-Proxy-Manager">6.1 安装 Docker 与 Nginx Proxy Manager</h3> <p>可以直接参考这篇内容:</p> <p><a href="https://blog.laoda.de/archives/nginxproxymanager/">https://blog.laoda.de/archives/nginxproxymanager/</a></p> <h3 id="6-2-创建安装目录">6.2 创建安装目录</h3> <p>创建一下安装目录:</p> <pre><code class="language-bash">sudo -i mkdir -p /root/data/docker_data/fast-note-sync cd /root/data/docker_data/fast-note-sync </code></pre> <p>接着我们来编辑 <code>docker-compose.yml</code>:</p> <pre><code class="language-bash">vim docker-compose.yml </code></pre> <p>贴入下面内容:</p> <pre><code class="language-yaml">services: fast-note-sync-service: image: haierkeys/fast-note-sync-service:latest container_name: fast-note-sync-service restart: unless-stopped ports: - "9000:9000" volumes: - ./storage:/fast-note-sync/storage - ./config:/fast-note-sync/config </code></pre> <p>其中左边的 <code>9000</code> 可以改成服务器上没有用过的端口,比如 <code>9010</code>。</p> <p>修改完成之后,英文输入法下按 <code>i</code> 修改,完成之后按 <code>esc</code>,然后输入 <code>:wq</code> 保存退出。</p> <h3 id="6-3-下载默认配置文件">6.3 下载默认配置文件</h3> <p>官方默认配置比较长,我们直接拉一份下来:</p> <pre><code class="language-bash">cd /root/data/docker_data/fast-note-sync mkdir -p config storage wget -O config/config.yaml https://raw.githubusercontent.com/haierkeys/fast-note-sync-service/master/config/config.yaml </code></pre> <p>如果 GitHub 拉取比较慢,可以用作者提供的 CNB 镜像:</p> <pre><code class="language-bash">wget -O config/config.yaml https://cnb.cool/haierkeys/fast-note-sync-service/-/git/raw/master/config/config.yaml </code></pre> <p>然后编辑配置文件:</p> <pre><code class="language-bash">vim config/config.yaml </code></pre> <p>先改这几个地方:</p> <pre><code class="language-yaml">server: ext-api-url: "https://note.example.com" #改成你自己之后用的域名链接 security: auth-token-key: "换成一串足够长的随机字符串" share-token-key: "再换成另一串足够长的随机字符串" user: register-is-enable: true </code></pre> <p><code>ext-api-url</code> 改成你自己的域名。</p> <p><code>auth-token-key</code> 和 <code>share-token-key</code> 不要用默认值,随便找个密码生成器生成两串长一点的随机字符串即可。</p> <p>这里先保持:</p> <pre><code class="language-yaml">register-is-enable: true </code></pre> <p>因为我们第一次进去还要注册账号。等注册完第一个账号之后,后面再关掉注册。</p> <h3 id="6-4-查看端口是否被占用">6.4 查看端口是否被占用</h3> <p>查看端口是否被占用,以 <code>9000</code> 为例:</p> <pre><code class="language-bash">lsof -i:9000 </code></pre> <p>如果啥也没出现,表示端口未被占用,可以继续。</p> <p>如果出现:</p> <pre><code class="language-bash">-bash: lsof: command not found </code></pre> <p>运行:</p> <pre><code class="language-bash">apt install lsof </code></pre> <p>如果端口被占用了,就把 <code>docker-compose.yml</code> 里的左边端口改一下,比如:</p> <pre><code class="language-yaml">ports: - "9010:9000" </code></pre> <p>后面反向代理的时候也记得填新的端口。</p> <h3 id="6-5-启动-Fast-Note-Sync-Service">6.5 启动 Fast Note Sync Service</h3> <pre><code class="language-bash">cd /root/data/docker_data/fast-note-sync docker compose up -d </code></pre> <p>查看日志:</p> <pre><code class="language-bash">docker compose logs -f </code></pre> <p>没报错的话,理论上访问:</p> <pre><code class="language-text">http://服务器IP:9000 </code></pre> <p>就能看到后台页面。</p> <p>不过这个服务是拿来同步笔记的,不建议直接裸奔公网,咕咕建议一定要配置 HTTPS 反向代理。</p> <h2 id="7--反向代理">7. 反向代理</h2> <h3 id="7-1-利用-Nginx-Proxy-Manager">7.1 利用 Nginx Proxy Manager</h3> <p>在添加反向代理之前,确保你已经完成了域名解析,不会的可以看这个:</p> <p>域名购买、域名解析:<a href="https://blog.laoda.de/archives/namesilo/">https://blog.laoda.de/archives/namesilo/</a></p> <p>之后登录 Nginx Proxy Manager,添加一个新的 Proxy Host。</p> <p>按下面这样填:</p> <ul> <li>Domain Names:<code>note.example.com</code></li> <li>Scheme:<code>http</code></li> <li>Forward Hostname / IP:服务器内网 IP 或 Docker 网桥 IP</li> <li>Forward Port:<code>9000</code></li> <li>Websockets Support:打开</li> <li>Block Common Exploits:打开</li> </ul> <img src="https://blog.laoda.de/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fimg.laoda.de%2Fi%2F2026%2F06%2F30%2F094626-1.jpeg&amp;size=m" alt="fa6fcaa7fab1ff23dd16e99360b1ff79.png" style="zoom:50%;"> <img src="https://blog.laoda.de/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fimg.laoda.de%2Fi%2F2026%2F06%2F30%2F094621-1.jpeg&amp;size=m" alt="3c2616e8c9f49e159a52da02abec0cef.png" style="zoom:50%;"> <img src="https://blog.laoda.de/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fimg.laoda.de%2Fi%2F2026%2F06%2F30%2F094617-1.jpeg&amp;size=m" alt="601f0d3b4b9bf40cdc7515a1f91a1532.png" style="zoom:50%;"> <p>SSL 那边:</p> <ul> <li>勾选 <code>Request a new SSL Certificate</code></li> <li>勾选 <code>Force SSL</code></li> <li>填邮箱</li> <li>同意协议</li> <li>保存</li> </ul> <p>Fast Note Sync 会用到 WebSocket,同步服务要实时通信,所以 NPM 里 <strong>Websockets Support</strong> 记得打开。</p> <p>配置好之后,访问你自己的域名,比如:</p> <pre><code class="language-text">https://note.example.com </code></pre> <p>就能进入后台。</p> <h3 id="7-2-利用-Caddy-反向代理">7.2 利用 Caddy 反向代理</h3> <p>如果你用的是 Caddy,也可以这样写:</p> <pre><code class="language-caddyfile">note.example.com { reverse_proxy 127.0.0.1:9000 } </code></pre> <p>Caddy 会自动申请和续期 SSL 证书,适合不想点面板的小伙伴。</p> <h2 id="8--初始化账号与关闭注册">8. 初始化账号与关闭注册</h2> <p><img src="https://blog.laoda.de/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fimg.laoda.de%2Fi%2F2026%2F06%2F30%2F094612-1.jpeg&amp;size=m" alt="bbb21f6dffecbc855623e8ac3cb71729.png"></p> <p><img src="https://blog.laoda.de/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fimg.laoda.de%2Fi%2F2026%2F06%2F30%2F094609-1.jpeg&amp;size=m" alt="451803df8066fdd2add52f22a11f57ac.png"></p> <p>设置管理员</p> <p><img src="https://blog.laoda.de/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fimg.laoda.de%2Fi%2F2026%2F06%2F30%2F094605-1.jpeg&amp;size=m" alt="9c37c17b0823f305a04c8d64a46c77a1.png"></p> <p>第一次打开网页后台,会让你注册账号。</p> <p>注册完成并登录之后,我们建议马上关闭注册,避免别人也跑来注册。</p> <p>回到服务器:</p> <pre><code class="language-bash">cd /root/data/docker_data/fast-note-sync vim config/config.yaml </code></pre> <p>找到:</p> <pre><code class="language-yaml">user: register-is-enable: true </code></pre> <p>改成:</p> <pre><code class="language-yaml">user: register-is-enable: false </code></pre> <p><img src="https://blog.laoda.de/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fimg.laoda.de%2Fi%2F2026%2F06%2F30%2F094603-1.jpeg&amp;size=m" alt="3f8a4198d80c787e7746541a02e950dc.png"></p> <p>然后重启:</p> <pre><code class="language-bash">docker compose restart </code></pre> <p>这样后面就不会开放注册了。</p> <h2 id="9--Obsidian-插件配置">9. Obsidian 插件配置</h2> <h3 id="9-1-安装插件">9.1 安装插件</h3> <p>打开 Obsidian:</p> <pre><code class="language-text">设置 -&gt; 第三方插件 -&gt; 浏览 </code></pre> <p><img src="https://blog.laoda.de/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fimg.laoda.de%2Fi%2F2026%2F06%2F30%2F094600-1.jpeg&amp;size=m" alt="ec924172842b514749fbb4f53b859892.png"></p> <p><img src="https://blog.laoda.de/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fimg.laoda.de%2Fi%2F2026%2F06%2F30%2F094558-1.jpeg&amp;size=m" alt="92dc5c381a18507c537c5f7fff549f98.png"></p> <p>搜索:</p> <pre><code class="language-text">Fast Note Sync </code></pre> <p>如果插件市场里能搜到,直接安装启用即可。</p> <p><img src="https://blog.laoda.de/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fimg.laoda.de%2Fi%2F2026%2F06%2F30%2F094555-1.jpeg&amp;size=m" alt="fbc67648accd7ad15f36a82187ec9f80.png"></p> <p>如果搜不到,可以去 GitHub Releases 手动下载:</p> <p><a href="https://github.com/haierkeys/obsidian-fast-note-sync/releases">https://github.com/haierkeys/obsidian-fast-note-sync/releases</a></p> <p>下载这几个文件:</p> <pre><code class="language-text">main.js styles.css manifest.json </code></pre> <p>然后放到你的 Obsidian 库目录:</p> <pre><code class="language-text">你的库/.obsidian/plugins/fast-note-sync/ </code></pre> <p>重启 Obsidian 后启用插件。</p> <h3 id="9-2-授权连接服务端">9.2 授权连接服务端</h3> <p>回到 Fast Note Sync Service 的网页后台,访问你自己的域名:</p> <pre><code class="language-text">https://note.example.com </code></pre> <p>右上角可以更改语言:</p> <img src="https://blog.laoda.de/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fimg.laoda.de%2Fi%2F2026%2F06%2F30%2F094553-1.jpeg&amp;size=m" alt="edc4406039f1bb8a4ed42af30306360e.png" style="zoom:50%;"> <p>左侧进入:</p> <pre><code class="language-text">Note Vaults </code></pre> <img src="https://blog.laoda.de/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fimg.laoda.de%2Fi%2F2026%2F06%2F30%2F094547-1.jpeg&amp;size=m" alt="2ac873496c2c94d03449821736ae8c5c.png" style="zoom:50%;"> <p><img src="https://blog.laoda.de/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fimg.laoda.de%2Fi%2F2026%2F06%2F30%2F094541-1.jpeg&amp;size=m" alt="bddbe7ee87ecda288eb5c607f5fcf898.png"></p> <p>可以看到官方提供了两种方式:</p> <ul> <li>One-click Authorization for Obsidian</li> <li>Copy API Config</li> </ul> <p><img src="https://blog.laoda.de/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fimg.laoda.de%2Fi%2F2026%2F06%2F30%2F094539-1.jpeg&amp;size=m" alt="e47330d1e694b7ff3e90c16d241c01d8.png"></p> <p>如果你的电脑能正常唤起 Obsidian,可以直接点 <strong>One-click Authorization for Obsidian</strong>。</p> <p>如果不行,就点 <strong>Copy API Config</strong>,然后手动粘贴到 Obsidian 插件设置里。</p> <p>配置完成之后,Obsidian 就会开始和服务端同步。</p> <p>电脑上我试了下没问题。</p> <p>iPhone上我遇到无法粘贴复制的内容的问题,</p> <img src="https://blog.laoda.de/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fimg.laoda.de%2Fi%2F2026%2F06%2F30%2F094531-1.jpeg&amp;size=m" alt="f2602fb157a2134a518b05bdf082e16b.png" style="zoom:33%;"> <p>只能在手机浏览器里登录网页,然后用一键授权的方式来配置了 = =</p> <h2 id="10--使用体验">10. 使用体验</h2> <p>部署好之后,大概就是这样:</p> <ul> <li>电脑上新建一篇笔记</li> <li>手机 Obsidian 打开同一个 Vault</li> <li>修改会同步到服务端</li> <li>其他设备在线后自动拉取变化</li> <li>图片、附件也可以一起同步</li> <li>网页后台可以查看 Vault、笔记、附件和同步日志</li> </ul> <p>如果你平时 Obsidian 里有很多图片,建议服务器磁盘稍微大一点,不然附件多了之后很快就会占空间。</p> <p>另外,如果你准备把 <code>.obsidian</code> 配置也同步,建议先拿一个测试库试一下,确认插件、主题、配置都没问题之后,再同步主力库。</p> <h2 id="11--更新与卸载">11. 更新与卸载</h2> <h3 id="更新">更新</h3> <pre><code class="language-bash">cd /root/data/docker_data/fast-note-sync docker compose pull docker compose up -d </code></pre> <h3 id="停止">停止</h3> <pre><code class="language-bash">docker compose down </code></pre> <h3 id="卸载">卸载</h3> <pre><code class="language-bash">cd /root/data/docker_data/fast-note-sync docker compose down cd /root/data/docker_data rm -rf fast-note-sync </code></pre> <p>删除前记得先备份:</p> <pre><code class="language-text">/root/data/docker_data/fast-note-sync/storage /root/data/docker_data/fast-note-sync/config </code></pre> <p>你的笔记数据和配置都在这里。</p> <h2 id="12--注意事项">12. 注意事项</h2> <ol> <li>这个项目目前还没有端到端加密,官方 Roadmap 里写了后续计划支持,所以不要部署在不信任的服务器上。</li> <li>第一次注册完账号之后,记得关闭注册。</li> <li><code>auth-token-key</code> 和 <code>share-token-key</code> 不要用默认值。</li> <li>建议只通过 HTTPS 域名访问,不要直接把 <code>9000</code> 端口裸露给公网。</li> <li>Nginx Proxy Manager 里记得打开 WebSocket 支持。</li> <li>重要笔记库第一次同步前,建议先备份一份本地 Vault。</li> <li>如果你的笔记库里有很多大附件,第一次同步可能会比较慢,耐心等一下。</li> </ol> <h2 id="13--最后">13. 最后</h2> <p>Fast Note Sync Service 这个项目还是挺有意思的,它不是简单的 WebDAV 同步,而是专门给 Obsidian 做的一套私有同步服务。</p> <p>相比 WebDAV,它多了网页后台、历史版本、回收站、附件管理、分享、REST API、MCP 这些能力;相比官方 Obsidian Sync,它的优势就是可以自己部署,数据放在自己的服务器上,空间也由自己的硬盘决定。</p> <p>当然,目前端到端加密还没完成,所以更适合放在自己的 VPS 或家里服务器上自用。</p> <p>如果你本来就是 Obsidian 重度用户,又刚好有自己的 VPS,可以折腾一下。</p>

2026/6/30
阅读更多

关键时刻不能掉链子!一年235元开机即用!大陆漫游上网备用方案:3HK 大湾区 eSIM 储值卡购买激活全流程分享(2026)

<img src="https://blog.laoda.de/plugins/feed/assets/telemetry.gif?title=%E5%85%B3%E9%94%AE%E6%97%B6%E5%88%BB%E4%B8%8D%E8%83%BD%E6%8E%89%E9%93%BE%E5%AD%90%EF%BC%81%E4%B8%80%E5%B9%B4235%E5%85%83%E5%BC%80%E6%9C%BA%E5%8D%B3%E7%94%A8%EF%BC%81%E5%A4%A7%E9%99%86%E6%BC%AB%E6%B8%B8%E4%B8%8A%E7%BD%91%E5%A4%87%E7%94%A8%E6%96%B9%E6%A1%88%EF%BC%9A3HK%20%E5%A4%A7%E6%B9%BE%E5%8C%BA%20eSIM%20%E5%82%A8%E5%80%BC%E5%8D%A1%E8%B4%AD%E4%B9%B0%E6%BF%80%E6%B4%BB%E5%85%A8%E6%B5%81%E7%A8%8B%E5%88%86%E4%BA%AB%EF%BC%882026%EF%BC%89&amp;url=/archives/3hk-diy-esim-guide" width="1" height="1" alt="" style="opacity:0;"> <p>作为一个需要正常访问国际互联网的人士,为了关键时刻不掉链子(尤其是最近,有的小伙伴应该有体会)我觉得有必要给自己增加一个备选方案。</p> <p>我之前去德国正好<a href="https://blog.laoda.de/archives/germany-iphone-tax-refund-guide" title="买了一部德版的iPhone">买了一部德版的iPhone</a>,支持eSIM,月底又要去珠海出差,顺便去澳门一趟,就想着要不索性换个思路:直接用一张境外 SIM 卡在大陆漫游,原生 IP,开机即用,全程不需要任何额外姿势。</p> <p>我先前去香港买过一个hahasim的卡,保号1年10元,接接验证码还不错,不过没流量(或者说流量非常贵)。</p> <p>对比了 CMHK、CTM 快卡、澳门蓝卡几家之后,最终选了 3HK 大湾区 DIY 储值卡:HK$268 一年 45GB 流量(折人民币约 235 元,5 元/GB),支持 eSIM 远程购买、邮箱收码扫码即用;大陆漫游可在移动/联通/电信三网之间切换(一般是联通);开热点之后电脑、iPad 也可以共享香港 IP。性价比、便捷度、稳定性三个维度综合下来,我个人认为是目前能找到的最优解,于是有了这篇折腾记录。</p> <p>以下内容需要你有一部支持海外eSIM的手机(比如港版iPhone,国产的iPhone Air我估计不行)</p> <p>首先登录官网购买,选择365日组合——大湾区(内地、香港、澳门)</p> <p>官网地址:<a href="https://www.three.com.hk/prepaid/DIY/tc/offer/travel">https://www.three.com.hk/prepaid/DIY/tc/offer/travel</a></p> <img src="https://blog.laoda.de/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fimg.laoda.de%2Fi%2F2026%2F05%2F15%2F145150-1.webp&amp;size=m" alt="ee0492f96babfa97bd3cdc5f7ffb8e55.png" style="zoom:50%;"> <p>外游数据这个,自己选择,实测好像即使选暂不使用也会开始套餐计费时间,</p> <img src="https://blog.laoda.de/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fimg.laoda.de%2Fi%2F2026%2F05%2F15%2F145148-1.webp&amp;size=m" alt="173f40b13290a28e675e3769d5f1e147.png" style="zoom:50%;"> <p>在大陆的话直接选择eSIM,实体卡只能寄到香港地址,(注意国产的iPhone 17 Air好像只支持大陆的eSIM,这个可能用不了)</p> <img src="https://blog.laoda.de/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fimg.laoda.de%2Fi%2F2026%2F05%2F15%2F145146-1.webp&amp;size=m" alt="fe591b7e76e073d9e574db5446370b2f.png" style="zoom:50%;"> <p>确认套餐没问题,40G+5G</p> <img src="https://blog.laoda.de/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fimg.laoda.de%2Fi%2F2026%2F05%2F15%2F145144-1.webp&amp;size=m" alt="b6e6b9c152af400bedc0bce6b5c5b542.png" style="zoom:50%;"> <p>输入一个邮箱,会帮你创建账号,</p> <img src="https://blog.laoda.de/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fimg.laoda.de%2Fi%2F2026%2F05%2F15%2F145143-1.webp&amp;size=m" alt="59750e9c6f46da4b09a750872f5ed8c8.png" style="zoom:50%;"> <p>收到验证码,登录,建立账户,</p> <img src="https://blog.laoda.de/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fimg.laoda.de%2Fi%2F2026%2F05%2F15%2F145142-1.webp&amp;size=m" alt="fea04446fea9b1e054d75e94d5bfc364.png" style="zoom:50%;"> <p>选择付款方式,支付宝微信都可以,我这边用了汇丰的Red信用卡,有4%的返现。</p> <img src="https://blog.laoda.de/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fimg.laoda.de%2Fi%2F2026%2F05%2F15%2F145140-1.webp&amp;size=m" alt="953d131992e1ab459275db49e5be821b.png" style="zoom:50%;"> <p>成功!</p> <img src="https://blog.laoda.de/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fimg.laoda.de%2Fi%2F2026%2F05%2F15%2F145138-1.webp&amp;size=m" alt="d85454d526fd2e361686bd57b593b961.png" style="zoom:50%;"> <p>邮件会收到一个二维码,</p> <img src="https://blog.laoda.de/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fimg.laoda.de%2Fi%2F2026%2F05%2F15%2F145136-1.webp&amp;size=m" alt="3ddaa0f47b681764027bb4316b6ed9ce.png" style="zoom:50%;"> <p>打开手机的蜂窝网络,添加eSIM,</p> <img src="https://blog.laoda.de/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fimg.laoda.de%2Fi%2F2026%2F05%2F15%2F145134-1.webp&amp;size=m" alt="截屏 2026-05-15 14.08.43.png" style="zoom:50%;"> <p>很快就能加上,记得打开数据漫游,</p> <img src="https://blog.laoda.de/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fimg.laoda.de%2Fi%2F2026%2F05%2F15%2F145132-1.webp&amp;size=m" alt="722774f61207f758d0b16ed993e387b9.png" style="zoom:50%;"> <p>下载官方APP——My3</p> <img src="https://blog.laoda.de/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fimg.laoda.de%2Fi%2F2026%2F05%2F15%2F145130-1.webp&amp;size=m" alt="ee4cb942d543c84f697576cbae055074.png" style="zoom: 50%;"> <p>用手机号码加验证码登录,然后记得打开这个开关</p> <img src="https://blog.laoda.de/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fimg.laoda.de%2Fi%2F2026%2F05%2F15%2F145127-1.webp&amp;size=m" alt="8c858f7ed38144c35df48bfa6eaa36e4.png" style="zoom:33%;"> <img src="https://blog.laoda.de/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fimg.laoda.de%2Fi%2F2026%2F05%2F15%2F145125-1.webp&amp;size=m" alt="79ba6291a9d87e983a975e9ea1e181f0.png" style="zoom:33%;"> <p>蜂窝数据切换到3HK的卡就能上网了!不需要任何额外姿势。</p> <p>如果要在香港使用通话和短信功能,需要在app里实名一下。</p> <p>非香港人士选港澳通行证就可以了。</p> <img src="https://blog.laoda.de/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fimg.laoda.de%2Fi%2F2026%2F05%2F15%2F145121-1.webp&amp;size=m" alt="截屏 2026-05-15 14.40.05.png" style="zoom:50%;"> <p>注意:</p> <p>这张卡是"流量套餐 + 通话短信按量扣费"模式。年套餐 268 港币里只包含 45GB 数据流量,不含任何通话或短信额度。需要打电话/发短信,得在 My3 App 里单独充值余额,按下面这个标准扣费——</p> <ul> <li>接验证码(大陆+香港):免费 ✅ 用来注册海外平台超香</li> <li>大陆漫游接打电话:HK$6-8/分钟 偏贵,建议走微信语音</li> <li>大陆发短信:HK$3.5/条 贵,能用微信就别用 SMS</li> <li>香港本地通话:可单独买 HK$20/10000 分钟包,几乎白送</li> </ul> <p>总结一句:当数据卡用最划算,通话短信都用其他方式替代。</p> <p>大家有别的更好用的卡也欢迎评论区分享!</p>

2026/5/15
阅读更多

【好玩儿的Docker项目】十分钟搭建一个「极简私人网盘」——hacdias/webdav,让你可以像逛本地文件夹一样逛 VPS

<img src="https://blog.laoda.de/plugins/feed/assets/telemetry.gif?title=%E3%80%90%E5%A5%BD%E7%8E%A9%E5%84%BF%E7%9A%84Docker%E9%A1%B9%E7%9B%AE%E3%80%91%E5%8D%81%E5%88%86%E9%92%9F%E6%90%AD%E5%BB%BA%E4%B8%80%E4%B8%AA%E3%80%8C%E6%9E%81%E7%AE%80%E7%A7%81%E4%BA%BA%E7%BD%91%E7%9B%98%E3%80%8D%E2%80%94%E2%80%94hacdias%2Fwebdav%EF%BC%8C%E8%AE%A9%E4%BD%A0%E5%8F%AF%E4%BB%A5%E5%83%8F%E9%80%9B%E6%9C%AC%E5%9C%B0%E6%96%87%E4%BB%B6%E5%A4%B9%E4%B8%80%E6%A0%B7%E9%80%9B%20VPS&amp;url=/archives/docker-compose-install-hacdias-webdav" width="1" height="1" alt="" style="opacity:0;"> <h2 id="1--唠嗑">1. 唠嗑</h2> <p>聊到「私人网盘」,大部分人第一反应都是 Nextcloud、Seafile 这种「全家桶」——文件、日历、联系人、笔记、视频会议,功能丰富,大而全,但是比较吃机器的配置,VPS 性能差点儿的(比如 1G 内存的),像 Nextcloud 可能都跑不动。</p> <p>很多时候我们的需求其实非常朴素:</p> <blockquote> <p>「我能不能在 Mac 的 Finder 里直接打开 VPS 上的某个目录,像本地文件夹一样拖拖拽拽?」</p> </blockquote> <p>答案是可以,而且根本不用上 Nextcloud,一个轻量级的 WebDAV 服务就够了。</p> <h2 id="2--hacdias-webdav-简介">2. hacdias/webdav 简介</h2> <p>今天咕咕给大家介绍的 <a href="https://github.com/hacdias/webdav">hacdias/webdav</a> 就是这种「需求小但精准」的代表作——Go 写的独立 WebDAV 服务器,整个程序就一个二进制文件,配置文件用 YAML,跑起来内存占用几十兆。</p> <p>这个作者也是之前和大家介绍的 File Browser 项目的作者。</p> <img src="https://blog.laoda.de/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fimg.laoda.de%2Fi%2F2026%2F05%2F07%2F114404-1.webp&amp;size=m" alt="671776cf97b39dc312016575eb5d85d2.png" style="zoom:50%;"> <blockquote> <p>旧文回顾:<a href="https://blog.laoda.de/archives/aria2-rclone-filebrowser">手把手教你搭建私人离线下载神器 Aria2,搭配 Rclone+Filebrowser 自动上传至网盘</a></p> </blockquote> <p>它能干什么?</p> <ul> <li>把 VPS 上的某个目录通过 WebDAV 协议暴露出来</li> <li>支持多用户、多目录、多权限(CRUD 自由组合)</li> <li>支持 bcrypt 哈希密码(不用让明文密码裸奔)</li> <li>反代友好(配合 Nginx Proxy Manager 体验非常丝滑)</li> <li>没有 Web 界面、没有数据库、没有花里胡哨的功能</li> </ul> <p>适合的场景包括:</p> <ul> <li>Obsidian / Joplin / Logseq 这类笔记软件的云端同步</li> <li>KeePass / KeePassXC 数据库远程同步</li> <li>手机相册备份目标盘</li> <li>多设备共享一个工作目录</li> <li>摆脱 iCloud / OneDrive / Dropbox 的容量焦虑</li> </ul> <p>部署完之后,Mac、Windows、iPhone、Android,以及 Obsidian、Joplin、KeePassXC 这种支持 WebDAV 的应用都能直接挂上来用。</p> <p>可以让你告别云盘焦虑!</p> <h2 id="3--相关地址">3. 相关地址</h2> <p>官方 GitHub 地址:<a href="https://github.com/hacdias/webdav">https://github.com/hacdias/webdav</a> (目前 5.5k 个 star,欢迎大家去给项目点星星!)</p> <p>当然如果你想自己用 docker 搭建一个,那我们就继续往下!</p> <h2 id="4--搭建环境">4. 搭建环境</h2> <ul> <li>服务器:咕咕这边用的香港的 <a href="http://gao.ee/lca">莱卡云服务器</a>,虽然 1 核 512M 都够用,重点看磁盘大小和带宽,但是因为用的 docker 搭建,还是建议服务器内存 1G 以上,当然你也可以选择其他 <a href="https://blog.laoda.de/vps">高性价比的服务器</a>。</li> <li>系统:Debian 11 (<a href="https://blog.laoda.de/archives/useful-script/">DD 脚本</a> 非必需 DD,用原来的系统也 OK,之后教程都是用 Debian 或者 Ubuntu 搭建~)</li> <li>安装好 Docker、Docker-compose(<a href="https://blog.laoda.de/archives/hello-docker/">相关脚本</a>)</li> <li>【必需】域名一枚,并做好解析到服务器上(<a href="https://blog.laoda.de/archives/namesilo/">域名购买、域名解析</a> <a href="https://www.bilibili.com/video/BV1Sy4y1k7kZ/">视频教程</a>)</li> <li>【非必需】提前安装好宝塔面板海外版本 aapanel,并安装好 Nginx(<a href="https://forum.aapanel.com/d/9-aapanel-linux-panel-6812-installation-tutorial">安装地址</a>)</li> <li>【非必需本教程选用】安装好 Nginx Proxy Manager(<a href="https://blog.laoda.de/archives/nginxproxymanager/">相关教程</a>)</li> </ul> <h2 id="5--搭建视频-过俩周补充-----">5. 搭建视频(过俩周补充 = =)</h2> <h3 id="5-1-YouTube">5.1 YouTube</h3> <p>视频地址:</p> <h3 id="5-2-哔哩哔哩">5.2 哔哩哔哩</h3> <p>哔哩哔哩:</p> <h2 id="6--搭建方式">6. 搭建方式</h2> <h3 id="6-1-安装-Docker-与-Nginx-Proxy-Manager">6.1 安装 Docker 与 Nginx Proxy Manager</h3> <p>可以直接参考这篇内容:</p> <p><a href="https://blog.laoda.de/archives/nginxproxymanager/">https://blog.laoda.de/archives/nginxproxymanager/</a></p> <h3 id="6-2-创建安装目录">6.2 创建安装目录</h3> <p>创建一下安装的目录:</p> <pre><code class="language-bash">sudo -i mkdir -p /root/data/docker_data/webdav cd /root/data/docker_data/webdav </code></pre> <p>接着我们来编辑下<code>docker-compose.yml</code></p> <pre><code class="language-bash">vim docker-compose.yml </code></pre> <pre><code class="language-yaml">services: webdav: image: hacdias/webdav:latest container_name: webdav restart: unless-stopped ports: - "6065:6065" # 左边的端口可以换成 vps 上没有用过的端口 volumes: - ./config.yml:/config.yml:ro - ./data:/data </code></pre> <p>其中的左边的<code>6065</code>可以改成服务器上没有用过的端口。</p> <p>修改完成之后,可以在英文输入法下,按 <code>i</code> 修改,完成之后,按一下 <code>esc</code>,然后 <code>:wq</code> 保存退出。</p> <h4 id="生成-bcrypt-密码">生成 bcrypt 密码</h4> <p>为了不让明文密码裸奔在配置文件里,我们用 bcrypt 哈希一下密码。Debian / Ubuntu 上一行命令搞定:</p> <pre><code class="language-bash">apt install -y apache2-utils htpasswd -nbBC 12 "" '你想用的密码' | tr -d ':' | sed 's/\$2y/\$2a/' </code></pre> <p>会输出一串长这样的玩意儿:</p> <pre><code>$2a$12$8gsX8AKtD3EMdmksoqSDLAIEMEMMrSbKx/q0GG </code></pre> <p>把它整段复制下来,待会儿要塞到 <code>config.yml</code> 里。</p> <blockquote> <p>如果你嫌麻烦,明文密码也是合法配置——把 <code>config.yml</code> 设成 <code>chmod 600</code> 也能挡住一般场景。但既然多花一行命令的事儿,咕咕还是建议哈希一下。</p> </blockquote> <h3 id="写-config-yml">写 config.yml</h3> <pre><code class="language-bash">vim config.yml </code></pre> <p>贴入:</p> <pre><code class="language-yaml">address: 0.0.0.0 port: 6065 prefix: / # 反代后面一定要打开,让 webdav 信任 X-Forwarded-* 头 behindProxy: true # 全局默认权限(被下方 users 覆盖) permissions: none directory: /data users: - username: gugu password: "{bcrypt}$2a$12$你刚才生成的那一长串" directory: /data/gugu permissions: CRUD # 想再加一个只读访客?解开注释即可 # - username: guest # password: "{bcrypt}$2a$12$xxx" # directory: /data/public # permissions: R </code></pre> <blockquote> <p>关于 <code>permissions</code> 这一栏:<strong>C</strong> = Create(创建)、<strong>R</strong> = Read(读取)、<strong>U</strong> = Update(更新)、<strong>D</strong> = Delete(删除)。给自己用就 <code>CRUD</code> 全开,分享给朋友的访客号建议只给 <code>R</code>。</p> </blockquote> <p>保存退出,给配置文件上锁:</p> <pre><code class="language-bash">chmod 600 config.yml mkdir -p data </code></pre> <h3 id="6-3-查看端口是否被占用">6.3 查看端口是否被占用</h3> <p>查看端口是否被占用(以 <code>6065</code> 为例),输入:</p> <pre><code class="language-bash">lsof -i:6065 #查看 6065 端口是否被占用,如果被占用,重新自定义一个端口 </code></pre> <p>如果啥也没出现,表示端口未被占用,我们可以继续下面的操作了~</p> <p>如果出现:</p> <pre><code class="language-bash">-bash: lsof: command not found </code></pre> <p>运行:</p> <pre><code class="language-bash">apt install lsof #安装 lsof </code></pre> <p>如果端口没有被占用(被占用了就修改一下端口,比如改成 <code>8381</code>,注意 docker 命令行里和防火墙都要改)</p> <h3 id="6-4-启动-webdav">6.4 启动 webdav</h3> <pre><code>cd /root/data/docker_data/webdav docker compose up -d # 注意,老版本用户用 docker-compose up -d docker compose logs -f </code></pre> <p>镜像非常小,等待拉取好镜像,出现 <code>Created</code>的字样之后,</p> <p>如果日志里看到类似:</p> <pre><code>Listening on 0.0.0.0:6065 </code></pre> <p>并且没有报错,那就说明跑起来了。<code>Ctrl+C</code> 退出日志查看,容器还在后台老老实实工作。</p> <p>这时候你已经可以在浏览器里访问 <code>http://你的 VPS-IP:6065</code> 试试,输入用户名密码会看到一个空空荡荡的目录列表,说明服务正常。</p> <p>但是我们部署在公网一定要考虑使用反向代理工具配置 SSL!</p> <blockquote> <p>不用域名直接 IP 访问也不是不行,但 Mac 的 Finder 在 HTTP 下挂 WebDAV 会自动降级成只读,套上 HTTPS 才能愉快地读写。所以域名 + HTTPS 是强烈推荐的。</p> </blockquote> <p>做反向代理前,你需要一个域名!</p> <p><a href="https://loll.cc/ns">namesilo</a> 上面 xyz 后缀的域名一年就 7 块钱,可以年抛。(冷知识,namesilo 上 6 位数字的 xyz 续费永远都是 0.99 美元 = =)</p> <p>如果想要长期使用,还是建议买 com 后缀的域名,更加正规一些,可以输入 <code>laodade</code> 来获得 1 美元的优惠(不知道现在还有没有)</p> <p><a href="https://loll.cc/ns">namesilo</a> 自带隐私保护,咕咕一直在用这家,价格也是这些注册商里面比较低的,关键是他家不像其他家域名注册商,没有七七八八的套路!(就是后台界面有些 <del>丑</del> 古老 = =)</p> <p><a href="https://blog.laoda.de/archives/namesilo/">【域名购买】Namesilo 优惠码和域名解析教程(附带服务器购买推荐和注意事项)</a></p> <p>我们接着往下看!</p> <h2 id="7--反向代理">7. 反向代理</h2> <h3 id="7-1-利用-Nginx-Proxy-Manager">7.1 利用 Nginx Proxy Manager</h3> <p>在添加反向代理之前,确保你已经完成了域名解析,不会的可以看这个:<strong>域名一枚,并做好解析到服务器上</strong>(<a href="https://blog.laoda.de/archives/namesilo/">域名购买、域名解析</a> <a href="https://www.bilibili.com/video/BV1Sy4y1k7kZ/">视频教程</a>) (名称改成你自己想要的域名前缀即可)</p> <p><img src="https://blog.laoda.de/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fimg.laoda.de%2Fi%2F2026%2F05%2F07%2F114401-1.webp&amp;size=m" alt="68bfcaff11811fa6f3e6b24e6cfc09ce.png"></p> <p>之后,登陆 Nginx Proxy Manager(不会的看这个:<strong>安装 Nginx Proxy Manager</strong>(<a href="https://blog.laoda.de/archives/nginxproxymanager/">相关教程</a>))</p> <blockquote> <p><strong>注意:</strong></p> <p>Nginx Proxy Manager(以下简称 NPM)会用到 <code>80</code>、<code>443</code> 端口,所以本机不能占用(比如原来就有 Nginx)</p> </blockquote> <p>打开你的 NPM 后台,新建一个 <strong>Proxy Host</strong>:</p> <p><strong>Details 标签页:</strong></p> <ul> <li><strong>Domain Names</strong>:填你的域名,比如 <code>webdav.example.com</code></li> <li><strong>Scheme</strong>:<code>http</code></li> <li><strong>Forward Hostname / IP</strong>:填你 VPS 的内网 IP,或者直接 <code>宿主机 IP</code></li> <li><strong>Forward Port</strong>:<code>6065</code></li> <li><strong>Cache Assets</strong>:关</li> <li><strong>Block Common Exploits</strong>:⚠️ <strong>一定要关!</strong> NPM 这个开关会拦掉一些 WebDAV 的 HTTP 方法(比如 <code>PROPFIND</code>、<code>MKCOL</code>、<code>PROPPATCH</code>),不关你客户端会一直 405 报错,搞得你怀疑人生</li> <li><strong>Websockets Support</strong>:打开</li> </ul> <blockquote> <p>注意填写对应的 <code>域名</code>、<code>IP</code> 和 <code>端口</code>,按文章来的话,应该是 <code>6065</code></p> </blockquote> <p><strong>IP 填写:</strong></p> <p>如果 Nginx Proxy Manager 和 webdav 在同一台服务器上,可以在终端输入:</p> <pre><code class="language-bash">ip addr show docker0 </code></pre> <p>查看对应的 Docker 容器内部 IP。</p> <img src="https://blog.laoda.de/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fimg.laoda.de%2Fi%2F2024%2F12%2F12%2F153819-1.webp&amp;size=m" style="zoom:50%;"> <p>否则直接填 webdav 所在的服务器 IP 就行。</p> <img src="https://blog.laoda.de/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fimg.laoda.de%2Fi%2F2026%2F05%2F07%2F114357-1.webp&amp;size=m" alt="c3aecaf3e776af727e4f835a2eb9ac3e.png" style="zoom:33%;"> <p><strong>SSL 标签页:</strong></p> <ul> <li><strong>SSL Certificate</strong>:选 <code>Request a new SSL Certificate</code>,签个 Let's Encrypt</li> <li><strong>Force SSL</strong>:打开</li> <li><strong>HTTP/2 Support</strong>:打开</li> <li><strong>HSTS Enabled</strong>:可选</li> </ul> <img src="https://blog.laoda.de/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fimg.laoda.de%2Fi%2F2026%2F05%2F07%2F114354-1.webp&amp;size=m" alt="14e4edadd58f306888abf7c346ae47b4.png" style="zoom:33%;"> <p><strong>Advanced 标签页:</strong></p> <p>加上这段 nginx 配置(<strong>这段非常关键,没它传大文件会断开</strong>):</p> <pre><code class="language-nginx">client_max_body_size 0; proxy_request_buffering off; proxy_read_timeout 3600s; proxy_send_timeout 3600s; </code></pre> <img src="https://blog.laoda.de/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fimg.laoda.de%2Fi%2F2026%2F05%2F07%2F114352-1.webp&amp;size=m" alt="42cec3c76a3939e01e14aa6b390f363a.png" style="zoom:33%;"> <p>保存。</p> <p>然后访问域名就可以访问了!</p> <h3 id="7-2-利用宝塔面板-应该类似-没具体尝试-">7.2 利用宝塔面板(应该类似,没具体尝试)</h3> <p>发现还是有不少小伙伴习惯用宝塔面板,这边也贴一个宝塔面板的反代配置:</p> <p>直接新建一个站点,不要数据库,不要 php,纯静态即可。</p> <p>然后打开下面的配置,修改 Nginx 的配置。</p> <img src="https://blog.laoda.de/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fimg.laoda.de%2Fi%2F2024%2F12%2F12%2F153816-1.webp&amp;size=m" alt="image-20220819150345725" style="zoom: 33%;"> <img src="https://blog.laoda.de/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fimg.laoda.de%2Fi%2F2024%2F12%2F12%2F153814-1.webp&amp;size=m" alt="image-20220819150542867" style="zoom: 33%;"> <p>代码如下:</p> <pre><code class="language-nginx">location / { proxy_pass http://127.0.0.1:6065/; # 注意改成你实际使用的端口 rewrite ^/(.*)$ /$1 break; proxy_redirect off; proxy_set_header Host $host; proxy_set_header X-Forwarded-Proto $scheme; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header Upgrade-Insecure-Requests 1; proxy_set_header X-Forwarded-Proto https; } </code></pre> <p>此方法对 90% 的反向代理都能生效,然后就可以用域名来安装访问了。</p> <p>有同学可能会问,为什么不直接用宝塔自带的反向代理功能。</p> <img src="https://blog.laoda.de/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fimg.laoda.de%2Fi%2F2024%2F12%2F12%2F153804-1.webp&amp;size=m" alt="image-20220819150730128" style="zoom: 33%;"> <p>也可以,不过咕咕自己之前遇到过当有多个网站需要反代的时候,在这边设置会报错的情况 = =</p> <p>所以后来就不用了,直接用上面的方法来操作了。</p> <h2 id="8--使用教程">8. 使用教程</h2> <h2 id="在-Mac-上挂载">在 Mac 上挂载</h2> <p>打开 Finder,按 <code>⌘K</code>(或者菜单栏 → 前往 → 连接服务器),输入:</p> <img src="https://blog.laoda.de/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fimg.laoda.de%2Fi%2F2026%2F05%2F07%2F114347-1.webp&amp;size=m" alt="3e5d1bd55bb2b633169ec5221a86e5e3.png" style="zoom:50%;"> <img src="https://blog.laoda.de/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fimg.laoda.de%2Fi%2F2026%2F05%2F07%2F114344-1.webp&amp;size=m" alt="d75accde7480572ed2e8f91b92ab9ca9.png" style="zoom:50%;"> <img src="https://blog.laoda.de/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fimg.laoda.de%2Fi%2F2026%2F05%2F07%2F114343-1.webp&amp;size=m" alt="35d5d96cb1dbf545631b5ca36dd11d2b.png" style="zoom:50%;"> <pre><code>https://webdav.example.com </code></pre> <p>回车,会弹出登录框,填你在 <code>config.yml</code> 里设置的用户名 + <strong>原始密码</strong>(注意不是 bcrypt 哈希!)。点连接,VPS 上的目录就会以网络盘的形式出现在 Finder 侧边栏。</p> <p>到这里你就可以拖拖拽拽了——复制、改名、双击打开、新建文件夹,全都和本地操作没差。</p> <p>不过你本地到服务器的连接速度也很重要,像我测试的时候用的是美国的机器,反代用的香港,本地在上海操作,就会没有那么的丝滑。</p> <p>如果用美国线路优化的 VPS,就会丝滑很多。</p> <p>我还测试了上海的服务器,就和本地操作没差别了。</p> <p><img src="https://blog.laoda.de/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fimg.laoda.de%2Fi%2F2026%2F05%2F07%2F114341-1.webp&amp;size=m" alt="53c457e49583dce57c236dafc41689fd.png"></p> <h3 id="8-1-更新-webdav">8.1 更新 webdav</h3> <p>这个项目后续会持续有更新,所以提供一个更新的方式。</p> <pre><code class="language-yaml">cd /root/data/docker_data/webdav docker compose pull docker compose up -d # 请不要使用 docker compose stop 来停止容器,因为这么做需要额外的时间等待容器停止;docker compose up -d 直接升级容器时会自动停止并立刻重建新的容器,完全没有必要浪费那些时间。 docker image prune # prune 命令用来删除不再使用的 docker 对象。删除所有未被 tag 标记和未被容器使用的镜像 </code></pre> <p>提示:</p> <pre><code class="language-bash">WARNING! This will remove all dangling images. Are you sure you want to continue? [y/N] </code></pre> <p>输入 <code>y</code></p> <p>利用 Docker 搭建的应用,更新非常容易~</p> <h3 id="8-2-卸载-webdav">8.2 卸载 webdav</h3> <p>同样进入安装页面,先停止所有容器。</p> <pre><code class="language-bash">cd /root/data/docker_data/webdav docker compose down cd .. rm -rf /root/data/docker_data/webdav # 完全删除 </code></pre> <p>可以卸载得很干净。</p> <h2 id="9--使用问题">9. 使用问题</h2> <h3 id="一个不得不吐槽的问题">一个不得不吐槽的问题</h3> <p>Mac 自带的 Finder WebDAV 客户端……怎么说呢,<strong>残废</strong>。具体表现:</p> <ul> <li>传大文件容易卡死或者超时</li> <li>偶尔莫名其妙断连,得重新挂载</li> <li>会自动生成一堆 <code>.DS_Store</code>、<code>._xxx</code> 这类隐藏文件污染目录</li> <li>写入速度比读取慢得多</li> <li>偶尔会把刚创建的文件「藏起来」让你看不到,刷新一下又出来了</li> </ul> <p><img src="https://blog.laoda.de/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fimg.laoda.de%2Fi%2F2026%2F05%2F07%2F114327-1.webp&amp;size=m" alt="3fe696e38a9355115c4bc04af0fa76ae.png"></p> <p>如果你只是偶尔传几个文档应付一下没问题。但想当主力网盘用,我搜索了一下,网络上推荐了几个第三方客户端:</p> <ul> <li><strong>Mountain Duck</strong>(付费,约 $39 一次性)—— 真正把 WebDAV 挂成本地盘符,Finder 里和访问 iCloud Drive 几乎没差,支持离线缓存、上传队列、智能同步</li> <li><strong>Cyberduck</strong>(免费)—— Mountain Duck 同公司出品,但只能「打开」不能「挂载」,操作起来更像 FTP 客户端</li> <li><strong>rclone mount</strong>(免费,命令行)—— 不嫌折腾的话稳定性和性能都很好,还能加 VFS 缓存提速</li> </ul> <p>大家有用到好用的客户端也欢迎在评论区分享交流!</p> <h2 id="安全小贴士">安全小贴士</h2> <p>WebDAV 暴露在公网上,几条小建议:</p> <ol> <li><strong><code>config.yml</code> 一定 <code>chmod 600</code></strong>,避免被同机器的其他用户读到你的哈希</li> <li><strong>NPM 里给这个 host 加 IP 白名单</strong>,或者套一层 Cloudflare Access、Authelia 之类的二次认证,公网裸奔风险还是不小的</li> <li><strong>fail2ban 也可以装一下</strong>,防止有人暴力试密码</li> <li><strong>重要数据用 rclone 同步一份到 Backblaze B2 或 Cloudflare R2</strong> —— hacdias/webdav 本身没有版本管理,删了就真的删了。B2 的 5GB 免费额度对小型笔记同步绰绰有余</li> <li><strong>VPS 快照定期开起来</strong>,多花不了几块钱,关键时刻能救命</li> </ol> <p>大家使用上还有什么心得或者问题,欢迎评论区交流~</p> <h2 id="10--结尾">10. 结尾</h2> <p>祝大家用得开心,有问题可以去 GitHub 提 <a href="https://github.com/hacdias/webdav/issues">Issues</a>,也可以在评论区互相交流探讨。</p> <p>同时,有能力给项目做贡献的同学,也欢迎积极加入到 <a href="https://github.com/hacdias/webdav">项目</a> 中来,贡献自己的一份力量!</p> <p>最后,感谢作者 <a href="https://github.com/hacdias">@Henrique Dias</a> 的辛苦付出,让我们能用到这么优秀的项目!欢迎大家都去给这个项目点个 star。</p> <h2 id="参考资料">参考资料</h2> <p>官方 GitHub:<a href="https://github.com/hacdias/webdav">https://github.com/hacdias/webdav</a></p>

2026/5/7
阅读更多

从「不会 Swift」到 App Store 过审:我用 Claude Code 又写了个 iOS App

<img src="https://blog.laoda.de/plugins/feed/assets/telemetry.gif?title=%E4%BB%8E%E3%80%8C%E4%B8%8D%E4%BC%9A%20Swift%E3%80%8D%E5%88%B0%20App%20Store%20%E8%BF%87%E5%AE%A1%EF%BC%9A%E6%88%91%E7%94%A8%20Claude%20Code%20%E5%8F%88%E5%86%99%E4%BA%86%E4%B8%AA%20iOS%20App&amp;url=/archives/claude-code-ios-app" width="1" height="1" alt="" style="opacity:0;"> <h2 id="起点----上一篇挖的坑-这一篇填了">起点 —— 上一篇挖的坑,这一篇填了</h2> <blockquote> <p>2026年5月1日更新:国区Apple Store也上架了,中文名字:续然,欢迎大家使用和反馈意见!</p> </blockquote> <p><a href="https://blog.laoda.de/archives/claude-code-halo-theme">上一篇</a> 写完 Claude Code 魔改 Halo 主题,文末我顺手挖了个坑——说下一步想试试用 Claude 做个 iOS App。当时吹牛成分 60%、好奇成分 40%,没想到这话放出去不到一周,App 真就写完提审了。</p> <p>动手之前我给自己定了两个目标。</p> <p><strong>一是摸一下 Claude 的能力边界</strong>。Halo 主题那种活,网页端改改 CSS,对 Claude 来说是舒适区。iOS 开发就不一样——SwiftUI、SwiftData、真机调试、证书签名、App Store 审核,每一环都是新坑,我想看看咕咕坐副驾这事能跑多远。</p> <p><strong>二是真有这个需求</strong>。不是为了写博客硬编的场景——我自己就囤着一堆 VPS、域名、邮箱、SaaS 订阅,哪天哪个扣费、哪天哪个到期,全靠Notion记录肉眼盯。(好吧我承认起因其实是怕自己的<a href="https://blog.laoda.de/archives/vps-bandwagonhost">搬瓦工</a>、<a href="https://blog.laoda.de/archives/vps-ion-cloud">iON</a> 等等传家宝 VPS 忘记续费 = =)。</p> <p>上一篇的评论区,有朋友推荐了 <a href="https://github.com/ellite/Wallos">Wallos</a>——一个自托管的订阅管理工具。我确实很早之前 Docker 部署了一份(<a href="https://blog.laoda.de/archives/docker-compose-install-wallos">也出过教程</a>),跑在自己 VPS 上。但 Wallos 的提醒路径是<strong>邮件</strong>——我经常忘记开邮箱…… 而且我的邮箱太多了……</p> <p>手机这东西一天看 50 次。如果有一个 App 把提醒直接推到锁屏——肯定不会错过。</p> <p>动机清晰了。</p> <p>2026 年 4 月 21 日晚上 7 点 28 分,我在一个空文件夹里打开 Claude Code,告诉它:</p> <p>「做一个 iOS App,追踪订阅和域名到期时间,本地存储,不要账号,不要云」——就这么开始。</p> <h2 id="图标----一只带铃铛的鸽子">图标 —— 一只带铃铛的鸽子</h2> <img src="https://blog.laoda.de/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fimg.laoda.de%2Fi%2F2026%2F04%2F24%2F201346-1.webp&amp;size=m" alt="AppIcon-1024.png" style="zoom:33%;"> <p>整个项目里,<strong>唯一一个从一开始我就想清楚、没让 Claude 瞎发挥的东西</strong>,就是这只鸽子。</p> <ul> <li>🕊️ <strong>鸽子 = 信使</strong>——按时把消息捎到</li> <li>🔔 <strong>铃铛 = 通知</strong>——到点了响一声</li> <li>🧡 <strong>粉橙渐变</strong>——压住"催债感"和"焦虑感",DueSub 做的是温柔的提醒,不是红底白字的「还有 3 天扣费!」</li> </ul> <p>图标里的一图二喻,其实就是这个 App 的产品哲学——<strong>非打扰式、按时到达、不制造焦虑</strong>。</p> <p>功能是"续费提醒",视觉符号是"捎着铃铛的信鸽"——我只用一句话把这个意向说给 Claude 听,基本上两版就过了。这也是这次协作里我觉得最神奇的地方——<strong>你脑子里模糊的画面,只要你能用一句话说清楚它的内核,Claude 基本能把它落到像素上</strong>。</p> <p>当然也适配黑暗模式:</p> <img src="https://blog.laoda.de/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fimg.laoda.de%2Fi%2F2026%2F04%2F24%2F201515-1.webp&amp;size=m" alt="AppIcon-1024-Dark.png" style="zoom:33%;"> <h2 id="成品长这样">成品长这样</h2> <p>话不多说,直接上图。</p> <img src="https://blog.laoda.de/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fimg.laoda.de%2Fi%2F2026%2F04%2F24%2F201452-1.webp&amp;size=m" alt="01-list.png" style="zoom:33%;"> <p><strong>主列表</strong>:支出和收入同屏对照,月付、季付、年付、一次性,所有周期一个列表里理清。顶部那个粉橙色的卡片显示「30 天内还有几笔」,是我自己每天打开 App 最想第一眼看到的信息。</p> <img src="https://blog.laoda.de/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fimg.laoda.de%2Fi%2F2026%2F04%2F24%2F201417-1.webp&amp;size=m" alt="05-homewidget.png" style="zoom:33%;"> <p><strong>主屏小组件</strong>:桌面上直接看 30 天内即将到期的 N 项,不用点进 App。对我来说这就是这个 App 存在的意义——<strong>你手机锁屏那一眼就能看到的东西,你不会忘记</strong>。</p> <img src="https://blog.laoda.de/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fimg.laoda.de%2Fi%2F2026%2F04%2F24%2F201403-1.webp&amp;size=m" alt="04-lockscreen.png" style="zoom: 33%;"> <p><strong>锁屏小组件</strong>:只显示下一笔——不用解锁、不用打开 App、锁屏状态下直接看到名字和剩余天数。这个我真的挺喜欢。</p> <p>整个视觉语言贯穿的还是那个「<strong>非打扰式</strong>」的产品哲学——数字不标红、不用感叹号、不搞「紧急!」这种暴力美学,就是温柔地提醒你一句。</p> <h2 id="4-天-18-个-commit">4 天 18 个 commit</h2> <p>我之前没写过 Swift,一行都没有。</p> <p>Xcode也没装过。</p> <p>SwiftUI、SwiftData、WidgetKit(分别是苹果用来做 iOS 界面、存本地数据、做桌面/锁屏小组件的三套框架)是啥,全是边做边查。但不妨碍我 4 天写完、上架、提审。</p> <table> <thead> <tr> <th>日期</th> <th>Commits</th> <th>主要内容</th> </tr> </thead> <tbody> <tr> <td>04-21(周二)</td> <td>7</td> <td>初版 + 改名 + 图标 + 隐私声明 + 截图策略</td> </tr> <tr> <td>04-22(周三)</td> <td>4</td> <td>通知 P0 三连击 + 真机验证 + 修复上线</td> </tr> <tr> <td>04-23(周四)</td> <td>3</td> <td>多币种实时汇率(opt-in)</td> </tr> <tr> <td>04-24(周五)</td> <td>4</td> <td>Bundle ID 改名 + ASC 元数据 + 提交审核</td> </tr> </tbody> </table> <p>我大部分时间在干嘛?<strong>打字、截图、点按钮</strong>。写需求、看效果、给 Claude 反馈「这里不对、那里要改、审美再收一点」。真正「坐键盘前敲 Swift 语法」的时间——几乎没有。</p> <p>但这不是说 Claude Code 是魔法。它不是。它会在一个看起来简单的地方疯狂跑偏,能爽到停不下来,也能气到想砸键盘。</p> <p>下面是翻车现场合集。</p> <h2 id="翻车现场-1-改名三连跳-放了自己一鸽子-">翻车现场 1:改名三连跳(放了自己一鸽子)</h2> <p>App 一开始没专门起名字,我自用编译好之后,手机里最早就显示叫「续费提醒」。土是土了点,但直白。</p> <p>第二天我觉得这名字太像 90 年代共享软件,改叫 <strong>Renewly</strong>。英文名、现代感、听起来像个 SaaS。</p> <p><strong>然后我犯了一个很愚蠢的错误——没有第一时间去 App Store 查重名</strong>。名字定了、图标调了、本地化文件改了、隐私政策的 title 也换了——一直到我准备提交审核之前,才突然想起来去搜一下。<strong>被人占了</strong>。有一个已经在跑的订阅工具 app 也叫 Renewly,功能还不完全重叠——这种重名是 Apple 审核环节大概率会让你改的那种。</p> <p>这个锅就是没经验导致的。作为一个日常就在 App Store 搜 app、自己也跑一堆自托管工具的人,「<strong>发布前先查名字</strong>」这种事不用别人教。我偏偏就忘了——<strong>咕咕本咕,放了自己一鸽子</strong>。</p> <p>只能第三次改名。这次更彻底:英文叫 <strong>DueSub</strong>(Due + Subscription 的缝合),中文叫 <strong>续然</strong>(续是续费,然是自然、果然)。</p> <p>踩完坑的经验——<strong>名字想好之后第一件事:去 App Store 和 Google Play 都搜一遍</strong>,再去 Namecheap 看看 .com 在不在(最好但不是必须),都空着再去设计图标、改代码、改本地化。我这次反过来做,付了 3 次改名的工时。</p> <h2 id="翻车现场-2-通知的三个坑-同一天翻了三次-">翻车现场 2:通知的三个坑(同一天翻了三次)</h2> <p>这个 App 最核心的功能是<strong>到期提醒</strong>——你订的 VPS 明天扣费,我提前 7 天、3 天、1 天推送,当天上午 9 点再补一刀。这个功能不对,整个 App 就是一个「带搜索框的表格」。</p> <p>结果 04-22 那天我翻了三次车,一天修了三个 BUG。</p> <p><strong>坑一:九点以后打开 App,当天的提醒丢了。</strong> <br> 调度逻辑是每天早上 9 点对「当天到期的项」推送一次。自测 OK。结果某天下午我打开 App,发现<strong>明明有一项今天到期,却没推送</strong>。根因是 App 长时间没启动,调度队列里根本没这条通知,系统自然不会补发。修法是 App 启动时 fallback 检查一次——当天有到期项、现在已经过 9 点、用户今天还没收到,就立刻补推。</p> <p><strong>坑二:自动续费的文案跟代码对不上。</strong> <br> 设置页之前有个「自动续费项默认不提醒」的开关,文案写「你已经设置自动扣款了,不需要再提醒」。听起来很合理——<strong>但代码不是这么做的</strong>。代码里自动续费项依然推 7/3/1 天的提醒,只有当天 9 点不推。这种「你说的是 A,代码做的是 B」最阴——不 crash、不报错、但就是跟用户说好的不一样。改了文案——提前几天提一下不是坏事,是自动扣那一刻再来一下才烦。</p> <p><strong>坑三:App 开着的时候,通知横幅被系统吞了。</strong> <br> iOS 的默认行为——App 在前台时,系统不弹横幅。理由是 App 自己处理。这对聊天软件合理,对一个<strong>到期提醒 App</strong> 就是「你盯着屏幕的时候我就不提醒你」——有点傻缺。我真机上复现:早上 9 点 App 开着,该推的一声没响。我当时盯着屏幕看了 10 分钟,以为自己又写炸了。修法其实很简单——一行代码的事,让 App 主动告诉系统「前台也请弹」。</p> <p>三个坑修完、真机回归测过,这一天我感受到了**「软件这事儿怎么每个角落都能翻车」**的经典真理。Claude 写代码很快,但它不会替你想清楚「用户下午 3 点才打开 App 会发生什么」——你得自己变成产品经理兼测试,一个个角落去踩。</p> <h2 id="翻车现场-3-Bundle-ID-的最后一公里">翻车现场 3:Bundle ID 的最后一公里</h2> <p>全程最让我心里一凉的一次。</p> <p>Apple 的 Bundle ID(可以理解成 App 的全球唯一身份证号)一旦在 App Store Connect 注册并提交过 build(也就是打包上传过一次安装文件),<strong>就不能改了</strong>——要改只能注册新 App ID、开新 App 记录,之前填的元数据、截图、审核资料<strong>全部作废从头来</strong>。</p> <p>我最早的 Bundle ID 前缀用了我日常 handle,但开发者账号实名信息是另一个名字。两边对不上,Apple 校验过不去。</p> <p>只能改 Bundle ID。改它意味着:</p> <ul> <li>Xcode 里所有 target 的 Bundle ID</li> <li>主 App 和小组件共享数据用的 <strong>App Group</strong></li> <li>证书和描述文件要重签</li> <li>测试机上装过的旧版全部失效,要重装</li> </ul> <p>最阴的是 App Group(让主 App 和桌面小组件共享数据的一个"命名空间",Apple 规定这个名字全球唯一)。我旧的 group 名改新的时候报冲突,<strong>说这个名字有人用过</strong>(其实是我自己,几分钟前注册的、没删)。加了个 <code>.shared</code> 后缀绕过——<strong>这个本来不应该存在的后缀永远留在我代码里了</strong>,作为一个「我和 Apple 的命名空间规则打过架」的纪念。</p> <p>修完、重签、重装、回归——折腾好一会儿才终于走到 App Store Connect 那一步。</p> <h2 id="翻车现场-4-App-Store-Connect-是一个-checkbox-地狱">翻车现场 4:App Store Connect 是一个 checkbox 地狱</h2> <p>到这一步我才意识到:<strong>写 App 只是上架这件事的冰山一角</strong>。真正的灾难在 ASC。</p> <p>让我列一下这次填过的表(不完整,凭记忆):</p> <ul> <li>App 名称 / 副标题 / 推广文本 / 描述 / 关键词(<strong>英文中文两套</strong>)</li> <li>主类别 / 副类别 / 年龄评级(一份 20 多题的问卷,问我「本 App 是否包含恐怖元素」——我做的是订阅提醒)</li> <li>版本号 / 构建号 / 上架日期</li> <li>隐私政策 URL(<strong>每种语言一份</strong>,英文一个、中文一个)</li> <li>Marketing URL / Support URL</li> <li><strong>12 张截图</strong>(6.9" iPhone,iPad 跳过只做 iPhone)</li> <li>App Icon 1024×1024</li> <li><strong>App 隐私标签</strong>(和代码里的 <code>PrivacyInfo.xcprivacy</code> 是两份<strong>独立的表</strong>,要自己保证内容一致)</li> <li>内容版权声明(不是 App 的版权,是 App 里用没用别人的素材)</li> <li>出口合规性(加密算法使用)</li> <li>IDFA 使用声明</li> <li><strong>DSA 欧盟合规声明</strong>(新增的,个人开发者选 Non-Trader 才不会公开家庭住址)</li> <li>App 审核联系方式 + 演示账户 + 审核备注</li> <li>定价 + 可用国家地区</li> <li>Mac / Vision Pro 兼容性(默认勾,记得关)</li> <li>发布方式(自动 / 手动)</li> </ul> <p>整理这份清单用了一下午。一个勾一个勾过,途中踩的坑:</p> <ul> <li>勾了「所有国家」才发现默认包括中国大陆——国区要备案我没备,赶紧去掉</li> <li>英文的隐私政策 URL 填完以为就行——ASC 跳出红 banner:<strong>简体中文 - 隐私政策网址 - 此栏为必填项</strong>。每种语言要独立一个 URL</li> <li>以为提交时会弹「出口合规 / IDFA / 内容版权」三连问——这次居然没弹。后来才知道如果你在 Info.plist / xcprivacy / App 信息页都提前答过了,ASC 不再来烦你</li> <li>备注里写了 Settings 里有个 "Seed Sample Data" 按钮——结果代码里英文按钮实际叫 "Fill sample data"。<strong>自己的 App,按钮名都记错</strong>。这给 Apple reviewer 看到,他大概会觉得我连自己 app 都没摸熟</li> </ul> <p>最后所有字段绿勾、红 banner 消失、「提交以供审核」按钮可点。深呼吸,点下去。</p> <p>然后画面一片祥和。<strong>没有弹窗、没有「你确定吗」、没有任何仪式感</strong>——左侧栏那个黄色小点变成「正在等待审核」,就这样。</p> <h2 id="吐槽一下-上架-App-这个流程-真的太复杂了">吐槽一下:上架 App 这个流程,真的太复杂了</h2> <p>我要认真吐一下槽。</p> <p>Apple 的 App Store 是 iOS 生态的核心支柱,<strong>用户体验</strong>堪称行业标杆。但它对<strong>开发者</strong>的体验,是另一个世界。</p> <p>作为一个独立开发者、副业开发者、个人开发者,你要面对:</p> <ol> <li><strong>¥688 / 年的开发者费</strong>($99)。App 一分钱不赚,每年也得续。这个我认——毕竟用了整套工具链和分发网络。但作为一个常年买小鸡的人,每年还是要叹一声——<strong>这笔钱够我买一台好的服务器了</strong>。</li> <li><strong>证书体系</strong>。App ID / Bundle ID / Provisioning Profile / Distribution Certificate / Push Certificate……随便一个过期,线上炸。</li> <li><strong>ASC 的 30 多个字段 + 一堆合规声明</strong>。DSA、隐私标签、xcprivacy、内容版权、出口合规、IDFA、年龄问卷、类别、定价、可用区。</li> <li><strong>审核的黑盒</strong>。什么时候过、为什么过、拒了怎么改,全靠邮件 + Resolution Center。</li> <li><strong>多语言隔离填写</strong>。每个 locale 要独立填描述、关键词、URL——漏一个返工重来。</li> </ol> <p>这套流程对成熟团队(有专门的 release manager 和 compliance)是日常,对一个摸着石头过河的独立开发者——<strong>「完成率劝退」比代码本身高 10 倍</strong>。我完全理解为什么那么多做了一半的 iOS App 最后没上架——不是代码没写完,是<strong>上架的活儿没人愿意干</strong>。</p> <p>我这次能走完,95%的功劳给 Claude——不是它写代码快,是它陪我<strong>把这 30 多个 checkbox 一个一个过完</strong>。如果让我自己看 Apple 文档去填,我多半中途就放弃了。</p> <h2 id="顺便说一下国区-其实我想上的-">顺便说一下国区(其实我想上的)</h2> <p>这次上架,我<strong>主动跳过了中国大陆区</strong>——但这不是我本意。</p> <p>我本来是想上国区的。不是说看重国区下载量,就是觉得一个中文作者做的中文工具,中文应用商店里没它,多少有点别扭。</p> <p>备案这事我是知道的。早几年折腾服务器域名的时候,我就顺手买了一个十年的 <code>.cn</code> 域名挂在腾讯云、老老实实走完了 ICP 备案;当时配套也买过一台大陆服务器——但国内服务器的体验真的让人一言难尽,<strong>带宽小得可怜还卖得贵</strong>,到期我就没续费了。反正我要跑的服务都在境外,非要在大陆放一台机器没必要。</p> <p>这次准备上架国区,我盘算了一下以为挺简单——<code>.cn</code> 域名还在、备案号还在、腾讯云账号还在、实名信息跟 Apple 开发者账号对得上。填个备案号就完事了吧?</p> <p><strong>结果一查流程才发现:光域名备案不够,备案得绑定在一台「正在使用中」的境内服务器上</strong>。我那台大陆机子早停了,备案跟着就失效了——要恢复,<strong>得再租一台国内服务器</strong>。</p> <p>这就有意思了——<strong>我这个 App 完全离线、不联网、不部署任何东西</strong>。我不需要服务器,没有任何后端服务要跑,也没有任何 API 要托管。仅仅因为「要在国区上架」这个理由,我被强制要求去租一台<strong>我完全用不上的大陆服务器</strong>。</p> <p><strong>这一点我是真的不太理解</strong>。规则的本意大概是想给在国区上架的 App 留一个可追溯的境内主体,但对一个纯离线工具来说,服务器这条要求完全是空转——我没有后端、没有数据回传、没有任何需要境内托管的东西,却要为此每年多掏一份云厂商的钱。</p> <p>算一下数:国内一台够用的轻量应用服务器(能用于备案的最低配起),<strong>一年 400 块起步</strong>;加上 Apple 开发者 ¥688;加上偶尔维护备案的小成本——对一个<strong>不收费的免费 App</strong>,<strong>每年多花 1000 多块为爱发电,实在不太合理</strong>。</p> <p>所以这次我选了 <code>All Countries except China mainland</code>——174 个区全部开放,国区先空着。等哪天要做付费功能,有盈利模型能把这 1000 多块年成本覆盖掉,再回头补上国区不迟。</p> <p>如果你也是个人开发者、做的是免费离线工具——<strong>国区这件事可以先放一放</strong>。把其他 174 个区跑通、把用户反馈收起来、把付费模型验证出来,再回头说国内的事。</p> <h2 id="Claude-Code-作为-iOS-IDE-的体验">Claude Code 作为 iOS IDE 的体验</h2> <p>回到这次最初的目标之一——「<strong>摸一下能力边界</strong>」。</p> <p>我不懂 Swift,不会 SwiftUI 的声明式语法,SwiftData 的 <code>@Model</code> 到现在也讲不清原理。但一点都不慌——因为<strong>我不需要懂,我只需要知道我要什么</strong>。</p> <ul> <li>「加一个设置项,让用户切换显示货币」——Claude 去加</li> <li>「这个列表行高再紧一点」——Claude 去改</li> <li>「真机跑一下,别只编译通过」——Claude 真的会去 build、装真机、抓日志</li> <li>「这个通知为啥没弹,你查一下」——Claude 读上下文、看 delegate、写假设、去验证</li> </ul> <p>我的角色从「写代码」变成了「<strong>定需求 + 审效果 + 做判断</strong>」。前者要懂语法,后者要懂用户。</p> <p>爽的地方:<strong>你不再因为「不会 Swift」而放弃 iOS App 这个想法</strong>。</p> <p>不爽的地方:<strong>它跑偏的时候你得看得出来</strong>——代码能力不是凭空被替代的,你能让 Claude 写代码,但你必须能看出它写的是不是你要的。</p> <p>这次 4 天我踩到的<strong>能力边界</strong>大致在:</p> <ul> <li><strong>需要你当产品经理</strong>。Claude 不会主动想「用户下午 3 点打开会怎样」这种边界 case</li> <li><strong>需要你当测试</strong>。它说「已修复」不等于修了,必须你跑一遍真机复现</li> <li><strong>证书 / 签名 / 打包</strong>的坑它不一定记得住,要你盯着它去查 Apple 最新文档</li> <li><strong>ASC 的字段含义</strong>它有时候会猜错,你得自己去对官方说明</li> </ul> <p>其他的——写代码、读 API、调 debug、查栈、写测试——它比我快得多得多。</p> <h2 id="成本账">成本账</h2> <p>粗略算一下这个 App 到目前为止的总投入:</p> <table> <thead> <tr> <th>项</th> <th>成本</th> </tr> </thead> <tbody> <tr> <td>Apple 开发者账号</td> <td>¥688 / 年</td> </tr> <tr> <td>Claude Code 订阅</td> <td>已订的 Max 档,这次没多花</td> </tr> <tr> <td>域名 + 托管</td> <td>¥0(Landing 和隐私政策挂 GitHub Pages)</td> </tr> <tr> <td>服务器</td> <td>¥0(没备案,不需要)</td> </tr> <tr> <td>图标</td> <td>¥0(Claude + SF Symbols + 手调)</td> </tr> <tr> <td>截图</td> <td>¥0(模拟器截图 + 简单标题文字)</td> </tr> <tr> <td><strong>总计</strong></td> <td><strong>¥688</strong></td> </tr> </tbody> </table> <p>跳过国区之后,全部成本就剩一张 Apple 开发者年费。对一个本来一行 Swift 都没写过的人——<strong>这个投入产出比,放在 Claude Code 出现之前是完全不可想象的</strong>。</p> <h2 id="写在最后">写在最后</h2> <p>这不是一篇「你也能 4 天做 iOS App」的鸡血文。你不能,我也不能——如果换一个更复杂的 App(联网、账号、支付、音视频、AI 推理),这个周期会翻好几倍。</p> <p>但有一件事值得说——<strong>「我不会写代码」,不再是做东西的理由了</strong>。</p> <p>过去十年,「有想法但不会写代码」是劝退无数人的天花板。想做的东西卡在「找不到技术合伙人 / 招不起开发」这一步上。今天这个天花板被 Claude Code 这类工具<strong>狠狠砸了一个洞</strong>。</p> <p>不是洞下面一切都容易,iOS 上架流程该复杂还是复杂,国区备案的门槛该离谱还是离谱。但<strong>写代码本身</strong>这件事,从「不可能」,变成了「<strong>要不要动手</strong>」。</p> <h3 id="当开发成本降到接近于零-真正稀缺的是什么">当开发成本降到接近于零,真正稀缺的是什么</h3> <p>这次最让我想明白的一件事——<strong>开发成本的下降,反过来把产品问题推到了台前</strong>。</p> <p>过去「找到真实需求 + 招到靠谱开发 + 写出能用的代码」三件事捆在一起,能搞定其中任何一个都算稀罕。现在 Claude Code 把第二、第三件事的门槛降到了个人能承受的范围——那第一件事——<strong>你到底在解决一个真问题,还是在自嗨</strong>——就变成了唯一卡住你的那个门槛。</p> <p>我这个 App 是我自己的小真实痛点。规模不大,也不打算做成生意。但在做的过程里我越来越相信——<strong>在今天,独立开发者是真的能吃上饭的</strong>。这两年海外 indie dev 圈里,赚到钱的人一抓一大把,有年收入几十万美金的,也有做个 $10/月小工具把生活费搞出来的。核心都不是他们代码写得多牛——而是<strong>他们找到了一小群真实付费用户的真实需求</strong>。</p> <p>代码这条护城河正在被填掉,<strong>用户洞察</strong>成了新的护城河。对不少想副业糊口、或者想看能不能全职独立的人,这可能是十年难遇的一个窗口期——代码能力第一次不再是硬卡点,但同时,<strong>你对用户的理解</strong>的重要性被放大到前所未有。</p> <h3 id="下一个坑">下一个坑</h3> <p>这次 DueSub 是免费 App,没盈利模型——它本来就是给我自己用的,顺手做成了产品。下一个我想挖的坑,是<strong>做一个带付费功能的 App</strong>:走一遍订阅 / 一次性内购的完整流程,去碰 StoreKit、走税务合规、看看 Apple 30% 抽水到底怎么切、体验一下从 0 到第一个付费用户的全过程。</p> <p>具体做啥还没定——<strong>先把用户洞察这一步想清楚再动手</strong>。这次改名三连已经告诉我了,想清楚再动手,比手快多跑一版省事得多。</p> <h3 id="最后">最后</h3> <p>回到博客侧边栏那四个字——<strong>learn or earn</strong>。</p> <p>这回 learn 到了 iOS 一整套流程、Claude 能力边界、App Store 审核的全部坑;earn 谈不上,免费 App 没盈利模型。但<strong>摸清一条从 0 到 1 的路径</strong>本身,就是这次最大的 earn——下一次就知道怎么走第二步了。</p> <p>App 叫 <strong>DueSub / 续然</strong>,现在在审核队列里,预计 24–48 小时后上架<strong>非国区</strong> App Store。免费、离线、不要账号——如果你也跟我一样囤着一堆 VPS 和域名、又老忘开邮箱——装一个试试。</p> <p>国区的事儿,以后再说吧。</p> <p>咕咕下一篇接着挖坑。</p> <h2 id="后记---发博客之前-App-先过审了">后记 · 发博客之前 App 先过审了</h2> <p>正文写到上面那段「在审核队列里,预计 24-48 小时」的时候,App 确实还在审核。结果博客还没来得及排版发出,苹果那边先完事了。</p> <p>这篇博客发出来的时候,App 就已经在<strong>除中国大陆外</strong>的各区 App Store 可搜可下了:</p> <p><strong><a href="https://apps.apple.com/app/duesub-subscription-tracker/id6763448383">DueSub: Subscription Tracker</a></strong> —— 免费、离线、无需登录。</p>

2026/4/24
阅读更多

3 天, 61 个版本|非程序员的我用 Claude Code 接棒维护了自己的博客主题

<img src="https://blog.laoda.de/plugins/feed/assets/telemetry.gif?title=3%20%E5%A4%A9%2C%2061%20%E4%B8%AA%E7%89%88%E6%9C%AC%EF%BD%9C%E9%9D%9E%E7%A8%8B%E5%BA%8F%E5%91%98%E7%9A%84%E6%88%91%E7%94%A8%20Claude%20Code%20%E6%8E%A5%E6%A3%92%E7%BB%B4%E6%8A%A4%E4%BA%86%E8%87%AA%E5%B7%B1%E7%9A%84%E5%8D%9A%E5%AE%A2%E4%B8%BB%E9%A2%98&amp;url=/archives/claude-code-halo-theme" width="1" height="1" alt="" style="opacity:0;"> <p>前几天一个周末,我把自己博客用的主题 fork 了一份,花了 3 天时间,发了 61 个版本,解决了一堆多年来一直想改但没改的小问题,顺便踩了 N 个大坑。</p> <p>这篇就把这段 "非程序员 × Claude Code" 的 vibe coding 实录和大家分享一下。也顺便聊聊用下来对几家 AI 订阅的真实看法,还有一点关于 "软件门槛" 和 "肉身能力" 的胡思乱想。</p> <p>内容会有点长,但都是真金白银踩出来的体感。</p> <h2 id="起点--博客主题好久没更新了">起点——博客主题好久没更新了</h2> <p>咕咕这个博客用的是 <a href="https://www.halo.run/">Halo</a> 平台 + <a href="https://github.com/Jiewenhuang/halo-theme-joe3">Joe3</a> 主题,作者是 <a href="https://github.com/Jiewenhuang">Jiewenhuang</a>。这个主题其实非常好用,做得也很漂亮,我前前后后用了好几年,一直挺满意的。</p> <p>但最近一两年,作者因为工作原因更新慢了下来。这完全可以理解——开源主题作者本来就是用爱发电,有自己的主业和生活,能一直维护已经很难得。但对用户来说就是:</p> <ul> <li>有几个小 bug 长期挂着没修</li> <li>有几个想要的小功能提了 issue,但一直排队</li> <li>深色模式有些细节抠得不够到位</li> <li>性能上有些可以优化但还没做</li> </ul> <p>这些东西我其实都想自己动手改,但之前一直有个拦路虎——<strong>我不是程序员</strong>,平时也就是会点 VPS 折腾、写写 Docker 命令,真要去啃主题源码,心里还是怯的。</p> <p>那为啥现在突然敢动手了?因为一个东西:<strong>Claude Code</strong>。</p> <h2 id="从伸手党到自己动手--为什么决定-fork">从伸手党到自己动手——为什么决定 fork</h2> <p>我在openclaw出来不久的时候,一开始订阅了kimi的coding plan,后来觉得不够好用,听网上一堆人说GPT和Claude好用,就尝试订阅了 Claude Pro(每月 20 美元那种),偶尔用来问问问题、写写文案,确实不错。但一直没用它写代码——总觉得太复杂了。</p> <p>直到有一次,我随口让 Claude 帮忙看了下主题里一段 CSS,想改个小边距。它不光改对了,还顺带解释了为什么这么改,甚至告诉我 "你这个改动会影响移动端,建议加个 media query"。</p> <p>那一瞬间我突然意识到:<strong>原来这事儿是可以自己干的。</strong></p> <p>不是说我一夜之间学会了前端,而是——有 Claude 在,你只要能描述清楚你要什么、看得懂 git diff、会复制报错,剩下的它都能兜住。这个门槛突然低到一个非程序员也能跨过去的程度。</p> <p>然后想法就顺理成章了:</p> <ul> <li>要等主题原作者更新?大概率很难</li> <li>要发 PR?很多功能其实只有我自己需要(比如一些自定义侧边栏、适配自己阅读习惯的字号行高),发上去作者不一定合,也不应该合</li> <li><strong>那 fork 一份自己维护就好了</strong></li> </ul> <p>打定主意,Max 套餐开了(后面会细聊为啥咬牙从 Pro 升 Max),本地 Halo 环境跑了起来,VS Code + Claude Code 都准备好了。</p> <p>然后故事就开始了。</p> <h2 id="3-天-61-个版本--一发不可收拾-根本停不下来">3 天 61 个版本——一发不可收拾,根本停不下来</h2> <p>一开始我没想到会搞这么快。本来预期是"一周弄个能用的版本",结果 Claude Code 的节奏推着我往前走,一发不可收拾,根本停不下来。</p> <p><img src="https://blog.laoda.de/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fimg.laoda.de%2Fi%2F2026%2F04%2F22%2F170913-1.webp&amp;size=m" alt="2e0532b448b0787187335d641374daac.png"></p> <p>这是真实的 git log 统计:</p> <table> <thead> <tr> <th>日期</th> <th>星期</th> <th>发了多少版</th> <th>主要做了啥</th> </tr> </thead> <tbody> <tr> <td>2026-04-19</td> <td>周日</td> <td>5 版</td> <td>热身 · SEO / 初版性能优化</td> </tr> <tr> <td>2026-04-20</td> <td>周一</td> <td><strong>44 版</strong></td> <td>暴风骤雨日 · 翻车最多的一天</td> </tr> <tr> <td>2026-04-21</td> <td>周二</td> <td>12 版</td> <td>安全补丁 hotfix 系列收官</td> </tr> </tbody> </table> <p><strong>3 天, 61 个 tag。</strong> 作为对比,原作者过去两年发了大概 30 个版本——Claude Code 这个节奏,直接比原作者快 <strong>15 倍</strong>(当然其实很多小改动完全可以合并起来一次发版,不知道是Claude还是我给的skill的原因,这边图快就直接一个改动一个 tag 推了,虚胖成分有一些)。</p> <p>当然这不是在炫耀速度。这个速度之所以能跑起来,是因为:</p> <ul> <li>小需求说清楚 → Claude 几分钟就能改完 + 解释</li> <li>本地跑一下 → 有问题直接贴报错给它</li> <li>没问题 → git commit + tag + push</li> <li>GitHub Actions 自动打 release zip → Halo Console 一键升级</li> </ul> <p>整个反馈循环被压缩到 <strong>10 分钟以内</strong>。人只要负责 "想清楚要啥 + 看一眼做对了没"。</p> <p>仓库在这里,感兴趣的同学可以看下完整的 commit history:</p> <p>👉 <a href="https://github.com/Lau0x/halo-theme-joe-next">github.com/Lau0x/halo-theme-joe-next</a></p> <h2 id="翻车现场合集--61-版里至少一半是在修前一版">翻车现场合集——61 版里至少一半是在修前一版</h2> <p>光说 "3 天 61 版" 好像挺厉害,但真相是:<strong>其中至少一半,都是在修前一个版本的坑</strong>。</p> <p>下面这几个翻车故事,是这 3 天里最有代表性的。不展开技术细节,就聊故事感——让大家感受下 AI 写代码到底是啥体验。</p> <h3 id="翻车现场-1---3---1---31--的谜题">翻车现场 1——"3 + 1 = 31" 的谜题</h3> <p>想加一个 "相关推荐" 小卡片,在每篇文章末尾展示 3 篇同分类的文章。</p> <p>Claude 几分钟写完,跑起来一看——<strong>页面上出现了 30 张卡</strong>。多了整整 27 张。</p> <p>让它修,它改了一版,还是 30 张。再改一版,还是 30 张。<strong>连续 10 个候选版本 (rc.01 → rc.10) 都没修对。</strong></p> <p>最后逼它把变量打出来看,才发现是个非常隐蔽的类型问题:后台的 "3" 不是数字 3,是字符串 "3"。字符串 "3" + 1 在某些语言里等于 "31",然后程序就真的去拉了 31 条文章。</p> <p>一个让人哭笑不得的 bug。但也正因为它这么反直觉,Claude 自己怎么改都绕着走。</p> <p><strong>教训</strong>:AI 连续两三版修不对同一个问题的时候,<strong>不要再让它瞎猜了</strong>,直接让它打印真实的变量值。否则就是纯纯在 token 上烧钱。</p> <h3 id="翻车现场-2--给-jQuery-加了个小优化--整站炸了">翻车现场 2——给 jQuery 加了个小优化, 整站炸了</h3> <p>Claude 看了下主题代码,说 "jQuery 这样加载影响性能,给它加个属性可以异步加载,更快"。</p> <p>听着有道理,就让它改了。</p> <p><strong>改完推上去,整个博客都打不开了。</strong></p> <p>翻车原因说起来也合理:jQuery 延迟加载之后,页面里好几十处其他脚本依赖它的地方跑得太早,找不到 jQuery,整站一片红。</p> <p>最后直接把改动撤了,恢复原状。这个 "优化" 看上去简单,但真要做对,得把主题里几十处相关脚本一起调整,不是一下午能搞完的活。</p> <p><strong>教训</strong>:<strong>性能优化类的改动,生产验证前别打 tag</strong>。开个浏览器点一遍主要功能再发版。省事儿的 10 分钟,能省下一晚上查事故。</p> <h3 id="翻车现场-3--四版-UI-改造--全被自己否了">翻车现场 3——四版 UI 改造, 全被自己否了</h3> <p>这个可能是最戏剧性的一段。</p> <p>我某天看动态页觉得不够好看,跟 Claude 说 "美化一下,搞得稍微有设计感一些"。</p> <p>Claude 很兴致地给了个方案,发了 rc.01。我一看:<strong>不好看,页面上这个蓝色小圈圈是干啥的</strong>。</p> <p>Claude:好的,我马上收紧!发 rc.02,删了一些元素。</p> <p>我:<strong>完全不想要这套了</strong>。</p> <p>Claude:那换个设计语言!现代社交网络风格!rc.03。</p> <p>我:<strong>……这是好看了,但布局塌了,左边一列文字挤得一个字一行</strong>。</p> <p>Claude:我修!rc.04,布局修对了。</p> <p>我:<strong>还是没有原来的好看。算了,咱们回原版吧。</strong></p> <p><strong>四个版本全废,一键 <code>git revert</code> 回到上一个 stable。</strong></p> <p>这段翻车事后反思,根因不是 Claude 审美差——而是我一开始就没把"想要什么样"说清楚。它在自己的审美空间里闭环了四版,每版都是"它认为的美化",但和我脑子里那个隐约的画面完全对不上。</p> <p>所以后来我给自己加了条规矩:<strong>以后让 AI 做视觉类改动,开工前必须给它一个参考站 URL,或者几个情绪板关键词</strong>。不然就是四版起步的翻车。</p> <h3 id="翻车现场-4--修了个隐私漏洞--结果所有内容都不显示了">翻车现场 4——修了个隐私漏洞, 结果所有内容都不显示了</h3> <p>这个翻车比较严重。</p> <p>起因是一个真实的安全问题:后台某条动态设为 "私有" 之后,但<strong>管理员登录访问博客前台</strong>,私有动态居然还能看到内容。</p> <p>典型的主题层漏了一道过滤。</p> <p>我让 Claude 加过滤,它加了一行判断,推上去。</p> <p>我一看:<strong>所有动态都不显示了</strong>,包括本该显示的公开动态。</p> <p>再查才发现 Claude 用错了比较方式——字段的实际类型和它以为的不一样,导致判断永远失败,全部被过滤掉了。</p> <p>再改一版,修对了。再改一版,清理掉调试代码,收官。<strong>就为了修这一个 bug,一口气发了三个补丁版本。</strong></p> <p><strong>教训</strong>:AI 的判断看起来都挺合理,但<strong>一定要跑真实数据验证</strong>。不然它拍胸脯说"这下肯定对了",上生产之后可能就是另一个故事。</p> <h3 id="翻车现场-5--CSS-里的神秘竖条">翻车现场 5——CSS 里的神秘竖条</h3> <p>这个就是翻车 3 里那个 rc.03。上完新设计,打开一看——<strong>两列内容里,左边一列宽大约 100 像素, 右边一列 800 像素</strong>。左边那列里的文字被压得一个字一行竖着排,像被揉过一样。</p> <p>Claude 以为是自己写错了参数,改了好几次边距和间距,都没用。</p> <p>后来查出来是 CSS 里的一个经典陷阱——简单说就是 "平均分配宽度" 这个写法遇到了 "不能换行的超长内容",两边就不平均了。</p> <p>这个坑是前端圈公认的经典老坑,但如果不踩一次,你根本不知道会有这种问题。</p> <p><strong>这就是 vibe coding 的真实面貌——你不是在写代码, 你是在和各种古怪的坑玩。</strong></p> <h2 id="一个月付了--168-AI-订阅费--为啥最后都用-Claude">一个月付了 $168 AI 订阅费, 为啥最后都用 Claude</h2> <p>聊完翻车,顺带聊一下订阅选择。因为这一波下来,我对 "哪家 AI 最值得付费" 有了点新认识。</p> <h3 id="从-Pro-咬牙升到-Max">从 Pro 咬牙升到 Max</h3> <p>我一开始用的是 Claude Pro(每月 $20),觉得挺够用的,日常搞点东西没啥压力。</p> <p>但真要让 Claude Code 做 vibe coding 这种 "一天 20+ 版本" 的节奏,Pro 套餐<strong>很快就触顶</strong>——几个小时就把当日额度用完,然后就只能干等着刷新。</p> <p>咬着牙升了 Max(每月 $100)。升级那一刻说实话是有点肉疼的。</p> <p>但用了两天就想通了:</p> <blockquote> <p><strong>100 美元每月, 当学费其实很便宜。</strong></p> </blockquote> <p>你想想——如果我去找个独立开发者,把这 3 天干的活外包出去,主题 fork + 新 feature + 安全补丁 + SEO 优化 + 性能优化 + 几个 bug fix,怎么也得 <strong>500-1000 美元</strong>起步。Max 这 $100,算当月 "学费 + 外包费",比外包便宜,比自学快,最后东西还完全属于我自己。</p> <p>除此之外,我还可以用它来辅助我的工作(excel里也可以用Claude了)。</p> <p>顺便放张 Max 使用面板截图给大家感受一下强度:</p> <p><img src="https://blog.laoda.de/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fimg.laoda.de%2Fi%2F2026%2F04%2F22%2F170904-1.webp&amp;size=m" alt="340e03b65e8744760fe7d2f6e8edd13b.png"> <br> <img src="https://blog.laoda.de/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fimg.laoda.de%2Fi%2F2026%2F04%2F22%2F170854-1.webp&amp;size=m" alt="6ad4b8472387bc1f7b97e2af7a656856.png"></p> <p>一周用到 77%(All models),Current session 也跑了 17%。真用爆了。</p> <h3 id="月付--168-的-AI-订阅全家桶">月付 $168 的 AI 订阅全家桶</h3> <p>为了写这篇,我盘了下自己每月的 AI 订阅费:</p> <table> <thead> <tr> <th>工具</th> <th>月费</th> <th>主要用途</th> <th>实测体感</th> </tr> </thead> <tbody> <tr> <td><strong>Claude Max</strong></td> <td><strong>$100</strong></td> <td>Claude Code / 主力 vibe coding / 长对话</td> <td>⭐⭐⭐⭐⭐ 最稳最聪明,开发主力</td> </tr> <tr> <td>ChatGPT Plus</td> <td>$20</td> <td>OpenClaw / 日常问答 / GPT 5.4</td> <td>⭐⭐⭐⭐ 最近升级的 GPT 5.4 确实聪明了不少</td> </tr> <tr> <td>Gemini Pro</td> <td>$20</td> <td>偶尔对比答案,长文本场景</td> <td>⭐⭐⭐ 特定场景还行</td> </tr> <tr> <td>Kimi</td> <td>¥199 (≈$28)</td> <td>中文长文,OpenClaw 备用</td> <td>⭐⭐ 体感明显弱一个档次</td> </tr> </tbody> </table> <p><strong>合计:约 $168 / 月。</strong></p> <p>这个钱多不多?不少。但我就当学习AI交的学费了,之后看看能不能取消掉1-2个不常用的。</p> <h3 id="一条选型经验--选你能力范围内最好的模型">一条选型经验——选你能力范围内最好的模型</h3> <p>过去这段时间同一套 OpenClaw 工具链,我分别用 Kimi 2.5、GPT 5.4 试了一遍:</p> <ul> <li><strong>Kimi 2.5</strong>:总感觉差点意思,回答经常跑偏,得反复 prompt 才能拉回来</li> <li><strong>GPT 5.4</strong>:明显聪明,大部分需求一次过</li> </ul> <p>同一个任务,两家结果差异非常大。</p> <p>我得出的一条经验是:</p> <blockquote> <p><strong>选模型,在自己能力范围内选最好的,会用得更爽。</strong></p> </blockquote> <p>便宜 20% 但差 50%,算总账其实亏。因为<strong>差的模型浪费的不是钱, 是你自己的时间和耐心,甚至你还会怀疑这个东西真的好用吗?</strong>。就和你刚学琴买了一把300块的吉他,F和弦怎么也按不出来,你会怀疑自己我是不是真的学不会吉他。</p> <p>最后吐个槽。GPT 的 Codex(OpenAI 自己的命令行工具)我也试了,结果最大的体验是——老是弹这个:</p> <pre><code>⚠️ Agent failed before reply: OAuth token refresh failed for openai-codex: Failed to refresh OAuth token for openai-codex. Please try again or re-authenticate. </code></pre> <p>每几天就让你重新登录一次。体感很差。</p> <h2 id="最大的收获是-掌控感-">最大的收获是"掌控感"</h2> <p>聊完钱,聊聊情绪。</p> <p>回头看这 3 天、61 个版本、5 个典型翻车、一堆踩坑笔记,最大的收获<strong>不是技术层面的</strong>,而是——<strong>掌控感</strong>。</p> <h3 id="自己的博客--自己说了算">自己的博客, 自己说了算</h3> <p>这种感觉很难形容。</p> <p>以前看到博客某个样式不喜欢,只能<strong>忍</strong>。 <br> 想加个小功能,只能<strong>发 issue 等</strong>。 <br> 字号大小、行高、颜色有几个小细节不爽,只能<strong>告诉自己习惯就好</strong>。</p> <p><strong>现在?</strong></p> <p>想改就改。想删就删。想加啥加啥。不喜欢这个主题了——<strong>甚至可以让 Claude 帮我从头写一个</strong>。</p> <p>这种 "一切都在自己手里" 的感觉太爽了!</p> <h3 id="从博客到小工具--软件的门槛在降低">从博客到小工具, 软件的门槛在降低</h3> <p>而且这事不只是博客。</p> <p>我最近还在琢磨几个小想法——想做一个 iOS 小 APP,专门用来提醒自己 VPS 和各种订阅服务到期续费,免得哪天 "传家宝 VPS" 突然就没了(其实现在已经做差不多了);也想搞个 Chrome 插件,还没想好具体做啥,但感觉总能用上。</p> <p>这些以前想都不敢想。Swift 学起来多难啊,Xcode 折腾一整天,可能只出一个崩溃。</p> <p>但现在,描述清楚需求,Claude 自己写、自己跑、自己调、自己解释。<strong>一个非程序员,3 天能发 61 版博客主题,那做个小 APP 呢?可能也就是一个周末。</strong></p> <p>软件的门槛,正在以肉眼可见的速度降低。</p> <p>不是说程序员没用了——<strong>真正的工程问题还是需要专业的人</strong>。但对于 "我想给自己做个小东西" 这个诉求,门槛已经被 AI 摊平了。这是肉眼可见的变化。</p> <h2 id="写在最后--软件门槛降低了--下一道墙在哪-">写在最后——软件门槛降低了, 下一道墙在哪?</h2> <p>聊到这儿,我想说点更胡思乱想的东西。</p> <p><strong>如果软件门槛真的降低了, 那下一道墙在哪?</strong></p> <p>我最近一直在琢磨这个问题。想法不成熟,但大致是这样。</p> <p><strong>第一道墙:硬件。</strong></p> <p>代码可以让 AI 写,但服务器会坏、光纤会断、电池会衰减、螺丝会生锈。未来 3 年 5 年,能动手改硬件、能拆能焊、能找到小众元器件、能修能造的人,可能反而稀缺。毕竟 AI 目前还没长手。</p> <p><strong>当然,机器人也在路上。</strong></p> <p>特斯拉的 Optimus、波士顿动力的 Atlas、还有国内各种人形机器人——再过几年,它们大概率能帮你做一部分 "需要手" 的事:拧螺丝、搬东西、简单清洁、甚至可能插插线修修电路。</p> <p>但即便如此,我觉得还是有些事它们短期干不了:</p> <ul> <li><strong>判断当下最该做什么</strong>(机器人执行能力强,但"决定干啥"这事儿,还是人擅长)</li> <li><strong>精细手艺和经验</strong>(修老机器、拆解古董电子产品、手工打磨——这些靠的是感觉和长年积累的手感)</li> <li><strong>和人打交道的事</strong>(带朋友打麻将、哄孩子睡觉、陪父母聊天——这些不是机器人来"代做"就有意义的)</li> </ul> <p>所以硬件这道墙,会被机器人慢慢矮化,但不会马上塌。</p> <p><strong>第二道墙, 可能更有意思——肉身能力。</strong></p> <p>我越来越觉得,<strong>那些必须花时间训练才能掌握的能力——英语、西班牙语、游泳、骑车、做饭、长跑——反而变得越来越重要</strong>。</p> <p>为啥?因为软件可以让 AI 写,文案可以让 AI 编,图片可以让 AI 画,视频可以让 AI 生成。硬件未来让机器人干。</p> <p><strong>但你的身体不能让 AI 代练,也不能让机器人代练。</strong></p> <p>机器人再强,也没法替你去学一门新语言的肌肉记忆、没法替你游 1000 米感受水的阻力、没法替你骑车穿过一整座城市感受风。这些东西必须是你自己的时间、汗水、年轻换来的。</p> <p>当所有人都有 AI 之后,大家的 "软件产出" 会被拉到差不多的水平。再过几年当所有人都有机器人之后,生活里 "需要动手做的事" 也会被拉到差不多。</p> <p>那时候,真正拉开差距的,反而是那些<strong>最原始、最肉身、最不能外包</strong>的能力。</p> <p>所以如果你问我,现在手头有点闲时间,该干啥?</p> <p><strong>去学一门新语言。去游个泳。去学骑公路车。去学做红烧肉。</strong></p> <p>因为那些东西,将来可能比写代码还值钱。</p> <hr> <p>主题的仓库在这里:<a href="https://github.com/Lau0x/halo-theme-joe-next">github.com/Lau0x/halo-theme-joe-next</a>。</p> <p>如果你也用 Joe3 主题,欢迎去下载最新版 zip 试试看,也欢迎提 issue。我维护得动,就一直维护下去,维护不动了,至少留一份 fork 给后来人。</p> <p>OK,那差不多今天就聊这些,不知道有没有勾起大家 vibe coding 的兴趣,也欢迎大家评论区留下你的 AI 订阅搭配和踩坑经历~</p>

2026/4/22
阅读更多

回顾一下我买过的服务器们|VPS 收藏夹大盘点

<img src="https://blog.laoda.de/plugins/feed/assets/telemetry.gif?title=%E5%9B%9E%E9%A1%BE%E4%B8%80%E4%B8%8B%E6%88%91%E4%B9%B0%E8%BF%87%E7%9A%84%E6%9C%8D%E5%8A%A1%E5%99%A8%E4%BB%AC%EF%BD%9CVPS%20%E6%94%B6%E8%97%8F%E5%A4%B9%E5%A4%A7%E7%9B%98%E7%82%B9&amp;url=/archives/vps-review-2026" width="1" height="1" alt="" style="opacity:0;"> <p>大家好,好久没有和大家更新了。</p> <p>最近好像也没找到特别有意思的 Docker 项目,随着 Claude Code 和 OpenClaw 的火爆,其实玩 Docker 也变得容易了很多,感觉是不是都没啥必要出教程啥的了,直接让它们搞定。</p> <p>前俩天看到国内最早的云服务新浪云终止服务了。</p> <img src="https://blog.laoda.de/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fimg.laoda.de%2Fi%2F2026%2F04%2F15%2F174016-1.webp&amp;size=m" alt="82e8a5eba1874b6094bb053a8e7dc30e.png" style="zoom:50%;"> <p>今天就来做个回顾吧,回顾一下我买过的服务器们。</p> <h2 id="展示页面">展示页面</h2> <p>这个是目前咕咕持有的服务器的情况(不完全,有些比如新的VMRACK、还有腾讯云香港轻量的34元/月的没挂上去),很多都是老伙计了,可能现在买不太到。</p> <p>地址:<a href="https://status.laoda.de/">https://status.laoda.de/</a></p> <p><img src="https://blog.laoda.de/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fimg.laoda.de%2Fi%2F2026%2F04%2F15%2F174014-1.webp&amp;size=m" alt="bc2343d72516b285d6d2ff2f96aeb487.png"></p> <p>不知道大家看到这个页面的第一反应是什么,应该第一时间是看到了两台红色的已经阵亡的机器。(当然还有其他的阵亡的机器可能被我删了 = =)</p> <p>第一台是 <strong>Olink</strong>,Olink 当年最出名的是没记错的话是德国的 AS9929,36 刀/年的,还有一个是 GRE 隧道,给线路不好的服务器用的。</p> <p>老板运营一直很佛系,机器很稳,用的很省心。咕咕收过一台二手的,续费 36 刀/年的,后来自己又原价买了一台。</p> <p>去年好像迁移过一次机房到圣何塞,今年没想到就运营困难了,官宣了停止运营。挺可惜的(对了推广收益好像还没结算给我 = =)</p> <p><img src="https://blog.laoda.de/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fimg.laoda.de%2Fi%2F2026%2F04%2F15%2F174011-1.webp&amp;size=m" alt="bb7ae30fc1311648a75355f709009524.png"></p> <blockquote> <p><a href="https://blog.laoda.de/archives/olink">【VPS 测评】Olink 法兰克福三网 9929 测评</a></p> </blockquote> <p>第二个是搬瓦工的 <strong>The Chicken</strong>,这个其实是咕咕忘记续费了,导致阵亡。性价比其实还可以,但是和我其他的搬瓦工比起来不够高(否则我也不会忘记续费了哈哈)</p> <p>然后咕咕自己看到的是<strong>在线天数</strong>。</p> <p>毫无疑问,这个代表了稳定性。</p> <p>买 VPS,可能一开始是为了折腾,但是时间久了之后,尤其是跑了一些项目或者部署了一些网站之后,最在乎的,肯定是稳定性。</p> <p>虽然之前和大家反复强调不要相信任何一个服务器厂商,对于自己在意的数据一定要多备份。但是三天两头宕机的厂商,即使再便宜,我们也会被搞的很难受。</p> <h2 id="搬瓦工-Bandwagonhost--稳定省心">搬瓦工 Bandwagonhost——稳定省心</h2> <p><img src="https://blog.laoda.de/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fimg.laoda.de%2Fi%2F2026%2F04%2F15%2F174019-1.webp&amp;size=m" alt="1401f311d592c7d1f6af2d982d5db5d7.png"></p> <p>而搬瓦工在咕咕看来,就是稳定的代表。</p> <p>我刚接触 VPS 的时候,就已经早闻大名,搬瓦工到现在至少有 10 年历史了吧。我目前的博客用的反向代理就是 <strong>The Plan V2</strong> 的套餐,一年 100 多刀,可以切换十多个机房(不过我基本没切过,一直用的 DC09),这两三年时间下来,很稳定。</p> <p>还有其他的几台,也基本买了之后配置完就没怎么登录过。</p> <p><strong>双十一套餐</strong>的,一年才 29 刀,我跑了几个基础服务在上面,包括现在这个服务器网站就是跑这个上面的。</p> <p><strong>49 刀的套餐</strong>,曾经很多 mjj 溢价想要购买,咕咕这台是后面一次放货的时候抢的,500G 流量,和 The Plan V2 一样可以切换十多个机房,CN2 GIA 最便宜的套餐了,目前咕咕畅游互联网全靠它,转眼都快 3 年了。</p> <p>所以如果要推荐一个稳定好用的机器,咕咕第一选择也会考虑推荐搬瓦工,前俩天还用 Claude Code 辅助做了一个简单的 HTML,上面是瓦工的产品合集:<a href="https://bwg.laoda.de/">https://bwg.laoda.de/</a></p> <blockquote> <p><a href="https://blog.laoda.de/archives/vps-bandwagonhost">【VPS 选购分享】Bandwagonhost(搬瓦工)——曾经的王者,三网 G 口 GIA,贵族 VPS</a></p> </blockquote> <h2 id="斯巴达-Spartanhost--稳如老狗">斯巴达 Spartanhost——稳如老狗</h2> <p><img src="https://blog.laoda.de/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fimg.laoda.de%2Fi%2F2026%2F04%2F15%2F174012-1.webp&amp;size=m" alt="74d9ce604482798d7254542fa76527e6.png"></p> <p>另外值得一提的是斯巴达。</p> <p>这台也是黑五抢的,当时是 31 刀/年,AMD 处理器,第二年黑五的时候升级了配置,变成 62 刀/年,然后之前老被 DDoS,刷流量(咕咕曾经一个月买了 5T 的额外流量 = =),后来我狠心加钱买了升级防御包和年付流量包,目前好像也是 100 多刀/年,我主要搭了 NPM,用做反向代理。非常稳。</p> <p>老板就是客服,回复非常快,之前黑五 3、4 点熬夜抢鸡的场景历历在目,只是这俩年好像老板也佛系了,没怎么搞黑五活动。</p> <blockquote> <p><a href="https://blog.laoda.de/archives/spartanhost">【VPS 测评】斯巴达 AMD 黑五 31 刀测评</a></p> </blockquote> <h2 id="BuyVM--PT-党最爱">BuyVM——PT 党最爱</h2> <p><img src="https://blog.laoda.de/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fimg.laoda.de%2Fi%2F2026%2F04%2F15%2F174007-1.webp&amp;size=m" alt="2cd58a0a40a86f6007a41f6b5bbf0c57.png"></p> <p>BuyVM 之前最大的优势就是无限流量,而且便宜,VPS 只要 3 刀/月好像,加上你可以买存储块,256G、512G、1T 的都可以,所以是 PT 党们的最爱,尤其是卢森堡无视 DMCA。</p> <p>咕咕这个就是卢森堡 + 256G 的最丐版配置,不过前段时间好像换老板了,卢森堡的机器也迁移到了瑞士。咕咕 PT 退烧了,偶尔用家宽下点影视作品,这个机器已经好久没登录过了。</p> <blockquote> <p><a href="https://blog.laoda.de/archives/vps-buyvm">【VPS选购分享】BuyVM——下载神器!无限流量,无视版权投诉,经常缺货DMCA版权投诉/适合BT/PT下载/Netflix解锁</a></p> </blockquote> <h2 id="CloudCone--黑五神机">CloudCone——黑五神机</h2> <p><img src="https://blog.laoda.de/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fimg.laoda.de%2Fi%2F2026%2F04%2F15%2F174004-1.webp&amp;size=m" alt="f5e74dad419239b9d59728945742fedc.png"></p> <p>CloudCone 这台是黑五抢的机器,看看已经是 3 年前的了~</p> <p>1 核 2G 内存 60G 硬盘,重点是续费只要 <strong>10.44 刀/年</strong>,放着挂着看看就很香。(之前还有一个 7 刀的,出掉了,那台价格更便宜,但是配置不如这台)</p> <blockquote> <p><a href="https://blog.laoda.de/archives/vps-cloudcone">【VPS选购分享】Cloudcone—— 高性价比, 自研面板,客服退款干脆利落 廉价VPS</a></p> </blockquote> <h2 id="DMIT--瓦工上游">DMIT——瓦工上游</h2> <p><img src="https://blog.laoda.de/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fimg.laoda.de%2Fi%2F2026%2F04%2F15%2F174002-1.webp&amp;size=m" alt="bb1d53cc766ff9be65ac012321b78a20.png"></p> <p>DMIT 的两台,头脑发热跟风抢的,虽然说 DMIT 是搬瓦工的上游,但是我怎么觉得没搬瓦工稳呢。</p> <p>线路不错,他家的一个特色是默认是用密钥登录服务器的,第一次买的小伙伴可能有点晕晕的,不知道怎么连接服务器,我应该出过小白教程,安全性确实会高一些。</p> <h2 id="Hetzner--欧洲小钢炮">Hetzner——欧洲小钢炮</h2> <p><img src="https://blog.laoda.de/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fimg.laoda.de%2Fi%2F2026%2F04%2F15%2F174001-1.webp&amp;size=m" alt="bc3c7ec52ec6baa6a527e009fd5020f4.png"></p> <p>机器性能强,价格低,性价比非常高,而且很稳。咕咕目前的博客就部署在它们家美西的机器上,支持备份快照,防火墙也很好用,非常省心。</p> <p>他家的竞拍的独立服务器也非常受 PT 党的欢迎,曾经拥有过两台,后面因为价格和自己用不上那么高的性能而没有继续续费。</p> <p>竞拍地址:<a href="https://www.hetzner.com/sb">https://www.hetzner.com/sb</a></p> <p><img src="https://blog.laoda.de/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fimg.laoda.de%2Fi%2F2026%2F04%2F15%2F173958-1.webp&amp;size=m" alt="bfab4e4ff2192f46cd67f2311993eb6b.png"></p> <p>唯一遗憾是之前咕咕写的这篇文章 <a href="https://blog.laoda.de/archives/vps-hetzner">【VPS 选购分享】Hetzner——欧洲阿里云!性能小钢炮!非常适合跑业务,还有适合刷 PT 的神鸡!</a>,被一个热心的 PT 网友转载了,然后他是给大家介绍用来类似薅羊毛刷 PT 的(新用户可以用邀请链接注册可以获得 20 欧的试用金),他那篇文章火了,然后他也是好心好像用了他和我的邀请链接,最后导致咕咕的账户的链接推广功能被官方禁止了(认为通过我链接注册账户的都是低价值客户 = =)</p> <p>之前其实 Hetzner 注册挺难的,现在不知道会不会容易一些了。</p> <h2 id="HostHatch--大盘鸡">HostHatch——大盘鸡</h2> <p><img src="https://blog.laoda.de/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fimg.laoda.de%2Fi%2F2026%2F04%2F15%2F173957-1.webp&amp;size=m" alt="773d743f83833bbb366f3b1ae67ef19e.png"></p> <p>关键词:<strong>大盘鸡</strong>。</p> <p>咕咕这个是 3 年 65 刀的,1T 的硬盘,不多说,它家的机器基本也是 PT 党用来刷 PT 用的,我这台是 3 年付的黑五版本,他家稳定性一般,不过对 PT 来说还好,我也很久没登录过了。</p> <blockquote> <p><a href="https://blog.laoda.de/archives/vps-hosthatch">【VPS 选购分享】HostHatch——目前看过的最高性价比大盘鸡!40 刀/年 2T 硬盘!</a></p> </blockquote> <h2 id="Naranja-Tech--荷兰小钢炮">Naranja Tech——荷兰小钢炮</h2> <p><img src="https://blog.laoda.de/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fimg.laoda.de%2Fi%2F2026%2F04%2F15%2F173954-1.webp&amp;size=m" alt="fd0a9c9f466867dd03036457c62e044f.png"></p> <p>荷兰小钢炮,黑五买的,他家黑五基本都有活动,这台好像 18 欧/年,2G 够用,目前搭了几个简单的小服务,也是稳的一批~</p> <h2 id="Netcup--性价比高">Netcup——性价比高</h2> <p><img src="https://blog.laoda.de/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fimg.laoda.de%2Fi%2F2026%2F04%2F15%2F173948-1.webp&amp;size=m" alt="295957760bb8fb167ee7e191e6118f4d.png"></p> <p>和 Hetzner 一样,性价比非常高的商家,之前一直持有他家的 RS2000,基本是一个小杜甫的状态,无限流量,性能还牛逼,一个月十多欧,非常好用,目前由于经济原因,换了一台 VPS,很好用的机器。</p> <p>唯一需要提醒的是他家是有<strong>合约制</strong>的,不续费要提前告知,否则一样给你续费,不交钱给你寄律师函!</p> <h2 id="OVH--抗-DDoS">OVH——抗 DDoS</h2> <p><img src="https://blog.laoda.de/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fimg.laoda.de%2Fi%2F2026%2F04%2F15%2F174105-1.webp&amp;size=m" alt="ce4ec68eb66f462ab2a916a69af5de7b"></p> <p>法国的,最出名的是抗 DDoS,独立服务器比较有名,经常会有抽奖(买到的实际配置比官方标注的高,比如 2T 硬盘变成 4T,200M 带宽变成 1G 的),咕咕现在持有一台,每个月付费 16.99 欧,2×960 GB SSD SATA(抽奖开出来是 4×960 GB SSD SATA)的 KS-LE-2。</p> <p>他家基本不给大陆的小伙伴注册,咕咕当时还不是很严格,找的代理帮忙注册的账号。</p> <p>然后 OVH 和 Netcup 似乎你账号上没有订购东西,一段时间之后会删除账号,OVH 的话可以买个 OVH 后缀的域名挂着,一年就几欧。</p> <blockquote> <p><a href="https://blog.laoda.de/archives/ovh-sys-2-sat-32-waw">【VPS 选购经验分享】OVH——欧洲最大、世界第三的托管服务提供商,抗 DDOS(独立服务器测评及取消 RAID、挂载硬盘分享)</a></p> </blockquote> <h2 id="RackNerd--性价比之王">RackNerd——性价比之王</h2> <p><img src="https://blog.laoda.de/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fimg.laoda.de%2Fi%2F2026%2F04%2F15%2F174047-1.webp&amp;size=m" alt="ad0c716f46764589962a111db512ce15"></p> <p>性价比之王,最适合新人入坑的鸡,很多一年就 10 刀左右。够折腾,高配一些的价格也便宜。</p> <p>之前有整理过列表,看看能不能捡漏~</p> <p>地址:https://blog.laoda.de/vps</p> <blockquote> <p><a href="https://blog.laoda.de/archives/vps-racknerd">【VPS 选购分享】Racknerd——曾经的"四大金刚",如今的性价比之王</a></p> </blockquote> <h2 id="iON--曾经的神鸡">iON——曾经的神鸡</h2> <p><img src="https://blog.laoda.de/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fimg.laoda.de%2Fi%2F2026%2F04%2F15%2F173921-1.webp&amp;size=m" alt="3027a80f4a19e427c04505370cc36fad.png"></p> <p>咕咕这台是 2019 年的,续费 20.19 刀的款,联通优化线路,1T 的流量,还是<strong>单向流量</strong>!非常高性价比的鸡。</p> <p>当然最有性价比的是双十一的,同配置但是续费 11.11 刀的。咕咕曾经也有过,后来转手了。</p> <p>iON 现在老板似乎也换过人了,机器也稍微偏贵一些,但是品质可以。</p> <blockquote> <p><a href="https://blog.laoda.de/archives/vps-ion-cloud">【VPS 选购分享】iON —— 咕咕的神鸡!Krypt 旗下,专为国人优化的 VPS,适合电信用户!7×24 小时全天候人工服务,还是单向流量!</a></p> </blockquote> <h2 id="写在最后">写在最后</h2> <p>好了,差不多了。</p> <p>现在一个一个看过来,满满都是当年玩鸡的回忆。</p> <p>可惜现在工作生活琐事太多,玩鸡的时间越来越少了。</p> <p>列表上的这些鸡,除非是厂商跑路了或者是咕咕实在是入不敷出了,大概率会继续续费下去,也算是对过去日子的一些回忆。</p> <p>OK,那差不多今天就聊这些,不知道有没有勾起老玩家的回忆,也欢迎大家评论区留下你印象最深的机器~</p> <p><em>参考链接:</em></p> <ul> <li>搬瓦工产品合集:<a href="https://bwg.laoda.de/">https://bwg.laoda.de/</a></li> <li>VPS 推荐列表:<a href="https://blog.laoda.de/vps">https://blog.laoda.de/vps</a></li> <li>Hetzner 竞拍独服:<a href="https://www.hetzner.com/sb">https://www.hetzner.com/sb</a></li> </ul>

2026/4/15
阅读更多

聊聊目前手头的服务器近况 + VMRACK 三网优化线路 VPS 测试

<img src="https://blog.laoda.de/plugins/feed/assets/telemetry.gif?title=%E8%81%8A%E8%81%8A%E7%9B%AE%E5%89%8D%E6%89%8B%E5%A4%B4%E7%9A%84%E6%9C%8D%E5%8A%A1%E5%99%A8%E8%BF%91%E5%86%B5%20%2B%20VMRACK%20%E4%B8%89%E7%BD%91%E4%BC%98%E5%8C%96%E7%BA%BF%E8%B7%AF%20VPS%20%E6%B5%8B%E8%AF%95&amp;url=/archives/vmrack-test" width="1" height="1" alt="" style="opacity:0;"> <h2 id="唠嗑">唠嗑</h2> <p>好久没折腾服务器了,今天趁着下雨在家,把手头目前已有的服务器都登录了一遍,收拾一下,也顺便给大家更新一下咕咕服务器的最新近况~</p> <p><img src="https://blog.laoda.de/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fimg.laoda.de%2Fi%2F2026%2F04%2F12%2F170201-1.webp&amp;size=m" alt="服务器近况截图"></p> <p>地址:<a href="https://status.laoda.de/">https://status.laoda.de/</a></p> <p>目前持有的机器很多都是老古董了,买不到了。</p> <p>Olink 实在是可惜,由于资金问题,加上老板本来就佛系运营,已经官宣停止运营了。</p> <p>搬瓦工还是稳如老狗,咕咕的几台机器都已经要超过 1000 天在线了(The Chicken忘记续费被取消了 = =),之前黑五买的 Naranja Tech 也累计运行 886 天。</p> <p>对搬瓦工有感兴趣的小伙伴可以在这边选购:</p> <blockquote> <p>CN2 GIA 高速优化线路 VPS 选购:<a href="https://bwg.laoda.de">https://bwg.laoda.de</a></p> </blockquote> <p>好久没买新的机器了,今天来测一下 VMRACK 这家宣传做了三网优化的精品线路的机器,看看到底怎么样。</p> <h2 id="1--VMRACK-简介">1. VMRACK 简介</h2> <p>官网地址:<a href="https://www.vmrack.net?ref_code=5jz9MPhfBVH">https://www.vmrack.net</a></p> <p>VMRACK 主打三网优化线路,电信走 CN2 GIA,联通走 9929,移动走 CMIN2,看宣传还是挺有吸引力的。</p> <p>先说结论,实测下来,线路确实非常稳。</p> <h2 id="2--机器信息">2. 机器信息</h2> <p>咕咕手头的这台是 <strong>2 核 2G 内存,40G 硬盘,1.5T 流量/月,500Mbps 带宽</strong>,三网精品线路(电信走 CN2 GIA,联通走 9929,移动走 CMIN2)。</p> <blockquote> <p>注意商家把三网优化线路和三网精品线路做了区分,三网精品线路更高一级</p> </blockquote> <p><img src="https://blog.laoda.de/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fimg.laoda.de%2Fi%2F2026%2F04%2F13%2F133704-1.webp&amp;size=m" alt="4c7187eed6517e43257a324c97cc4daf.png"></p> <p><img src="https://blog.laoda.de/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fimg.laoda.de%2Fi%2F2026%2F04%2F12%2F170131-1.webp&amp;size=m" alt="机器信息"></p> <p><img src="https://blog.laoda.de/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fimg.laoda.de%2Fi%2F2026%2F04%2F12%2F170127-1.webp&amp;size=m" alt="机器信息2"></p> <p>续费价格:</p> <p><img src="https://blog.laoda.de/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fimg.laoda.de%2Fi%2F2026%2F04%2F12%2F170152-1.webp&amp;size=m" alt="ca6c1db04a144da2b816d17864e198bf"></p> <p>不过现在官方有特惠活动,</p> <p><img src="https://blog.laoda.de/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fimg.laoda.de%2Fi%2F2026%2F04%2F13%2F133717-1.webp&amp;size=m" alt="a9ce72e3f2c62aaed1464273fceda507.png"></p> <p>相同线路,最高性价比的35刀/年的售罄了,49刀/年的也售罄了,99刀/年的还有,还是比我这个常规款便宜不少 = = 地址:<a href="https://www.vmrack.net?ref_code=5jz9MPhfBVH">https://www.vmrack.net</a></p> <h2 id="3--开始测试">3. 开始测试</h2> <p>这边咕咕用的是融合怪测试脚本,一键跑完各项测试~</p> <pre><code class="language-bash">curl -L https://gitlab.com/spiritysdx/za/-/raw/main/ecs.sh -o ecs.sh &amp;&amp; chmod +x ecs.sh &amp;&amp; bash ecs.sh </code></pre> <blockquote> <p>GitHub 地址:https://github.com/spiritLHLS/ecs</p> </blockquote> <h3 id="3-1-CPU-情况">3.1 CPU 情况</h3> <p><img src="https://blog.laoda.de/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fimg.laoda.de%2Fi%2F2026%2F04%2F12%2F170111-1.webp&amp;size=m" alt="CPU测试结果"></p> <p>CPU 型号:AMD EPYC-Rome Processor</p> <p>单核跑分 <strong>1581</strong>,多核跑分 <strong>3178</strong>,表现还是不错的~</p> <h3 id="3-2-内存---硬盘">3.2 内存 &amp; 硬盘</h3> <p><img src="https://blog.laoda.de/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fimg.laoda.de%2Fi%2F2026%2F04%2F12%2F170103-1.webp&amp;size=m" alt="内存硬盘测试"></p> <p>内存读写速度正常,日常使用(跑 Docker 服务)没问题,fio 的 19.5k IOPS 足够撑住大多数应用场景。但 dd 测试暴露出读取性能偏弱,如果跑读密集型的业务(比如大量随机读取的数据库查询),可能会感受到瓶颈。</p> <h3 id="3-3-流媒体解锁">3.3 流媒体解锁</h3> <p>解锁情况还是比较理想的,直接丢几张图:</p> <p><img src="https://blog.laoda.de/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fimg.laoda.de%2Fi%2F2026%2F04%2F12%2F170055-1.webp&amp;size=m" alt="流媒体解锁1"></p> <p><img src="https://blog.laoda.de/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fimg.laoda.de%2Fi%2F2026%2F04%2F12%2F170050-1.webp&amp;size=m" alt="流媒体解锁2"></p> <p>Netflix、Disney+、ChatGPT、YouTube 这些主流平台基本都解锁了,日常使用完全够用~</p> <h3 id="3-4-IP-质量">3.4 IP 质量</h3> <p><img src="https://blog.laoda.de/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fimg.laoda.de%2Fi%2F2026%2F04%2F12%2F170011-1.webp&amp;size=m" alt="IP质量1"></p> <p><img src="https://blog.laoda.de/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fimg.laoda.de%2Fi%2F2026%2F04%2F12%2F170039-1.webp&amp;size=m" alt="5531258c47c64b6f960004c866ba1679"></p> <p>IP 为原生 IP,信任得分 100,整体质量不错。</p> <h3 id="3-5-邮件端口">3.5 邮件端口</h3> <p><img src="https://blog.laoda.de/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fimg.laoda.de%2Fi%2F2026%2F04%2F12%2F165953-1.webp&amp;size=m" alt="邮件端口检测"></p> <p>主流邮箱的 SMTP 端口基本都是开放的,有自建邮件服务需求的小伙伴可以参考~</p> <h3 id="3-6-三网回程情况">3.6 三网回程情况</h3> <p><img src="https://blog.laoda.de/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fimg.laoda.de%2Fi%2F2026%2F04%2F12%2F165945-1.webp&amp;size=m" alt="三网回程"></p> <p>这个是重点了!确实是三网优化:</p> <ul> <li><strong>电信回程</strong>:走 CN2 GIA</li> <li><strong>联通回程</strong>:走 AS9929</li> <li><strong>移动回程</strong>:走 CMIN2</li> </ul> <p>三网都走了优化线路。</p> <h3 id="3-7-回程路由">3.7 回程路由</h3> <p><img src="https://blog.laoda.de/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fimg.laoda.de%2Fi%2F2026%2F04%2F12%2F171549.webp&amp;size=m" alt="回程路由1"></p> <p><img src="https://blog.laoda.de/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fimg.laoda.de%2Fi%2F2026%2F04%2F12%2F165921-1.webp&amp;size=m" alt="a969428a69e140ecac3d48ce6afe739f"></p> <p>从路由追踪可以看到,电信确实走了 CN2 GIA 到香港再回国,联通走的 9929,移动走的 CMIN2,和宣传一致。</p> <h3 id="3-8-PING-情况">3.8 PING 情况</h3> <p><img src="https://blog.laoda.de/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fimg.laoda.de%2Fi%2F2026%2F04%2F12%2F165721-1.webp&amp;size=m" alt="PING测试"></p> <p>150ms 左右,属于美西正常 ping 值。</p> <h3 id="3-9-测速情况">3.9 测速情况</h3> <p><img src="https://blog.laoda.de/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fimg.laoda.de%2Fi%2F2026%2F04%2F12%2F165703-1.webp&amp;size=m" alt="测速1"></p> <p>上下行基本都能跑满 500Mbps 的带宽,表现相当不错。</p> <img src="https://blog.laoda.de/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fimg.laoda.de%2Fi%2F2026%2F04%2F12%2F165652-1.webp&amp;size=m" alt="测速2" style="zoom:50%;"> <p>另外,VMRACK 支持 <strong>1 美元更换 IP</strong>,对于有换 IP 需求的小伙伴来说还是比较友好的。</p> <h2 id="4--总结">4. 总结</h2> <p>VPS 性能不错,具体的参数大家可以看上面的测评结果。</p> <p>简单总结一下亮点:</p> <ul> <li><strong>IP 为原生 IP</strong>,解锁能力优秀</li> <li><strong>三网精品线路</strong>:电信 CN2 GIA + 联通 9929 + 移动 CMIN2</li> <li><strong>1 美元更换 IP</strong> 的服务</li> </ul> <p>有感兴趣的小伙伴可以去官网看看:<a href="https://www.vmrack.net?ref_code=5jz9MPhfBVH">https://www.vmrack.net</a></p> <hr> <h2 id="附-完整测试数据">附:完整测试数据</h2> <details> <summary>点击展开完整测试报告</summary> <pre><code class="language-txt">测评频道: https://t.me/+UHVoo2U4VyA5NTQ1 VPS融合怪版本:2026.02.28 Shell项目地址:https://github.com/spiritLHLS/ecs Go项目地址 [推荐]:https://github.com/oneclickvirt/ecs ---------------------基础信息查询--感谢所有开源项目---------------------- CPU 型号 : AMD EPYC-Rome Processor CPU 核心数 : 2 CPU 频率 : 2595.124 MHz CPU 缓存 : L1: 64.00 KB / L2: 1.00 MB / L3: 32.00 MB AES-NI指令集 : ✔ Enabled VM-x/AMD-V支持 : ❌ Disabled 内存 : 208.23 MiB / 1.92 GiB Swap : [ no swap partition or swap file detected ] 硬盘空间 : 1.67 GiB / 39.17 GiB 启动盘路径 : /dev/vda1 系统在线时间 : 1 days, 1 hour 27 min 负载 : 0.54, 0.16, 0.05 系统 : Debian GNU/Linux 12 (bookworm) (x86_64) 架构 : x86_64 (64 Bit) 内核 : 6.1.0-33-amd64 TCP加速方式 : bbr 虚拟化架构 : KVM NAT类型 : Full Cone IPV4 ASN : AS46783 EASY LINK LLC IPV4 位置 : Los Angeles / California / US ------------------------CPU测试--通过sysbench测试------------------------- -&gt; CPU 测试中 (Fast Mode, 1-Pass @ 5sec) 1 线程测试(单核)得分: 1581 Scores 2 线程测试(多核)得分: 3178 Scores --------------------内存测试--感谢lemonbench开源---------------------------- -&gt; 内存测试 Test (Fast Mode, 1-Pass @ 5sec) 单线程读测试: 41550.52 MB/s 单线程写测试: 19125.71 MB/s --------------------磁盘dd读写测试--感谢lemonbench开源-------------------- -&gt; 磁盘IO测试中 (4K Block/1M Block, Direct Mode) 测试操作 写速度 读速度 100MB-4K Block 34.7 MB/s (8465 IOPS, 3.02s) 5.6 MB/s (1357 IOPS, 18.86s) 1GB-1M Block 554 MB/s (528 IOPS, 1.89s) 122 MB/s (115 IOPS, 8.62s) ----------------------磁盘fio读写测试--感谢yabs开源----------------------- Block Size | 4k (IOPS) | 64k (IOPS) ------ | --- ---- | ---- ---- Read | 78.10 MB/s (19.5k) | 1.02 GB/s (16.0k) Write | 78.31 MB/s (19.5k) | 1.03 GB/s (16.1k) Total | 156.41 MB/s (39.1k) | 2.06 GB/s (32.2k) | | Block Size | 512k (IOPS) | 1m (IOPS) ------ | --- ---- | ---- ---- Read | 612.37 MB/s (1.1k) | 528.12 MB/s (515) Write | 644.91 MB/s (1.2k) | 563.29 MB/s (550) Total | 1.25 GB/s (2.4k) | 1.09 GB/s (1.0k) ---------------流媒体解锁--感谢oneclickvirt/UnlockTests测试---------------- Can not detect IPv6 Address 测试时间: 2026-04-11 08:37:32 IPV4: ============[ 跨国平台 ]============ Apple YES (Region: USA) BingSearch YES (Region: US) Claude YES Dazn YES (Region: US) Disney+ YES (Region: US) Gemini YES (Region: US) GoogleSearch YES Google Play Store YES (Region: US) IQiYi YES (Region: US) Instagram Licensed Audio YES KOCOWA YES MetaAI YES Netflix YES (Region: US) Netflix CDN US OneTrust YES (Region: US CALIFORNIA) ChatGPT YES (Region: US) Paramount+ YES Amazon Prime Video YES (Region: US) Reddit YES SonyLiv YES (Region: IN) Sora YES (Region: US) Spotify Registration NO Steam Store YES (Community Available) (Region: US) TVBAnywhere+ YES (Region: US) TikTok YES (Region: US) Viu.com YES Wikipedia Editability YES YouTube Region YES YouTube CDN LAX ---------------------TikTok解锁--感谢lmc999的源脚本--------------------- Tiktok Region: 【US】 -------------IP质量检测--基于oneclickvirt/securityCheck使用-------------- 数据仅作参考,不代表100%准确,如果和实际情况不一致请手动查询多个数据库比对 以下为各数据库编号,输出结果后将自带数据库来源对应的编号 ipinfo数据库 [0] | scamalytics数据库 [1] | virustotal数据库 [2] | abuseipdb数据库 [3] | ip2location数据库 [4] ip-api数据库 [5] | ipwhois数据库 [6] | ipregistry数据库 [7] | ipdata数据库 [8] | db-ip数据库 [9] ipapiis数据库 [A] | ipapicom数据库 [B] | bigdatacloud数据库 [C] | dkly数据库 [D] | ipqualityscore数据库 [E] ipintel数据库 [F] | ipfighter数据库 [G] | fraudlogix数据库 [H] | cloudflare数据库 [I] | IPV4: 安全得分: 信任得分(越高越好): 100 [8] VPN得分(越低越好): 0 [8] 代理得分(越低越好): 0 [8] 社区投票-无害: 0 [2] 社区投票-恶意: 0 [2] 威胁得分(越低越好): 1 [8] 欺诈得分(越低越好): 65 [E] 滥用得分(越低越好): 0 [3] ASN滥用得分(越低越好): 0.0152 (Elevated) [A] 公司滥用得分(越低越好): 0.0009 (Low) [A] 威胁级别: low [B] 流量占比: 真人(越高越好)47% [I] 机器人(越低越好)52% [I] 黑名单记录统计:(有多少黑名单网站有记录): 无害记录数: 0 [2] 恶意记录数: 0 [2] 可疑记录数: 0 [2] 无记录数: 94 [2] 安全信息: 使用类型: unknown [8] hosting [0 3 7 C] business [A] 公司类型: hosting [A] isp [0 7] 浏览器类型: 主流82% 其他17% [I] 设备类型: 桌面77% 移动22% 其他0% [I] 操作系统类型: 主流94% 其他5% [I] 是否云提供商: Yes [7] 是否数据中心: No [5 8 A G] Yes [0 C] 是否移动设备: No [5 A C G] Yes [E] 是否代理: No [0 4 5 7 8 A B C] Yes [E G] 是否VPN: Yes [E] No [0 7 A C G] 是否Tor: No [0 3 7 8 A B C E] 是否Tor出口: No [7] 是否网络爬虫: No [A B E] 是否匿名: No [7 8] 是否攻击者: No [7 8] 是否滥用者: No [7 8 A C E] 是否威胁: No [7 8 C] 是否中继: No [0 7 8 C] 是否Bogon: No [7 8 A C] 是否机器人: No [E] DNS-黑名单: 304(Total_Check) 0(Clean) 10(Blacklisted) 17(Other) Google搜索可行性:NO ------------邮件端口检测--基于oneclickvirt/portchecker开源------------ Platform SMTP SMTPS POP3 POP3S IMAP IMAPS LocalPort ✔ ✔ ✔ ✔ ✔ ✔ QQ ✔ ✔ ✔ ✘ ✔ ✘ 163 ✔ ✔ ✔ ✘ ✔ ✘ Sohu ✔ ✔ ✔ ✘ ✔ ✘ Yandex ✔ ✔ ✔ ✘ ✔ ✘ Gmail ✔ ✔ ✘ ✘ ✘ ✘ Outlook ✔ ✘ ✔ ✘ ✔ ✘ Office365 ✔ ✘ ✔ ✘ ✔ ✘ Yahoo ✔ ✔ ✘ ✘ ✘ ✘ MailCOM ✔ ✔ ✔ ✘ ✔ ✘ MailRU ✔ ✔ ✘ ✘ ✔ ✘ AOL ✔ ✔ ✘ ✘ ✘ ✘ GMX ✔ ✔ ✔ ✘ ✔ ✘ Sina ✔ ✘ ✔ ✘ ✔ ✘ Apple ✘ ✔ ✘ ✘ ✘ ✘ FastMail ✘ ✔ ✘ ✘ ✘ ✘ ProtonMail✘ ✘ ✘ ✘ ✘ ✘ MXRoute ✔ ✘ ✔ ✘ ✔ ✘ Namecrane ✔ ✘ ✔ ✘ ✔ ✘ XYAMail ✘ ✘ ✘ ✘ ✘ ✘ ZohoMail ✘ ✔ ✘ ✘ ✘ ✘ Inbox_eu ✔ ✔ ✔ ✘ ✘ ✘ Free_fr ✘ ✔ ✔ ✘ ✔ ✘ -------------上游及三网回程--基于oneclickvirt/backtrace开源-------------- 国家: US 城市: Los Angeles 服务商: AS46783 EASY LINK LLC 北京电信v4 219.141.140.10 电信CN2GIA [精品线路] 北京联通v4 202.106.195.68 联通9929 [优质线路] 北京移动v4 221.179.155.161 移动CMI [普通线路] 移动CMIN2 [精品线路] 上海电信v4 202.96.209.133 检测不到回程路由节点的IPV4地址 上海联通v4 210.22.97.1 联通9929 [优质线路] 联通4837 [普通线路] 上海移动v4 211.136.112.200 移动CMI [普通线路] 移动CMIN2 [精品线路] 广州电信v4 58.60.188.222 电信CN2GIA [精品线路] 广州联通v4 210.21.196.6 联通9929 [优质线路] 广州移动v4 120.196.165.24 移动CMI [普通线路] 移动CMIN2 [精品线路] 成都电信v4 61.139.2.69 电信CN2GIA [精品线路] 成都联通v4 119.6.6.6 联通9929 [优质线路] 联通4837 [普通线路] 成都移动v4 211.137.96.205 移动CMI [普通线路] 移动CMIN2 [精品线路] 准确线路自行查看详细路由,本测试结果仅作参考 同一目标地址多个线路时,检测可能已越过汇聚层,除第一个线路外,后续信息可能无效 ----------------------回程路由--基于nexttrace开源----------------------- 依次测试电信/联通/移动经过的地区及线路,核心程序来自nexttrace,请知悉! 广州电信 58.60.188.222 0.52 ms * RFC1918 1.24 ms AS4134 [CHINANET-US] 美国 加利福尼亚 洛杉矶 chinatelecom.com.cn 电信 149.67 ms * [CN2-Global] 中国 香港 chinatelecom.cn 电信 150.87 ms * [CN2-BackBone] 中国 广东 广州 chinatelecom.cn 电信 157.66 ms * [CN2-BackBone] 中国 广东 广州 chinatelecom.cn 电信 151.97 ms AS4134 [CHINANET-BB] 中国 广东 广州 chinatelecom.com.cn 电信 159.56 ms AS4134 [APNIC-AP] 中国 广东 深圳 chinatelecom.com.cn 电信 广州联通 210.21.196.6 1.12 ms * RFC1918 1.31 ms AS10099 中国 香港 chinaunicomglobal.com 165.78 ms AS10099 [CUG-BACKBONE] 中国 香港 chinaunicomglobal.com 联通 172.08 ms AS9929 [CNC-BACKBONE] 中国 上海 chinaunicom.cn 联通 CUII 149.18 ms AS9929 [CNC-BACKBONE] 中国 广东 广州 chinaunicom.cn 联通 CUII 160.21 ms * [CNC-BACKBONE] 中国 广东 广州 190.30 ms AS17623 [APNIC-AP] 中国 广东 深圳 chinaunicom.cn 联通 193.60 ms AS17623 中国 广东 深圳 宝安区 chinaunicom.cn 联通 广州移动 120.196.165.24 0.68 ms * RFC1918 1.12 ms AS58807 [CMIN2-NET] 中国 cmi.chinamobile.com 移动 129.02 ms AS58807 [CMIN2-NET] 美国 加利福尼亚 洛杉矶 cmi.chinamobile.com 移动 126.54 ms AS58807 [CMIN2-NET] 中国 上海 洛杉矶-上海 cmi.chinamobile.com 移动 129.41 ms AS9808 [CMNET] 中国 上海 X-I chinamobileltd.com 移动 128.32 ms AS9808 [CMNET] 中国 上海 I-C chinamobileltd.com 移动 129.03 ms AS9808 [CMNET] 中国 上海 chinamobileltd.com 153.36 ms AS9808 [CMNET] 中国 北京 chinamobileltd.com 移动 151.70 ms AS9808 [CMNET] 中国 北京 chinamobileltd.com 移动 154.29 ms AS9808 [CMNET] 中国 北京 chinamobileltd.com 移动 153.31 ms AS56040 [APNIC-AP] 中国 广东 深圳 gd.10086.cn 移动 ---------------------自动更新测速节点列表--本脚本原创---------------------- 位置 上传速度 下载速度 延迟 丢包率 Speedtest.net 511.98 Mbps 494.12 Mbps 1.04 0.0% 检测到错误:Aborted或core dumped,终止测速 执行出现错误,如果有必要请使用 https://github.com/oneclickvirt/ecs 进行测试,避免环境依赖出现问题 联通上海5G 535.07 Mbps 546.82 Mbps 173.12 NULL 检测到错误:Aborted或core dumped,终止测速 执行出现错误,如果有必要请使用 https://github.com/oneclickvirt/ecs 进行测试,避免环境依赖出现问题 电信Suzhou5G 513.73 Mbps 553.80 Mbps 138.52 NULL 电信Zhenjiang5G 557.11 Mbps 548.61 Mbps 137.32 NULL 移动Suzhou 372.13 Mbps 0.39 Mbps 137.64 0.0% ------------------------------------------------------------------------ 总共花费 : 6 分 1 秒 时间 : Sat Apr 11 08:41:13 UTC 2026 ------------------------------------------------------------------------ </code></pre> </details>

2026/4/13
阅读更多

德国买iPhone退税全流程实测:19%消费税能退多少?踩坑+避坑全记录

<img src="https://blog.laoda.de/plugins/feed/assets/telemetry.gif?title=%E5%BE%B7%E5%9B%BD%E4%B9%B0iPhone%E9%80%80%E7%A8%8E%E5%85%A8%E6%B5%81%E7%A8%8B%E5%AE%9E%E6%B5%8B%EF%BC%9A19%25%E6%B6%88%E8%B4%B9%E7%A8%8E%E8%83%BD%E9%80%80%E5%A4%9A%E5%B0%91%EF%BC%9F%E8%B8%A9%E5%9D%91%2B%E9%81%BF%E5%9D%91%E5%85%A8%E8%AE%B0%E5%BD%95&amp;url=/archives/germany-iphone-tax-refund-guide" width="1" height="1" alt="" style="opacity:0;"> <p>好久没和大家更新博客了,这段时间真的事情巨多(家里的事情+公司出差+回来之后研究小龙虾和Claude Code),一直想着有时间就要更新一下,但是每次回到家忙完事情已经挺晚了,也感觉累了,就迟迟没有动手写博客。</p> <p>今天趁着闲暇时间,赶紧和大家更新一波。</p> <h2 id="购机篇">购机篇</h2> <p>我在3月份的时候出差去了趟德国,工作结束之后,我突然心血来潮说要不要整一部国际版的iPhone。</p> <p>熟悉咕咕的小伙伴知道,其实我目前有一台美版的iPhone SE3在服役中(支持实体SIM+eSIM),手感非常好,续航问题我也通过背夹电池的方法解决了(官方方案的升级版,后续和大家分享,可以重度使用一天),但问题就是屏幕确实有点小了,打字也容易按错。</p> <p>"这次出差算是比较成功的,下次带队不知道是啥时候了,买台iPhone 17 Pro Max奖励一下自己,也顺便给大家实测一下在德国购买iPhone的流程和后续的退税流程。"</p> <p>嗯,我是这么说服我自己的。虽然我知道港版功能一样,而且更便宜……</p> <p>德国这边对换手机这件事情好像他们不怎么在意,地铁公交上很多都用的旧手机。因为我第二天就要出发返回国内,我先看了下小红书攻略,说最好提前预约,然后门店提货,会更稳一点,于是我就用我的SE3 在苹果的德国官网上下单了一台蓝色 512G的iPhone 17 Pro Max,用的Apple Pay选的Chase的信用卡付款的,选择线下提货的地点是慕尼黑玛丽亚广场的苹果官方旗舰店。</p> <p><img src="https://blog.laoda.de/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fimg.laoda.de%2Fi%2F2026%2F04%2F02%2F145346-1.webp&amp;size=m" alt="c8a62bede31c33f7ae35f6174e36d34e.png"></p> <p>早起过去,人果然很多。</p> <p><img src="https://blog.laoda.de/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fimg.laoda.de%2Fi%2F2026%2F04%2F02%2F145344-1.webp&amp;size=m" alt="IMG20260316114848.jpg"></p> <p>和工作人员说明来取货,出示了订单和二维码,</p> <p><img src="https://blog.laoda.de/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fimg.laoda.de%2Fi%2F2026%2F04%2F02%2F145340-1.webp&amp;size=m" alt="b55f6bf87662b37b6983b935ce873670.png"></p> <p>很快小姐姐就给我拿来了手机。</p> <p>图片 小姐姐</p> <p>然后我表示我需要退税,小姐姐似乎不太懂,询问了身边的同事,叽里咕噜的,德语我们也听不懂,然后小姐姐去拿来了一张单子,长这样:</p> <p><img src="https://blog.laoda.de/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fimg.laoda.de%2Fi%2F2026%2F04%2F02%2F145337-1.webp&amp;size=m" alt="微信图片_20260402135927_278_361.jpg"></p> <p><img src="https://blog.laoda.de/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fimg.laoda.de%2Fi%2F2026%2F04%2F03%2F101000-1.webp&amp;size=m" alt="DJI_20260316093501_0052_D-0004.png"></p> <p>你敢相信,在德国给外国人的退税的单子居然是德语的,而且小姐姐很抱歉地说没有英语的版本,可能需要我们自己借助翻译填写一下……</p> <p>而且由于我们是线上下单的,店里并没有给我们发票。(我查询了一下发现退税需要有退税单和发票还有实物的)</p> <h2 id="退税踩坑">退税踩坑</h2> <p>我虽然表示震惊,但也接受了,凭借我多年官网购买iPhone的经验,苹果官方肯定会给我邮件发一个发票的,而且看起来这个表是苹果官方的退税,没有中间商赚差价,可以退全额,你知道德国的电子产品消费税是多少吗?19% !</p> <p><img src="https://blog.laoda.de/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fimg.laoda.de%2Fi%2F2026%2F04%2F02%2F145335-1.webp&amp;size=m" alt="c52a32d17627facb4ba29e9596383aca.png"></p> <p>怪不得感觉大家都换新手机不感冒……</p> <p>然而新的问题出现了……</p> <p>我下午翻看邮件,一直没有等到苹果给我发的发票,没有发票就意味着海关不可能给我退税,我又查了下,发现在官网购买的手机,电子发票可能是在24-48小时内送达到邮箱!我的机票是第二天早上的,时间上来不及了。</p> <p>于是我和同事又感受了大概四十分钟的地铁,从慕尼黑机场的希尔顿返回到玛丽亚广场。我找了个理由和店员说支付的时候卡弄错了,想要把这台网上订的、上午刚提的iPhone退了,然后换一张卡在店里直接买一台。</p> <p>这次接待的是一个小哥,小哥欣然同意表示没问题,简单检查了手机之后,直接给我退了款,然后我换了一张招行的卡支付,他还是把这台手机给了我,并熟练地给我global blue的单子,告诉我可以退大概10%的税,而且在玛丽亚广场不远的地方有global blue的柜台。(同事略懂日语,还和小哥谈起了他手臂上的纹身 = =)</p> <p>我们过去之后工作人员给盖了章,然后工作人员让我出示了信用卡(因为我打算退到信用卡里),最后告诉了我慕尼黑机场的海关的位置,以及告诉我敲完海关的章需要把信封交给机场的global blue的柜台,整体体验非常丝滑。</p> <p><img src="https://blog.laoda.de/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fimg.laoda.de%2Fi%2F2026%2F04%2F02%2F145331-1.webp&amp;size=m" alt="IMG20260316202639.jpg"></p> <p><img src="https://blog.laoda.de/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fimg.laoda.de%2Fi%2F2026%2F04%2F02%2F145340-1_1.webp&amp;size=m" alt="IMG_20260402_144825.jpg"></p> <h2 id="退税篇">退税篇</h2> <p>手机总算告一段落,但是还有一个问题出现了……</p> <p>本来我打算直接拆封使用的,但是我还是做了一下功课——购买的产品不能使用,一旦使用了之后海关不给退税!</p> <p>所以还是只能老老实实装在包里,等第二天退完税之后使用。</p> <p>我这次是慕尼黑 T1 航站楼出发,海关在 B 区,Global Blue 就在对面,流程还算比较清楚。</p> <p><img src="https://blog.laoda.de/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fimg.laoda.de%2Fi%2F2026%2F04%2F02%2F145328-1.webp&amp;size=m" alt="IMG20260316144455.jpg"></p> <p><img src="https://blog.laoda.de/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fimg.laoda.de%2Fi%2F2026%2F04%2F02%2F145339-1.webp&amp;size=m" alt="4913918cdc827cf000974b0dbb414e39.png"></p> <p><img src="https://blog.laoda.de/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fimg.laoda.de%2Fi%2F2026%2F04%2F02%2F145332-1.webp&amp;size=m" alt="bcd887f1b03eb755000264551cdf02e9.png"></p> <p>如果你买的东西是走 Global Blue 退税的,而且放在托运行李箱里,值机的时候一定要先跟工作人员说:</p> <blockquote> <p>箱子里有 tax free items</p> </blockquote> <p>工作人员会先给你的行李箱贴标签,然后你拿着箱子去海关那边盖章,盖完之后海关会直接帮你把行李托运走,因为前面已经贴好标签了,所以不用担心箱子乱跑。</p> <p>之后你再去对面的 Global Blue 柜台办后续就可以。</p> <p>如果退税物品是在随身行李里,正常来说是要过完安检以后,再去里面的海关检查盖章。</p> <p>不过我这次也直接拿给外面的海关看了,结果他也给我盖章了,所以我这次就一起退掉了,但这个我感觉不一定每次都行,可能还是看现场工作人员。</p> <p><strong>几个小经验:</strong></p> <ul> <li>小额的东西一般不太查</li> <li>大额的基本都会查</li> <li>买的东西千万别拆封</li> </ul> <p>这次买的手机,海关那边检查了非常久,反复用手指抠,问我是不是拆过。所以像 iPhone 这种高价值商品,千万别提前拆,不然很容易影响退税。</p> <p>顺便说一下,这次除了手机,我在 dm 也顺手买了一些东西。dm 的退税流程和 Global Blue 不太一样,麻烦不少——要么你下次回店里,拿海关盖章的小票再去退;要么走国内的易退税邮寄处理。两种我个人觉得都不算方便,有机会还是直接去店里退更省事。不过这次 dm 的东西,海关是完全没检查。</p> <h2 id="最终到手价格">最终到手价格</h2> <p>最后,看看这部手机退税下来一共花了多少钱:</p> <p>手机原价 13525.96 - 汇率优惠 13.53 - Global Blue 退税 1340.78 - 招行1%返现 131.46 = <strong>12040.19 元</strong></p> <img src="https://blog.laoda.de/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fimg.laoda.de%2Fi%2F2026%2F04%2F02%2F145313-1.webp&amp;size=m" alt="截屏 2026-04-02 14.19.48.png" style="zoom:50%;"> <img src="https://blog.laoda.de/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fimg.laoda.de%2Fi%2F2026%2F04%2F02%2F145306-1.webp&amp;size=m" alt="微信图片_20260402142038_1463_68.jpg" style="zoom:50%;"> <p>招行那个1%的返现好像实际到账不足1%?</p> <p>比起国内还是贵了不少的。</p> <h2 id="关于德国版-iPhone">关于德国版 iPhone</h2> <p>最后,给大家看一下德国版本的iPhone有啥不一样的地方:</p> <img src="https://blog.laoda.de/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fimg.laoda.de%2Fi%2F2026%2F04%2F03%2F100929-1.webp&amp;size=m" alt="微信图片_20260403080935_283_361.jpg" style="zoom:50%;"> <p>其实除了这个CE标识,其他的和港版应该是一样的,日常eSIM需求的更推荐港版,我这个就当纪念了 = =</p>

2026/4/3
阅读更多

10 分钟搭建一个支持超过 1000 种不同格式的在线文件转换器——ConvertX|好玩儿的 Docker 项目

<img src="https://blog.laoda.de/plugins/feed/assets/telemetry.gif?title=10%20%E5%88%86%E9%92%9F%E6%90%AD%E5%BB%BA%E4%B8%80%E4%B8%AA%E6%94%AF%E6%8C%81%E8%B6%85%E8%BF%87%201000%20%E7%A7%8D%E4%B8%8D%E5%90%8C%E6%A0%BC%E5%BC%8F%E7%9A%84%E5%9C%A8%E7%BA%BF%E6%96%87%E4%BB%B6%E8%BD%AC%E6%8D%A2%E5%99%A8%E2%80%94%E2%80%94ConvertX%EF%BD%9C%E5%A5%BD%E7%8E%A9%E5%84%BF%E7%9A%84%20Docker%20%E9%A1%B9%E7%9B%AE&amp;url=/archives/docker-compose-install-convertx" width="1" height="1" alt="" style="opacity:0;"> <h2 id="1--唠嗑">1. 唠嗑</h2> <p>好久没更新了,这周来和大家分享一个自托管的在线文件转换器。支持超过一千种不同格式。使用 TypeScript、Bun 和 Elysia 编写。</p> <p><img src="https://blog.laoda.de/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fimg.laoda.de%2Fi%2F2026%2F01%2F20%2F150219-1.webp&amp;size=m" alt="e3a1dbdc76a602c085010e7ba78c6e40.png"></p> <h2 id="2--ConvertX-简介">2. ConvertX 简介</h2> <p><strong>功能特性</strong></p> <ul> <li>将文件转换为不同格式</li> <li>一次处理多个文件</li> <li>密码保护</li> <li>多个账户</li> </ul> <p><img src="https://blog.laoda.de/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fimg.laoda.de%2Fi%2F2026%2F01%2F20%2F150217-1.webp&amp;size=m" alt="5da3095bb57e3702117dda7268c82b2d.png"></p> <p>支持转换的类型</p> <table> <thead> <tr> <th>Converter</th> <th>Use case</th> <th>Converts from</th> <th>Converts to</th> </tr> </thead> <tbody> <tr> <td><a href="https://inkscape.org/">Inkscape</a></td> <td>Vector images</td> <td>7</td> <td>17</td> </tr> <tr> <td><a href="https://github.com/libjxl/libjxl">libjxl</a></td> <td>JPEG XL</td> <td>11</td> <td>11</td> </tr> <tr> <td><a href="https://github.com/RazrFalcon/resvg">resvg</a></td> <td>SVG</td> <td>1</td> <td>1</td> </tr> <tr> <td><a href="https://github.com/libvips/libvips">Vips</a></td> <td>Images</td> <td>45</td> <td>23</td> </tr> <tr> <td><a href="https://github.com/strukturag/libheif">libheif</a></td> <td>HEIF</td> <td>2</td> <td>4</td> </tr> <tr> <td><a href="https://tug.org/xetex/">XeLaTeX</a></td> <td>LaTeX</td> <td>1</td> <td>1</td> </tr> <tr> <td><a href="https://calibre-ebook.com/">Calibre</a></td> <td>E-books</td> <td>26</td> <td>19</td> </tr> <tr> <td><a href="https://www.libreoffice.org/">LibreOffice</a></td> <td>Documents</td> <td>41</td> <td>22</td> </tr> <tr> <td><a href="https://github.com/TomWright/dasel">Dasel</a></td> <td>Data Files</td> <td>5</td> <td>4</td> </tr> <tr> <td><a href="https://pandoc.org/">Pandoc</a></td> <td>Documents</td> <td>43</td> <td>65</td> </tr> <tr> <td><a href="https://github.com/mvz/email-outlook-message-perl">msgconvert</a></td> <td>Outlook</td> <td>1</td> <td>1</td> </tr> <tr> <td>VCF to CSV</td> <td>Contacts</td> <td>1</td> <td>1</td> </tr> <tr> <td><a href="https://dvisvgm.de/">dvisvgm</a></td> <td>Vector images</td> <td>4</td> <td>2</td> </tr> <tr> <td><a href="https://imagemagick.org/">ImageMagick</a></td> <td>Images</td> <td>245</td> <td>183</td> </tr> <tr> <td><a href="http://www.graphicsmagick.org/">GraphicsMagick</a></td> <td>Images</td> <td>167</td> <td>130</td> </tr> <tr> <td><a href="https://github.com/assimp/assimp">Assimp</a></td> <td>3D Assets</td> <td>77</td> <td>23</td> </tr> <tr> <td><a href="https://ffmpeg.org/">FFmpeg</a></td> <td>Video</td> <td>~472</td> <td>~199</td> </tr> <tr> <td><a href="https://potrace.sourceforge.net/">Potrace</a></td> <td>Raster to vector</td> <td>4</td> <td>11</td> </tr> <tr> <td><a href="https://github.com/visioncortex/vtracer">VTracer</a></td> <td>Raster to vector</td> <td>8</td> <td>1</td> </tr> <tr> <td><a href="https://github.com/microsoft/markitdown">Markitdown</a></td> <td>Documents</td> <td>6</td> <td>1</td> </tr> </tbody> </table> <h2 id="3--相关地址">3. 相关地址</h2> <p>官方 GitHub 地址:<a href="https://github.com/C4illin/ConvertX">https://github.com/C4illin/ConvertX</a> (目前 15.3k 个 star,欢迎大家去给项目点星星!)</p> <p>当然如果你想自己用 docker 搭建一个,那我们就继续往下!</p> <h2 id="4--搭建环境">4. 搭建环境</h2> <ul> <li>服务器:咕咕这边用的香港的 <a href="http://gao.ee/lca">莱卡云服务器</a>,建议服务器内存 1G 以上,当然你也可以选择其他 <a href="https://blog.laoda.de/vps">高性价比的服务器</a>。</li> <li>系统:Debian 11 (<a href="https://blog.laoda.de/archives/useful-script/">DD 脚本</a> 非必需 DD,用原来的系统也 OK,之后教程都是用 Debian 或者 Ubuntu 搭建~)</li> <li>安装好 Docker、Docker-compose(<a href="https://blog.laoda.de/archives/hello-docker/">相关脚本</a>)</li> <li>【必需】域名一枚,并做好解析到服务器上(<a href="https://blog.laoda.de/archives/namesilo/">域名购买、域名解析</a> <a href="https://www.bilibili.com/video/BV1Sy4y1k7kZ/">视频教程</a>)</li> <li>【非必需】提前安装好宝塔面板海外版本 aapanel,并安装好 Nginx(<a href="https://forum.aapanel.com/d/9-aapanel-linux-panel-6812-installation-tutorial">安装地址</a>)</li> <li>【非必需本教程选用】安装好 Nginx Proxy Manager(<a href="https://blog.laoda.de/archives/nginxproxymanager/">相关教程</a>)</li> </ul> <h2 id="5--搭建视频-过俩周补充-----">5. 搭建视频(过俩周补充 = =)</h2> <h3 id="5-1-YouTube">5.1 YouTube</h3> <p>视频地址:</p> <h3 id="5-2-哔哩哔哩">5.2 哔哩哔哩</h3> <p>哔哩哔哩:</p> <h2 id="6--搭建方式">6. 搭建方式</h2> <h3 id="6-1-安装-Docker-与-Nginx-Proxy-Manager">6.1 安装 Docker 与 Nginx Proxy Manager</h3> <p>可以直接参考这篇内容:</p> <p><a href="https://blog.laoda.de/archives/nginxproxymanager/">https://blog.laoda.de/archives/nginxproxymanager/</a></p> <h3 id="6-2-创建安装目录">6.2 创建安装目录</h3> <p>创建一下安装的目录:</p> <pre><code class="language-bash">sudo -i mkdir -p /root/data/docker_data/convertx cd /root/data/docker_data/convertx </code></pre> <p>接着我们来编辑下<code>docker-compose.yml</code></p> <pre><code class="language-bash">vim docker-compose.yml </code></pre> <pre><code class="language-yaml">services: convertx: image: ghcr.io/c4illin/convertx container_name: convertx restart: unless-stopped ports: - "3001:3000" environment: - JWT_SECRET=aLongAndSecretStringUsedToSignTheJSONWebToken123412 # will use randomUUID() if unset # - HTTP_ALLOWED=true # uncomment this if accessing it over a non-https connection 如果通过 http 访问的话取消这行注释 - LANGUAGE=zh volumes: - ./data:/app/data </code></pre> <p>其中的左边的<code>3001</code>可以改成服务器上没有用过的端口。</p> <p>修改完成之后,可以在英文输入法下,按 <code>i</code> 修改,完成之后,按一下 <code>esc</code>,然后 <code>:wq</code> 保存退出。</p> <h4 id="环境变量">环境变量</h4> <p>所有都是可选的,但建议设置 <code>JWT_SECRET</code>。</p> <table> <thead> <tr> <th>Name</th> <th>Default</th> <th>Description</th> </tr> </thead> <tbody> <tr> <td>JWT_SECRET</td> <td>when unset it will use the value from randomUUID()</td> <td>用于签署 JSON Web Token 的一串密钥字符串</td> </tr> <tr> <td>ACCOUNT_REGISTRATION</td> <td>false</td> <td>允许用户注册账户</td> </tr> <tr> <td>HTTP_ALLOWED</td> <td>false</td> <td>允许 HTTP 连接,仅在本地部署的时候将它设置为 true</td> </tr> <tr> <td>ALLOW_UNAUTHENTICATED</td> <td>false</td> <td>允许未认证用户使用该服务,仅在本地部署的时候将它设置为 true。</td> </tr> <tr> <td>AUTO_DELETE_EVERY_N_HOURS</td> <td>24</td> <td>每 n 小时检查一次,删除超过 n 小时的文件,设置为 0 表示禁用。</td> </tr> <tr> <td>WEBROOT</td> <td></td> <td>将根路径设置为 "/convert" 的地址将在 "example.com/convert/" 上提供网站。</td> </tr> <tr> <td>FFMPEG_ARGS</td> <td></td> <td>传递给 ffmpeg 输入文件的参数,例如 <code>-hwaccel vaapi</code>。有关硬件加速的更多信息,请参见 <a href="https://github.com/C4illin/ConvertX/issues/190">#190</a>。</td> </tr> <tr> <td>FFMPEG_OUTPUT_ARGS</td> <td></td> <td>传递给 ffmpeg 输出的参数,例如 <code>-preset veryfast</code></td> </tr> <tr> <td>HIDE_HISTORY</td> <td>false</td> <td>隐藏历史页面</td> </tr> <tr> <td>LANGUAGE</td> <td>en</td> <td>用于格式化日期字符串的语言,指定为 <a href="https://en.wikipedia.org/wiki/IETF_language_tag">BCP 47 language tag</a></td> </tr> <tr> <td>UNAUTHENTICATED_USER_SHARING</td> <td>false</td> <td>所有未经身份验证用户共享转换历史记录</td> </tr> <tr> <td>MAX_CONVERT_PROCESS</td> <td>0</td> <td>允许的最大并发转换进程数量。设置为 0 表示无限制。</td> </tr> </tbody> </table> <p>按需添加修改,修改完成之后,可以在英文输入法下,按 <code>i</code> 修改,完成之后,按一下 <code>esc</code>,然后 <code>:wq</code> 保存退出。</p> <h3 id="6-3-查看端口是否被占用">6.3 查看端口是否被占用</h3> <p>查看端口是否被占用(以 <code>3001</code> 为例),输入:</p> <pre><code class="language-bash">lsof -i:3001 #查看 3001 端口是否被占用,如果被占用,重新自定义一个端口 </code></pre> <p>如果啥也没出现,表示端口未被占用,我们可以继续下面的操作了~</p> <p>如果出现:</p> <pre><code class="language-bash">-bash: lsof: command not found </code></pre> <p>运行:</p> <pre><code class="language-bash">apt install lsof #安装 lsof </code></pre> <p>如果端口没有被占用(被占用了就修改一下端口,比如改成 <code>8381</code>,注意 docker 命令行里和防火墙都要改)</p> <h3 id="6-4-启动-convertx">6.4 启动 convertx</h3> <pre><code>cd /root/data/docker_data/convertx docker compose up -d # 注意,老版本用户用 docker-compose up -d </code></pre> <p><img src="https://blog.laoda.de/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fimg.laoda.de%2Fi%2F2026%2F01%2F20%2F150655-1.webp&amp;size=m" alt="662decf436731fa2acf7a276e4af9caf.png"></p> <p>镜像还是比较大的……</p> <p>耐心等待拉取好镜像,出现 <code>Created</code>的字样之后,</p> <p>理论上我们就可以输入 <code>http://ip:3001</code> 访问了。</p> <p>但是我们部署在公网一定要考虑使用反向代理工具配置 SSL!</p> <p>做反向代理前,你需要一个域名!</p> <p><a href="https://loll.cc/ns">namesilo</a> 上面 xyz 后缀的域名一年就 7 块钱,可以年抛。(冷知识,namesilo 上 6 位数字的 xyz 续费永远都是 0.99 美元 = =)</p> <p>如果想要长期使用,还是建议买 com 后缀的域名,更加正规一些,可以输入 <code>laodade</code> 来获得 1 美元的优惠(不知道现在还有没有)</p> <p><a href="https://loll.cc/ns">namesilo</a> 自带隐私保护,咕咕一直在用这家,价格也是这些注册商里面比较低的,关键是他家不像其他家域名注册商,没有七七八八的套路!(就是后台界面有些 <del>丑</del> 古老 = =)</p> <p><a href="https://blog.laoda.de/archives/namesilo/">【域名购买】Namesilo 优惠码和域名解析教程(附带服务器购买推荐和注意事项)</a></p> <p>我们接着往下看!</p> <h2 id="7--反向代理">7. 反向代理</h2> <h3 id="7-1-利用-Nginx-Proxy-Manager">7.1 利用 Nginx Proxy Manager</h3> <p>在添加反向代理之前,确保你已经完成了域名解析,不会的可以看这个:<strong>域名一枚,并做好解析到服务器上</strong>(<a href="https://blog.laoda.de/archives/namesilo/">域名购买、域名解析</a> <a href="https://www.bilibili.com/video/BV1Sy4y1k7kZ/">视频教程</a>) (名称改成你自己想要的域名前缀即可)</p> <p><img src="https://blog.laoda.de/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fimg.laoda.de%2Fi%2F2026%2F01%2F20%2F150210-1.webp&amp;size=m" alt="0a6c012bb847558679e499f447dd4958.png"></p> <p>之后,登陆 Nginx Proxy Manager(不会的看这个:<strong>安装 Nginx Proxy Manager</strong>(<a href="https://blog.laoda.de/archives/nginxproxymanager/">相关教程</a>))</p> <blockquote> <p><strong>注意:</strong></p> <p>Nginx Proxy Manager(以下简称 NPM)会用到 <code>80</code>、<code>443</code> 端口,所以本机不能占用(比如原来就有 Nginx)</p> </blockquote> <p>直接丢几张图:</p> <img src="https://blog.laoda.de/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fimg.laoda.de%2Fi%2F2026%2F01%2F20%2F150207-1.webp&amp;size=m" alt="61a4b0f2f2c6bb56b3bf540e00deb8a5.png" style="zoom:50%;"> <img src="https://blog.laoda.de/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fimg.laoda.de%2Fi%2F2026%2F01%2F20%2F150204-1.webp&amp;size=m" alt="ceb23ce98beed6ac28add2b03d4e53e8.png" style="zoom:50%;"> <img src="https://blog.laoda.de/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fimg.laoda.de%2Fi%2F2026%2F01%2F20%2F150203-1.webp&amp;size=m" alt="894dac73d5b90a9032806b61cdf22540.png" style="zoom:50%;"> <img src="https://blog.laoda.de/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fimg.laoda.de%2Fi%2F2026%2F01%2F20%2F150200-1.webp&amp;size=m" alt="67b28075187f39a44dce4b23b9228abb.png" style="zoom:50%;"> <blockquote> <p>注意填写对应的 <code>域名</code>、<code>IP</code> 和 <code>端口</code>,按文章来的话,应该是 <code>3001</code></p> </blockquote> <p><strong>IP 填写:</strong></p> <p>如果 Nginx Proxy Manager 和 convertx 在同一台服务器上,可以在终端输入:</p> <pre><code class="language-bash">ip addr show docker0 </code></pre> <p>查看对应的 Docker 容器内部 IP。</p> <img src="https://blog.laoda.de/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fimg.laoda.de%2Fi%2F2024%2F12%2F12%2F153819-1.webp&amp;size=m" style="zoom:50%;"> <p>否则直接填 convertx 所在的服务器 IP 就行。</p> <p>然后访问域名就可以访问了!</p> <h3 id="7-2-利用宝塔面板">7.2 利用宝塔面板</h3> <p>发现还是有不少小伙伴习惯用宝塔面板,这边也贴一个宝塔面板的反代配置:</p> <p>直接新建一个站点,不要数据库,不要 php,纯静态即可。</p> <p>然后打开下面的配置,修改 Nginx 的配置。</p> <img src="https://blog.laoda.de/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fimg.laoda.de%2Fi%2F2024%2F12%2F12%2F153816-1.webp&amp;size=m" alt="image-20220819150345725" style="zoom: 33%;"> <img src="https://blog.laoda.de/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fimg.laoda.de%2Fi%2F2024%2F12%2F12%2F153814-1.webp&amp;size=m" alt="image-20220819150542867" style="zoom: 33%;"> <p>代码如下:</p> <pre><code class="language-nginx">location / { proxy_pass http://127.0.0.1:3001/; # 注意改成你实际使用的端口 rewrite ^/(.*)$ /$1 break; proxy_redirect off; proxy_set_header Host $host; proxy_set_header X-Forwarded-Proto $scheme; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header Upgrade-Insecure-Requests 1; proxy_set_header X-Forwarded-Proto https; } </code></pre> <p>此方法对 90% 的反向代理都能生效,然后就可以用域名来安装访问了。</p> <p>有同学可能会问,为什么不直接用宝塔自带的反向代理功能。</p> <img src="https://blog.laoda.de/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fimg.laoda.de%2Fi%2F2024%2F12%2F12%2F153804-1.webp&amp;size=m" alt="image-20220819150730128" style="zoom: 33%;"> <p>也可以,不过咕咕自己之前遇到过当有多个网站需要反代的时候,在这边设置会报错的情况 = =</p> <p>所以后来就不用了,直接用上面的方法来操作了。</p> <h2 id="8--使用教程">8. 使用教程</h2> <p>非常简单,所见即所得。</p> <p>先注册账号:</p> <p><img src="https://blog.laoda.de/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fimg.laoda.de%2Fi%2F2026%2F01%2F20%2F150155-1.webp&amp;size=m" alt="45e7bc9343633faae0922c3f9a788670.png"></p> <p><img src="https://blog.laoda.de/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fimg.laoda.de%2Fi%2F2026%2F01%2F20%2F150153-1.webp&amp;size=m" alt="f1a7c788c803a41e6aa3ccb88607c5fd.png"></p> <p>JPG 转换成 webp</p> <p><img src="https://blog.laoda.de/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fimg.laoda.de%2Fi%2F2026%2F01%2F20%2F150151-1.webp&amp;size=m" alt="8475bb89d272567691366dd6c838f86b.png"></p> <p><img src="https://blog.laoda.de/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fimg.laoda.de%2Fi%2F2026%2F01%2F20%2F150148-1.webp&amp;size=m" alt="f65aa6b232020dc70071d0cea24f442c.png"></p> <p>MP4 转 AVI</p> <p><img src="https://blog.laoda.de/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fimg.laoda.de%2Fi%2F2026%2F01%2F20%2F150146-1.webp&amp;size=m" alt="2e655386026439a4f31bad8221504d7f.png"></p> <p>正常转换。</p> <p>转换历史:</p> <p><img src="https://blog.laoda.de/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fimg.laoda.de%2Fi%2F2026%2F01%2F20%2F150143-1.webp&amp;size=m" alt="11626ac66e09b45b5d7efe77b02e8d10.png"></p> <p>服务器里还是能看到文件的,</p> <p><img src="https://blog.laoda.de/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fimg.laoda.de%2Fi%2F2026%2F01%2F20%2F150132-1.webp&amp;size=m" alt="97c9f37455cfbabc34e0049b079356ef.png"></p> <h3 id="8-1-更新-convertx">8.1 更新 convertx</h3> <p>这个项目后续会持续有更新,所以提供一个更新的方式。</p> <pre><code class="language-yaml">cd /root/data/docker_data/convertx docker compose pull docker compose up -d # 请不要使用 docker compose stop 来停止容器,因为这么做需要额外的时间等待容器停止;docker compose up -d 直接升级容器时会自动停止并立刻重建新的容器,完全没有必要浪费那些时间。 docker image prune # prune 命令用来删除不再使用的 docker 对象。删除所有未被 tag 标记和未被容器使用的镜像 </code></pre> <p>提示:</p> <pre><code class="language-bash">WARNING! This will remove all dangling images. Are you sure you want to continue? [y/N] </code></pre> <p>输入 <code>y</code></p> <p>利用 Docker 搭建的应用,更新非常容易~</p> <h3 id="8-2-卸载-convertx">8.2 卸载 convertx</h3> <p>同样进入安装页面,先停止所有容器。</p> <pre><code class="language-bash">cd /root/data/docker_data/convertx docker compose down cd .. rm -rf /root/data/docker_data/convertx # 完全删除 </code></pre> <p>可以卸载得很干净。</p> <h2 id="9--使用问题">9. 使用问题</h2> <p>大家使用上有什么心得或者问题,欢迎评论区交流~</p> <h2 id="10--结尾">10. 结尾</h2> <p>祝大家用得开心,有问题可以去 GitHub 提 <a href="https://github.com/C4illin/ConvertX/issues">Issues</a>,也可以在评论区互相交流探讨。</p> <p>同时,有能力给项目做贡献的同学,也欢迎积极加入到 <a href="https://github.com/C4illin/ConvertX">项目</a> 中来,贡献自己的一份力量!</p> <p>最后,感谢作者 <a href="https://github.com/C4illin">@C4illin</a> 的辛苦付出,让我们能用到这么优秀的项目!欢迎大家都去给这个项目点个 star。</p> <h2 id="参考资料">参考资料</h2> <p>官方 GitHub:<a href="https://github.com/C4illin/ConvertX">https://github.com/C4illin/ConvertX</a></p>

2026/1/20
阅读更多

十分钟搭建一个 AI 驱动的图表生成器——Next-AI-Draw.io|好玩儿的 Docker 项目

<img src="https://blog.laoda.de/plugins/feed/assets/telemetry.gif?title=%E5%8D%81%E5%88%86%E9%92%9F%E6%90%AD%E5%BB%BA%E4%B8%80%E4%B8%AA%20AI%20%E9%A9%B1%E5%8A%A8%E7%9A%84%E5%9B%BE%E8%A1%A8%E7%94%9F%E6%88%90%E5%99%A8%E2%80%94%E2%80%94Next-AI-Draw.io%EF%BD%9C%E5%A5%BD%E7%8E%A9%E5%84%BF%E7%9A%84%20Docker%20%E9%A1%B9%E7%9B%AE&amp;url=/archives/docker-compose-install-next-ai-draw-io" width="1" height="1" alt="" style="opacity:0;"> <h2 id="1--唠嗑">1. 唠嗑</h2> <p>好久没更新了,这周来和大家分享一个 AI 驱动的图表生成器——Next-AI-Draw.io</p> <h2 id="2--Next-AI-Draw-io-简介">2. Next-AI-Draw.io 简介</h2> <p>Next-AI-Draw.io 是一个集成了 AI 功能的 Next.js 网页应用,与 draw.io 图表无缝结合。通过自然语言命令和 AI 辅助可视化来创建、修改和增强图表。</p> <p><img src="https://blog.laoda.de/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fimg.laoda.de%2Fi%2F2025%2F12%2F18%2F170220-1.webp&amp;size=m" alt="9dc17920a8529ad875088874df11e1c7.png"></p> <p><img src="https://blog.laoda.de/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fimg.laoda.de%2Fi%2F2025%2F12%2F18%2F170218-1.webp&amp;size=m" alt="0284f7edc67110d515a3edabf7ba418a.png"></p> <p><strong>功能特性</strong></p> <ul> <li><strong>LLM 驱动的图表创建</strong>:利用大语言模型通过自然语言命令直接创建和操作 draw.io 图表</li> <li><strong>基于图像的图表复制</strong>:上传现有图表或图像,让 AI 自动复制和增强</li> <li><strong>PDF 和文本文件上传</strong>:上传 PDF 文档和文本文件,提取内容并从现有文档生成图表</li> <li><strong>AI 推理过程显示</strong>:查看支持模型的 AI 思考过程(OpenAI o1/o3、Gemini、Claude 等)</li> <li><strong>图表历史记录</strong>:全面的版本控制,跟踪所有更改,允许您查看和恢复 AI 编辑前的图表版本</li> <li><strong>交互式聊天界面</strong>:与 AI 实时对话来完善您的图表</li> <li><strong>云架构图支持</strong>:专门支持生成云架构图(AWS、GCP、Azure)</li> <li><strong>动画连接器</strong>:在图表元素之间创建动态动画连接器,实现更好的可视化效果</li> </ul> <h2 id="3--相关地址">3. 相关地址</h2> <p>官方 GitHub 地址:<a href="https://github.com/DayuanJiang/next-ai-draw-io">https://github.com/DayuanJiang/next-ai-draw-io</a> (目前 13k 个 star,欢迎大家去给项目点星星!)</p> <p>Demo 地址:<a href="https://next-ai-drawio.jiang.jp/">https://next-ai-drawio.jiang.jp/</a></p> <p>当然如果你体验完还想自己用 docker 搭建一个,那我们就继续往下!</p> <h2 id="4--搭建环境">4. 搭建环境</h2> <ul> <li>服务器:咕咕这边用的香港的 <a href="http://gao.ee/lca">莱卡云服务器</a>,建议服务器内存 1G 以上,当然你也可以选择其他 <a href="https://blog.laoda.de/vps">高性价比的服务器</a>。</li> <li>系统:Debian 11 (<a href="https://blog.laoda.de/archives/useful-script/">DD 脚本</a> 非必需 DD,用原来的系统也 OK,之后教程都是用 Debian 或者 Ubuntu 搭建~)</li> <li>安装好 Docker、Docker-compose(<a href="https://blog.laoda.de/archives/hello-docker/">相关脚本</a>)</li> <li>【必需】域名一枚,并做好解析到服务器上(<a href="https://blog.laoda.de/archives/namesilo/">域名购买、域名解析</a> <a href="https://www.bilibili.com/video/BV1Sy4y1k7kZ/">视频教程</a>)</li> <li>【非必需】提前安装好宝塔面板海外版本 aapanel,并安装好 Nginx(<a href="https://forum.aapanel.com/d/9-aapanel-linux-panel-6812-installation-tutorial">安装地址</a>)</li> <li>【非必需本教程选用】安装好 Nginx Proxy Manager(<a href="https://blog.laoda.de/archives/nginxproxymanager/">相关教程</a>)</li> </ul> <h2 id="5--搭建视频-过俩周补充-----">5. 搭建视频(过俩周补充 = =)</h2> <h3 id="5-1-YouTube">5.1 YouTube</h3> <p>视频地址:</p> <h3 id="5-2-哔哩哔哩">5.2 哔哩哔哩</h3> <p>哔哩哔哩:</p> <h2 id="6--搭建方式">6. 搭建方式</h2> <h3 id="6-1-安装-Docker-与-Nginx-Proxy-Manager">6.1 安装 Docker 与 Nginx Proxy Manager</h3> <p>可以直接参考这篇内容:</p> <p><a href="https://blog.laoda.de/archives/nginxproxymanager/">https://blog.laoda.de/archives/nginxproxymanager/</a></p> <h3 id="6-2-创建安装目录">6.2 创建安装目录</h3> <p>创建一下安装的目录:</p> <pre><code class="language-bash">sudo -i mkdir -p /root/data/docker_data/drawioai cd /root/data/docker_data/drawioai </code></pre> <p>接着我们来编辑下<code>docker-compose.yml</code></p> <pre><code class="language-bash">vim docker-compose.yml </code></pre> <pre><code class="language-yaml">name: nextaidrawio services: next-ai-draw-io: ports: - 3001:3000 env_file: - .env image: ghcr.io/dayuanjiang/next-ai-draw-io:latest </code></pre> <p>其中的左边的<code>3001</code>可以改成服务器上没有用过的端口。</p> <p>修改完成之后,可以在英文输入法下,按 <code>i</code> 修改,完成之后,按一下 <code>esc</code>,然后 <code>:wq</code> 保存退出。</p> <p>接着我们来编辑下<code>.env</code></p> <pre><code class="language-bash">cd /root/data/docker_data/drawioai vim .env </code></pre> <p>粘贴如下内容:</p> <pre><code class="language-env"># AI Provider Configuration # AI_PROVIDER: Which provider to use # Options: bedrock, openai, anthropic, google, azure, ollama, openrouter, deepseek, siliconflow, gateway # Default: bedrock AI_PROVIDER=bedrock # AI_MODEL: The model ID for your chosen provider (REQUIRED) AI_MODEL=global.anthropic.claude-sonnet-4-5-20250929-v1:0 # AWS Bedrock Configuration # AWS_REGION=us-east-1 # AWS_ACCESS_KEY_ID=your-access-key-id # AWS_SECRET_ACCESS_KEY=your-secret-access-key # Note: Claude and Nova models support reasoning/extended thinking # BEDROCK_REASONING_BUDGET_TOKENS=12000 # Optional: Claude reasoning budget in tokens (1024-64000) # BEDROCK_REASONING_EFFORT=medium # Optional: Nova reasoning effort (low/medium/high) # OpenAI Configuration # OPENAI_API_KEY=sk-... # OPENAI_BASE_URL=https://api.openai.com/v1 # Optional: Custom OpenAI-compatible endpoint # OPENAI_ORGANIZATION=org-... # Optional # OPENAI_PROJECT=proj_... # Optional # Note: o1/o3/gpt-5 models automatically enable reasoning summary (default: detailed) # OPENAI_REASONING_EFFORT=low # Optional: Reasoning effort (minimal/low/medium/high) - for o1/o3/gpt-5 # OPENAI_REASONING_SUMMARY=detailed # Optional: Override reasoning summary (none/brief/detailed) # Anthropic (Direct) Configuration # ANTHROPIC_API_KEY=sk-ant-... # ANTHROPIC_BASE_URL=https://your-custom-anthropic/v1 # ANTHROPIC_THINKING_TYPE=enabled # Optional: Anthropic extended thinking (enabled) # ANTHROPIC_THINKING_BUDGET_TOKENS=12000 # Optional: Budget for extended thinking in tokens # Google Generative AI Configuration # GOOGLE_GENERATIVE_AI_API_KEY=... # GOOGLE_BASE_URL=https://generativelanguage.googleapis.com/v1beta # Optional: Custom endpoint # GOOGLE_CANDIDATE_COUNT=1 # Optional: Number of candidates to generate # GOOGLE_TOP_K=40 # Optional: Top K sampling parameter # GOOGLE_TOP_P=0.95 # Optional: Nucleus sampling parameter # Note: Gemini 2.5/3 models automatically enable reasoning display (includeThoughts: true) # GOOGLE_THINKING_BUDGET=8192 # Optional: Gemini 2.5 thinking budget in tokens (for more/less thinking) # GOOGLE_THINKING_LEVEL=high # Optional: Gemini 3 thinking level (low/high) # Azure OpenAI Configuration # Configure endpoint using ONE of these methods: # 1. AZURE_RESOURCE_NAME - SDK constructs: https://{name}.openai.azure.com/openai/v1{path} # 2. AZURE_BASE_URL - SDK appends /v1{path} to your URL # If both are set, AZURE_BASE_URL takes precedence. # AZURE_RESOURCE_NAME=your-resource-name # AZURE_API_KEY=... # AZURE_BASE_URL=https://your-resource.openai.azure.com/openai # Alternative: Custom endpoint # AZURE_REASONING_EFFORT=low # Optional: Azure reasoning effort (low, medium, high) # AZURE_REASONING_SUMMARY=detailed # Ollama (Local) Configuration # OLLAMA_BASE_URL=http://localhost:11434/api # Optional, defaults to localhost # OLLAMA_ENABLE_THINKING=true # Optional: Enable thinking for models that support it (e.g., qwen3) # OpenRouter Configuration # OPENROUTER_API_KEY=sk-or-v1-... # OPENROUTER_BASE_URL=https://openrouter.ai/api/v1 # Optional: Custom endpoint # DeepSeek Configuration # DEEPSEEK_API_KEY=sk-... # DEEPSEEK_BASE_URL=https://api.deepseek.com/v1 # Optional: Custom endpoint # SiliconFlow Configuration (OpenAI-compatible) # Base domain can be .com or .cn, defaults to https://api.siliconflow.com/v1 # SILICONFLOW_API_KEY=sk-... # SILICONFLOW_BASE_URL=https://api.siliconflow.com/v1 # Optional: switch to https://api.siliconflow.cn/v1 if needed # Vercel AI Gateway Configuration # Get your API key from: https://vercel.com/ai-gateway # Model format: "provider/model" e.g., "openai/gpt-4o", "anthropic/claude-sonnet-4-5" # AI_GATEWAY_API_KEY=... # Langfuse Observability (Optional) # Enable LLM tracing and analytics - https://langfuse.com # LANGFUSE_PUBLIC_KEY=pk-lf-... # LANGFUSE_SECRET_KEY=sk-lf-... # LANGFUSE_BASEURL=https://cloud.langfuse.com # EU region, use https://us.cloud.langfuse.com for US # Temperature (Optional) # Controls randomness in AI responses. Lower = more deterministic. # Leave unset for models that don't support temperature (e.g., GPT-5.1 reasoning models) # TEMPERATURE=0 # Access Control (Optional) # ACCESS_CODE_LIST=your-secret-code,another-code # Draw.io Configuration (Optional) # NEXT_PUBLIC_DRAWIO_BASE_URL=https://embed.diagrams.net # Default: https://embed.diagrams.net # Use this to point to a self-hosted draw.io instance # PDF Input Feature (Optional) # Enable PDF file upload to extract text and generate diagrams # Enabled by default. Set to "false" to disable. # ENABLE_PDF_INPUT=true # NEXT_PUBLIC_MAX_EXTRACTED_CHARS=150000 # Max characters for PDF/text extraction (default: 150000) </code></pre> <p>这里头有很多选项可以开启,当然你也可以不管它,到网页端输入,这样的话就保存在本地浏览器里了,如果在这边输入,所有知道这个网址的人都能用你的 AI 模型。</p> <p>假设我们用的 OPEN AI 的,其实<code>.env</code>里面只要这仨:</p> <pre><code class="language-env">AI_PROVIDER=openai AI_MODEL=gpt-4o OPENAI_API_KEY=your_api_key </code></pre> <p><code>your_api_key</code>改成你的 API 密钥。</p> <p>修改完成之后,可以在英文输入法下,按 <code>i</code> 修改,完成之后,按一下 <code>esc</code>,然后 <code>:wq</code> 保存退出。</p> <h3 id="6-3-查看端口是否被占用">6.3 查看端口是否被占用</h3> <p>查看端口是否被占用(以 <code>3001</code> 为例),输入:</p> <pre><code class="language-bash">lsof -i:3001 #查看 3001 端口是否被占用,如果被占用,重新自定义一个端口 </code></pre> <p>如果啥也没出现,表示端口未被占用,我们可以继续下面的操作了~</p> <p>如果出现:</p> <pre><code class="language-bash">-bash: lsof: command not found </code></pre> <p>运行:</p> <pre><code class="language-bash">apt install lsof #安装 lsof </code></pre> <p>如果端口没有被占用(被占用了就修改一下端口,比如改成 <code>8381</code>,注意 docker 命令行里和防火墙都要改)</p> <h3 id="6-4-启动-drawioai">6.4 启动 drawioai</h3> <pre><code>cd /root/data/docker_data/drawioai docker compose up -d # 注意,老版本用户用 docker-compose up -d </code></pre> <p>耐心等待拉取好镜像,出现 <code>done</code>的字样之后,</p> <p>理论上我们就可以输入 <code>http://ip:3001</code> 访问了。</p> <p>但是我们部署在公网一定要考虑使用反向代理工具配置 SSL!</p> <p>做反向代理前,你需要一个域名!</p> <p><a href="https://loll.cc/ns">namesilo</a> 上面 xyz 后缀的域名一年就 7 块钱,可以年抛。(冷知识,namesilo 上 6 位数字的 xyz 续费永远都是 0.99 美元 = =)</p> <p>如果想要长期使用,还是建议买 com 后缀的域名,更加正规一些,可以输入 <code>laodade</code> 来获得 1 美元的优惠(不知道现在还有没有)</p> <p><a href="https://loll.cc/ns">namesilo</a> 自带隐私保护,咕咕一直在用这家,价格也是这些注册商里面比较低的,关键是他家不像其他家域名注册商,没有七七八八的套路!(就是后台界面有些 <del>丑</del> 古老 = =)</p> <p><a href="https://blog.laoda.de/archives/namesilo/">【域名购买】Namesilo 优惠码和域名解析教程(附带服务器购买推荐和注意事项)</a></p> <p>我们接着往下看!</p> <h2 id="7--反向代理">7. 反向代理</h2> <h3 id="7-1-利用-Nginx-Proxy-Manager">7.1 利用 Nginx Proxy Manager</h3> <p>在添加反向代理之前,确保你已经完成了域名解析,不会的可以看这个:<strong>域名一枚,并做好解析到服务器上</strong>(<a href="https://blog.laoda.de/archives/namesilo/">域名购买、域名解析</a> <a href="https://www.bilibili.com/video/BV1Sy4y1k7kZ/">视频教程</a>) (名称改成你自己想要的域名前缀即可)</p> <p><img src="https://blog.laoda.de/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fimg.laoda.de%2Fi%2F2025%2F12%2F18%2F170211-1.webp&amp;size=m" alt="1c160f28afd3549159df3efa2507ad0c.png"></p> <p>之后,登陆 Nginx Proxy Manager(不会的看这个:<strong>安装 Nginx Proxy Manager</strong>(<a href="https://blog.laoda.de/archives/nginxproxymanager/">相关教程</a>))</p> <blockquote> <p><strong>注意:</strong></p> <p>Nginx Proxy Manager(以下简称 NPM)会用到 <code>80</code>、<code>443</code> 端口,所以本机不能占用(比如原来就有 Nginx)</p> </blockquote> <p>直接丢几张图:</p> <img src="https://blog.laoda.de/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fimg.laoda.de%2Fi%2F2025%2F12%2F18%2F170209-1.webp&amp;size=m" alt="c359c92172657c5667c6a1738b214511.png" style="zoom:50%;"> <img src="https://blog.laoda.de/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fimg.laoda.de%2Fi%2F2025%2F12%2F18%2F170200-1.webp&amp;size=m" alt="940b5b933d91851bb44423d5bfc3f628.png" style="zoom:50%;"> <img src="https://blog.laoda.de/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fimg.laoda.de%2Fi%2F2025%2F12%2F18%2F170156-1.webp&amp;size=m" alt="46b8997021848567235e69ee8b0be54f.png" style="zoom:50%;"> <img src="https://blog.laoda.de/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fimg.laoda.de%2Fi%2F2025%2F12%2F18%2F170151-1.webp&amp;size=m" alt="e2fed6006e075057bacdb57857e516b7.png" style="zoom: 50%;"> <blockquote> <p>注意填写对应的 <code>域名</code>、<code>IP</code> 和 <code>端口</code>,按文章来的话,应该是 <code>3001</code></p> </blockquote> <p><strong>IP 填写:</strong></p> <p>如果 Nginx Proxy Manager 和 drawioai 在同一台服务器上,可以在终端输入:</p> <pre><code class="language-bash">ip addr show docker0 </code></pre> <p>查看对应的 Docker 容器内部 IP。</p> <img src="https://blog.laoda.de/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fimg.laoda.de%2Fi%2F2024%2F12%2F12%2F153819-1.webp&amp;size=m" style="zoom:50%;"> <p>否则直接填 drawioai 所在的服务器 IP 就行。</p> <p>然后访问域名就可以访问了!</p> <h3 id="7-2-利用宝塔面板">7.2 利用宝塔面板</h3> <p>发现还是有不少小伙伴习惯用宝塔面板,这边也贴一个宝塔面板的反代配置:</p> <p>直接新建一个站点,不要数据库,不要 php,纯静态即可。</p> <p>然后打开下面的配置,修改 Nginx 的配置。</p> <img src="https://blog.laoda.de/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fimg.laoda.de%2Fi%2F2024%2F12%2F12%2F153816-1.webp&amp;size=m" alt="image-20220819150345725" style="zoom: 33%;"> <img src="https://blog.laoda.de/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fimg.laoda.de%2Fi%2F2024%2F12%2F12%2F153814-1.webp&amp;size=m" alt="image-20220819150542867" style="zoom: 33%;"> <p>代码如下:</p> <pre><code class="language-nginx">location / { proxy_pass http://127.0.0.1:3001/; # 注意改成你实际使用的端口 rewrite ^/(.*)$ /$1 break; proxy_redirect off; proxy_set_header Host $host; proxy_set_header X-Forwarded-Proto $scheme; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header Upgrade-Insecure-Requests 1; proxy_set_header X-Forwarded-Proto https; } </code></pre> <p>此方法对 90% 的反向代理都能生效,然后就可以用域名来安装访问了。</p> <p>有同学可能会问,为什么不直接用宝塔自带的反向代理功能。</p> <img src="https://blog.laoda.de/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fimg.laoda.de%2Fi%2F2024%2F12%2F12%2F153804-1.webp&amp;size=m" alt="image-20220819150730128" style="zoom: 33%;"> <p>也可以,不过咕咕自己之前遇到过当有多个网站需要反代的时候,在这边设置会报错的情况 = =</p> <p>所以后来就不用了,直接用上面的方法来操作了。</p> <h2 id="8--使用教程">8. 使用教程</h2> <p>非常简单,所见即所得。</p> <p>登录页,</p> <p><img src="https://blog.laoda.de/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fimg.laoda.de%2Fi%2F2025%2F12%2F18%2F171446.webp&amp;size=m" alt="ccd17e50213ceede7348a39b298f3085.png"></p> <p>语言设置,改成中文,</p> <p><img src="https://blog.laoda.de/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fimg.laoda.de%2Fi%2F2025%2F12%2F18%2F171509.webp&amp;size=m" alt="b0257ee37eafc1d9f205cf1858ab48fd.png"></p> <p>简单展示</p> <p><img src="https://blog.laoda.de/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fimg.laoda.de%2Fi%2F2025%2F12%2F18%2F171532.webp&amp;size=m" alt="fead7ea8e6e781129d1c91dd03ee7b02.png"></p> <p>右边可以输入提示词,比如让它画一只猫猫,</p> <p><img src="https://blog.laoda.de/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fimg.laoda.de%2Fi%2F2025%2F12%2F18%2F171552.webp&amp;size=m" alt="02798e80fde95bc63142b86cfc83dc33.png"></p> <p>画的熊猫不太像……</p> <p><img src="https://blog.laoda.de/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fimg.laoda.de%2Fi%2F2025%2F12%2F18%2F170127-1.webp&amp;size=m" alt="b86441ae7cc00d5bab3ae0032ffa9cad.png"></p> <p>当然你也可以隐藏掉这个提示词输入框,把它当成一个普通的画图软件来用,</p> <p><img src="https://blog.laoda.de/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fimg.laoda.de%2Fi%2F2025%2F12%2F18%2F170026-1.webp&amp;size=m" alt="b821ac8ceac261079ae31b3998e2b55c.png"></p> <p>如果用默认的,其实是用的项目作者的 API,速率可能会有限制,当然用自己的 API 就没有这个问题了,如果手上富裕,也可以考虑捐赠作者,支持开源项目长久发展。</p> <p><img src="https://blog.laoda.de/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fimg.laoda.de%2Fi%2F2025%2F12%2F18%2F170603.webp&amp;size=m" alt="727fd49d8064f8e7fae8d934daf5183f.png"></p> <h3 id="8-1-更新-drawioai">8.1 更新 drawioai</h3> <p>这个项目后续会持续有更新,所以提供一个更新的方式。</p> <pre><code class="language-yaml">cd /root/data/docker_data/drawioai docker compose pull docker compose up -d # 请不要使用 docker compose stop 来停止容器,因为这么做需要额外的时间等待容器停止;docker compose up -d 直接升级容器时会自动停止并立刻重建新的容器,完全没有必要浪费那些时间。 docker image prune # prune 命令用来删除不再使用的 docker 对象。删除所有未被 tag 标记和未被容器使用的镜像 </code></pre> <p>提示:</p> <pre><code class="language-bash">WARNING! This will remove all dangling images. Are you sure you want to continue? [y/N] </code></pre> <p>输入 <code>y</code></p> <p>利用 Docker 搭建的应用,更新非常容易~</p> <h3 id="8-2-卸载-drawioai">8.2 卸载 drawioai</h3> <p>同样进入安装页面,先停止所有容器。</p> <pre><code class="language-bash">cd /root/data/docker_data/drawioai docker compose down cd .. rm -rf /root/data/docker_data/drawioai # 完全删除 </code></pre> <p>可以卸载得很干净。</p> <h2 id="9--使用问题">9. 使用问题</h2> <p>那个保存按钮点了似乎没有任何反馈……</p> <p>不过画的图导出是没问题的。</p> <p>大家使用上有什么心得或者问题,欢迎评论区交流~</p> <h2 id="10--结尾">10. 结尾</h2> <p>祝大家用得开心,有问题可以去 GitHub 提 <a href="https://github.com/DayuanJiang/next-ai-draw-io/issues">Issues</a>,也可以在评论区互相交流探讨。</p> <p>同时,有能力给项目做贡献的同学,也欢迎积极加入到 <a href="https://github.com/DayuanJiang/next-ai-draw-io">项目</a> 中来,贡献自己的一份力量!</p> <p>最后,感谢作者 <a href="https://github.com/DayuanJiang?tab=repositories">@DayuanJiang</a> 的辛苦付出,让我们能用到这么优秀的项目!欢迎大家都去给这个项目点个 star。</p> <h2 id="参考资料">参考资料</h2> <p>官方 GitHub:<a href="https://github.com/DayuanJiang/next-ai-draw-io">https://github.com/DayuanJiang/next-ai-draw-io</a></p>

2025/12/18
阅读更多

十分钟搭建一个「万物皆可存」的智能书签应用——karakeep|好玩儿的 Docker 项目

<img src="https://blog.laoda.de/plugins/feed/assets/telemetry.gif?title=%E5%8D%81%E5%88%86%E9%92%9F%E6%90%AD%E5%BB%BA%E4%B8%80%E4%B8%AA%E3%80%8C%E4%B8%87%E7%89%A9%E7%9A%86%E5%8F%AF%E5%AD%98%E3%80%8D%E7%9A%84%E6%99%BA%E8%83%BD%E4%B9%A6%E7%AD%BE%E5%BA%94%E7%94%A8%E2%80%94%E2%80%94karakeep%EF%BD%9C%E5%A5%BD%E7%8E%A9%E5%84%BF%E7%9A%84%20Docker%20%E9%A1%B9%E7%9B%AE&amp;url=/archives/docker-compose-install-karakeep" width="1" height="1" alt="" style="opacity:0;"> <h2 id="1--唠嗑">1. 唠嗑</h2> <p>好久没更新了,这周来和大家分享一个好用的书签工具——Karakeep</p> <h2 id="2--karakeep-简介">2. karakeep 简介</h2> <p>Karakeep(前身为 Hoarder)是一款可自托管的「万物皆可存」书签应用,专为数据囤积爱好者打造,融入一丝 AI 魔法。</p> <p><img src="https://blog.laoda.de/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fimg.laoda.de%2Fi%2F2025%2F11%2F13%2F094935-1.webp&amp;size=m" alt="b877b9e2ebadea6bd7eb91991a4744e4.png"></p> <p><strong>功能</strong></p> <ul> <li>🔗 书签链接、记录简单笔记,并存储图片和 PDF 文件。</li> <li>⬇️ 自动抓取链接标题、描述和预览图。</li> <li>📋 将书签整理到不同列表中。</li> <li>🔎 全文本搜索所有存储内容。</li> <li>✨ 基于 AI(类似 ChatGPT)自动打标签和生成摘要。支持使用 Ollama 运行本地模型!</li> <li>🤖 规则引擎,实现自定义管理。</li> <li>🎆 图像 OCR,提取图片中的文字。</li> <li>🔖 Chrome 插件和 Firefox 扩展,快速添加书签。</li> <li>📱 iOS 应用和 Android 应用。</li> <li>📰 从 RSS 订阅源自动归档内容。</li> <li>🔌 REST API 和多种客户端。</li> <li>🌐 多语言支持。</li> <li>🖍️ 标记并保存归档内容中的高亮片段。</li> <li>🗄️ 完整页面归档(使用 monolith),防止链接失效。</li> <li>▶️ 使用 yt-dlp 自动归档视频。</li> <li>☑️ 支持批量操作。</li> <li>🔐 支持 SSO 单点登录。</li> <li>🌙 深色模式。</li> <li>💾 优先支持自托管。</li> <li>⬇️ 从 Chrome、Pocket、Linkwarden、Omnivore、Tab Session Manager 导入书签。</li> <li>🔄 通过 floccus 与浏览器书签自动同步。</li> <li>[计划中] 移动端离线阅读、书签语义搜索,……</li> </ul> <p><img src="https://blog.laoda.de/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fimg.laoda.de%2Fi%2F2025%2F11%2F13%2F094930-1.webp&amp;size=m" alt="6fb0d16e6fa436a7fceaa95125fee34d.png"></p> <h2 id="3--相关地址">3. 相关地址</h2> <p>官方 GitHub 地址:<a href="https://github.com/karakeep-app/karakeep">https://github.com/karakeep-app/karakeep</a> (目前 21.1k 个 star,欢迎大家去给项目点星星!)</p> <p>Demo 地址:<a href="https://try.karakeep.app/signin">https://try.karakeep.app</a></p> <p>默认账号密码:</p> <p>Email: <code>demo@karakeep.app</code></p> <p>Password: <code>demodemo</code></p> <p>当然如果你体验完还想自己用 docker 搭建一个,那我们就继续往下!</p> <h2 id="4--搭建环境">4. 搭建环境</h2> <ul> <li>服务器:咕咕这边用的香港的 <a href="http://gao.ee/lca">莱卡云服务器</a>,建议服务器内存 1G 以上,当然你也可以选择其他 <a href="https://blog.laoda.de/vps">高性价比的服务器</a>。</li> <li>系统:Debian 11 (<a href="https://blog.laoda.de/archives/useful-script/">DD 脚本</a> 非必需 DD,用原来的系统也 OK,之后教程都是用 Debian 或者 Ubuntu 搭建~)</li> <li>安装好 Docker、Docker-compose(<a href="https://blog.laoda.de/archives/hello-docker/">相关脚本</a>)</li> <li>【必需】域名一枚,并做好解析到服务器上(<a href="https://blog.laoda.de/archives/namesilo/">域名购买、域名解析</a> <a href="https://www.bilibili.com/video/BV1Sy4y1k7kZ/">视频教程</a>)</li> <li>【非必需】提前安装好宝塔面板海外版本 aapanel,并安装好 Nginx(<a href="https://forum.aapanel.com/d/9-aapanel-linux-panel-6812-installation-tutorial">安装地址</a>)</li> <li>【非必需本教程选用】安装好 Nginx Proxy Manager(<a href="https://blog.laoda.de/archives/nginxproxymanager/">相关教程</a>)</li> </ul> <h2 id="5--搭建视频-过俩周补充-----">5. 搭建视频(过俩周补充 = =)</h2> <h3 id="5-1-YouTube">5.1 YouTube</h3> <p>视频地址:</p> <h3 id="5-2-哔哩哔哩">5.2 哔哩哔哩</h3> <p>哔哩哔哩:</p> <h2 id="6--搭建方式">6. 搭建方式</h2> <h3 id="6-1-安装-Docker-与-Nginx-Proxy-Manager">6.1 安装 Docker 与 Nginx Proxy Manager</h3> <p>可以直接参考这篇内容:</p> <p><a href="https://blog.laoda.de/archives/nginxproxymanager/">https://blog.laoda.de/archives/nginxproxymanager/</a></p> <h3 id="6-2-创建安装目录">6.2 创建安装目录</h3> <p>创建一下安装的目录:</p> <pre><code class="language-bash">sudo -i mkdir -p /root/data/docker_data/karakeep cd /root/data/docker_data/karakeep </code></pre> <p>接着我们来编辑下<code>docker-compose.yml</code></p> <pre><code class="language-bash">vim docker-compose.yml </code></pre> <pre><code class="language-yaml">services: web: image: ghcr.io/karakeep-app/karakeep:latest restart: unless-stopped volumes: # By default, the data is stored in a docker volume called "data". # If you want to mount a custom directory, change the volume mapping to: # - /path/to/your/directory:/data - ./data:/data ports: - 3030:3000 env_file: - .env environment: MEILI_ADDR: http://meilisearch:7700 BROWSER_WEB_URL: http://chrome:9222 # OPENAI_API_KEY: ... # You almost never want to change the value of the DATA_DIR variable. # If you want to mount a custom directory, change the volume mapping above instead. DATA_DIR: /data # DON'T CHANGE THIS chrome: image: gcr.io/zenika-hub/alpine-chrome:124 restart: unless-stopped command: - --no-sandbox - --disable-gpu - --disable-dev-shm-usage - --remote-debugging-address=0.0.0.0 - --remote-debugging-port=9222 - --hide-scrollbars meilisearch: image: getmeili/meilisearch:v1.13.3 restart: unless-stopped env_file: - .env environment: MEILI_NO_ANALYTICS: "true" volumes: - ./meilisearch:/meili_data </code></pre> <p>其中的左边的<code>3030</code>可以改成服务器上没有用过的端口,如果要用到AI功能的话,可以取消<code>OPENAI_API_KEY</code>的注释,填入自己的密钥。</p> <p>修改完成之后,可以在英文输入法下,按 <code>i</code> 修改,完成之后,按一下 <code>esc</code>,然后 <code>:wq</code> 保存退出。</p> <p>接着我们来编辑下<code>.env</code></p> <pre><code class="language-bash">cd /root/data/docker_data/karakeep vim .env </code></pre> <p>粘贴如下内容:</p> <pre><code class="language-env">KARAKEEP_VERSION=release NEXTAUTH_SECRET=super_random_string MEILI_MASTER_KEY=another_random_string NEXTAUTH_URL=http://localhost:3000 </code></pre> <ul> <li>其中的<code>http://localhost:3000</code>这个改成你自己之后用域名访问的地址,比如我的<code>https://karakeep.gugu.ovh</code></li> <li>其中的<code>super_random_string</code>和<code>another_random_string</code>需要我们自己生成。</li> </ul> <p>我们可以先在命令行里面输入</p> <pre><code class="language-bash">openssl rand -base64 36 </code></pre> <p>生成随机的字符,然后替代它们。</p> <img src="https://blog.laoda.de/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fimg.laoda.de%2Fi%2F2025%2F11%2F13%2F094926-1.webp&amp;size=m" alt="aebf462264a0d4c95fa1416bee41830a.png" style="zoom:50%;"> <p>我需要放在<code>.env</code>里的内容就是:</p> <pre><code class="language-env">KARAKEEP_VERSION=release NEXTAUTH_SECRET=Lu4YuG5pXji2/QB6blXaoWnfK2WdtKifPhEBt0UQCHqkxCAe MEILI_MASTER_KEY=wJ222cTFWJ0/9Jnu38gU9Wb+AdCk2SBmQ687MdTbxT8c/V0b NEXTAUTH_URL=https://karakeep.gugu.ovh </code></pre> <p>修改完成之后,同样的,可以在英文输入法下,按 <code>i</code> 修改,完成之后,按一下 <code>esc</code>,然后 <code>:wq</code> 保存退出。</p> <h3 id="6-3-查看端口是否被占用">6.3 查看端口是否被占用</h3> <p>查看端口是否被占用(以 <code>3030</code> 为例),输入:</p> <pre><code class="language-bash">lsof -i:3030 #查看 3030 端口是否被占用,如果被占用,重新自定义一个端口 </code></pre> <p>如果啥也没出现,表示端口未被占用,我们可以继续下面的操作了~</p> <p>如果出现:</p> <pre><code class="language-bash">-bash: lsof: command not found </code></pre> <p>运行:</p> <pre><code class="language-bash">apt install lsof #安装 lsof </code></pre> <p>如果端口没有被占用(被占用了就修改一下端口,比如改成 <code>8381</code>,注意 docker 命令行里和防火墙都要改)</p> <h3 id="6-4-启动-karakeep">6.4 启动 karakeep</h3> <pre><code>cd /root/data/docker_data/karakeep docker compose up -d # 注意,老版本用户用 docker-compose up -d </code></pre> <p>耐心等待拉取好镜像,出现 <code>done</code>的字样之后,</p> <p>理论上我们就可以输入 <code>http://ip:3030</code> 访问了。</p> <p>但是这边这个服务必须先搞一下反向代理!既然是加密的项目,不能用 http 使用明文传输,我们部署在公网一定要考虑使用反向代理工具配置 SSL!</p> <p>做反向代理前,你需要一个域名!</p> <p><a href="https://loll.cc/ns">namesilo</a> 上面 xyz 后缀的域名一年就 7 块钱,可以年抛。(冷知识,namesilo 上 6 位数字的 xyz 续费永远都是 0.99 美元 = =)</p> <p>如果想要长期使用,还是建议买 com 后缀的域名,更加正规一些,可以输入 <code>laodade</code> 来获得 1 美元的优惠(不知道现在还有没有)</p> <p><a href="https://loll.cc/ns">namesilo</a> 自带隐私保护,咕咕一直在用这家,价格也是这些注册商里面比较低的,关键是他家不像其他家域名注册商,没有七七八八的套路!(就是后台界面有些 <del>丑</del> 古老 = =)</p> <p><a href="https://blog.laoda.de/archives/namesilo/">【域名购买】Namesilo 优惠码和域名解析教程(附带服务器购买推荐和注意事项)</a></p> <p>我们接着往下看!</p> <h2 id="7--反向代理">7. 反向代理</h2> <h3 id="7-1-利用-Nginx-Proxy-Manager">7.1 利用 Nginx Proxy Manager</h3> <p>在添加反向代理之前,确保你已经完成了域名解析,不会的可以看这个:<strong>域名一枚,并做好解析到服务器上</strong>(<a href="https://blog.laoda.de/archives/namesilo/">域名购买、域名解析</a> <a href="https://www.bilibili.com/video/BV1Sy4y1k7kZ/">视频教程</a>) (名称改成你自己想要的域名前缀即可)</p> <p><img src="https://blog.laoda.de/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fimg.laoda.de%2Fi%2F2025%2F11%2F13%2F094916-1.webp&amp;size=m" alt="8a522c57d08898256ce3814d5183f811.png"></p> <p>之后,登陆 Nginx Proxy Manager(不会的看这个:<strong>安装 Nginx Proxy Manager</strong>(<a href="https://blog.laoda.de/archives/nginxproxymanager/">相关教程</a>))</p> <blockquote> <p><strong>注意:</strong></p> <p>Nginx Proxy Manager(以下简称 NPM)会用到 <code>80</code>、<code>443</code> 端口,所以本机不能占用(比如原来就有 Nginx)</p> </blockquote> <p>直接丢几张图:</p> <img src="https://blog.laoda.de/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fimg.laoda.de%2Fi%2F2025%2F11%2F13%2F094914-1.webp&amp;size=m" alt="deeedad5afbbcc25441ac1a41e89ba31.png" style="zoom:50%;"> <img src="https://blog.laoda.de/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fimg.laoda.de%2Fi%2F2025%2F11%2F13%2F094909-1.webp&amp;size=m" alt="cf5928e754752471d09460cd31fb2eae.png" style="zoom:50%;"> <img src="https://blog.laoda.de/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fimg.laoda.de%2Fi%2F2025%2F11%2F13%2F094905-1.webp&amp;size=m" alt="832a857e518a6999f692c56a1b0d6d18.png" style="zoom:50%;"> <img src="https://blog.laoda.de/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fimg.laoda.de%2Fi%2F2025%2F11%2F13%2F094900-1.webp&amp;size=m" alt="bd08eed12b655993b53b6df83bcb6606.png" style="zoom:50%;"> <img src="https://blog.laoda.de/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fimg.laoda.de%2Fi%2F2025%2F11%2F13%2F094853-1.webp&amp;size=m" alt="ec68bb8adeb0d9661917ba2e77032906.png" style="zoom:50%;"> <blockquote> <p>注意填写对应的 <code>域名</code>、<code>IP</code> 和 <code>端口</code>,按文章来的话,应该是 <code>3030</code></p> </blockquote> <p><strong>IP 填写:</strong></p> <p>如果 Nginx Proxy Manager 和 karakeep 在同一台服务器上,可以在终端输入:</p> <pre><code class="language-bash">ip addr show docker0 </code></pre> <p>查看对应的 Docker 容器内部 IP。</p> <img src="https://blog.laoda.de/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fimg.laoda.de%2Fi%2F2024%2F12%2F12%2F153819-1.webp&amp;size=m" style="zoom:50%;"> <p>否则直接填 karakeep 所在的服务器 IP 就行。</p> <p>然后访问域名就可以访问了!</p> <h3 id="7-2-利用宝塔面板">7.2 利用宝塔面板</h3> <p>发现还是有不少小伙伴习惯用宝塔面板,这边也贴一个宝塔面板的反代配置:</p> <p>直接新建一个站点,不要数据库,不要 php,纯静态即可。</p> <p>然后打开下面的配置,修改 Nginx 的配置。</p> <img src="https://blog.laoda.de/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fimg.laoda.de%2Fi%2F2024%2F12%2F12%2F153816-1.webp&amp;size=m" alt="image-20220819150345725" style="zoom: 33%;"> <img src="https://blog.laoda.de/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fimg.laoda.de%2Fi%2F2024%2F12%2F12%2F153814-1.webp&amp;size=m" alt="image-20220819150542867" style="zoom: 33%;"> <p>代码如下:</p> <pre><code class="language-nginx">location / { proxy_pass http://127.0.0.1:3030/; # 注意改成你实际使用的端口 rewrite ^/(.*)$ /$1 break; proxy_redirect off; proxy_set_header Host $host; proxy_set_header X-Forwarded-Proto $scheme; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header Upgrade-Insecure-Requests 1; proxy_set_header X-Forwarded-Proto https; } </code></pre> <p>此方法对 90% 的反向代理都能生效,然后就可以用域名来安装访问了。</p> <p>有同学可能会问,为什么不直接用宝塔自带的反向代理功能。</p> <img src="https://blog.laoda.de/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fimg.laoda.de%2Fi%2F2024%2F12%2F12%2F153804-1.webp&amp;size=m" alt="image-20220819150730128" style="zoom: 33%;"> <p>也可以,不过咕咕自己之前遇到过当有多个网站需要反代的时候,在这边设置会报错的情况 = =</p> <p>所以后来就不用了,直接用上面的方法来操作了。</p> <h2 id="8--使用教程">8. 使用教程</h2> <p>非常简单,所见即所得。</p> <p>登录,</p> <img src="https://blog.laoda.de/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fimg.laoda.de%2Fi%2F2025%2F11%2F13%2F100639-1.webp&amp;size=m" alt="32cb2fe6c8694197a931f84b06e6931d.png" style="zoom:33%;"> <p>首先注册一个账号,</p> <img src="https://blog.laoda.de/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fimg.laoda.de%2Fi%2F2025%2F11%2F13%2F100634-1.webp&amp;size=m" alt="e04dc92bb6e9435aaebfc5d43eb4f56f.png" style="zoom: 33%;"> <p>后台页面,</p> <p><img src="https://blog.laoda.de/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fimg.laoda.de%2Fi%2F2025%2F11%2F13%2F094841-1.webp&amp;size=m" alt="2069ec87e83c0c1ad747046940e44717.png"></p> <p>用户设置,</p> <img src="https://blog.laoda.de/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fimg.laoda.de%2Fi%2F2025%2F11%2F13%2F094838-1.webp&amp;size=m" alt="213472f4fcb8caa51ae8344d98424a60.png" style="zoom:50%;"> <p>如果觉得英文不习惯,也可以改成中文,</p> <p><img src="https://blog.laoda.de/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fimg.laoda.de%2Fi%2F2025%2F11%2F13%2F094833-1.webp&amp;size=m" alt="0cfd2e99f9d8a159389de1d0f8b3f163.png"></p> <p>输入一个网址,测试一下,</p> <p><img src="https://blog.laoda.de/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fimg.laoda.de%2Fi%2F2025%2F11%2F13%2F094832-1.webp&amp;size=m" alt="2d1764ecbf9c5ff13e0692864d15659b.png"></p> <p>支持给你存下来的网页打标签,</p> <p><img src="https://blog.laoda.de/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fimg.laoda.de%2Fi%2F2025%2F11%2F13%2F101102.webp&amp;size=m" alt="58b7e0e99d814b62bfa15217b140b3a4.png"></p> <p>服务器统计情况,</p> <p><img src="https://blog.laoda.de/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fimg.laoda.de%2Fi%2F2025%2F11%2F13%2F094827-1.webp&amp;size=m" alt="abe96ef7a5a74c3d3cd8330ec85073c2.png"></p> <p>Karakeep是支持浏览器插件的,这边可以直接下载浏览器插件,方便浏览网页的时候使用,</p> <p><img src="https://blog.laoda.de/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fimg.laoda.de%2Fi%2F2025%2F11%2F13%2F100541-1.webp&amp;size=m" alt="c5519d815ece46e7a66ebf39b763ec26.png"></p> <p>输入你搭建好的域名,然后登录即可,</p> <img src="https://blog.laoda.de/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fimg.laoda.de%2Fi%2F2025%2F11%2F13%2F094824-1.webp&amp;size=m" alt="ccd39153c2f87d5ae8be6ee098cfbf7c.png" style="zoom:50%;"> <p>使用</p> <p><img src="https://blog.laoda.de/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fimg.laoda.de%2Fi%2F2025%2F11%2F13%2F094817-1.webp&amp;size=m" alt="ea1377f2cd994631248c61114a911c92.png"></p> <p><img src="https://blog.laoda.de/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fimg.laoda.de%2Fi%2F2025%2F11%2F13%2F094815-1.webp&amp;size=m" alt="c89e80bfa4ab4a3de5f1e1b7e6c11124.png"></p> <p>也支持下载完整页面,</p> <p><img src="https://blog.laoda.de/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fimg.laoda.de%2Fi%2F2025%2F11%2F13%2F101018.webp&amp;size=m" alt="502f8bd34f6f45f8af5ad101bd17bd69.png"></p> <p>还有一些AI功能,大家可以自行探索。或者查看<a href="https://docs.karakeep.app/">Karakeep的官方文档</a></p> <h3 id="8-1-更新-karakeep">8.1 更新 karakeep</h3> <p>这个项目后续会持续有更新,所以提供一个更新的方式。</p> <pre><code class="language-yaml">cd /root/data/docker_data/karakeep docker compose pull docker compose up -d # 请不要使用 docker compose stop 来停止容器,因为这么做需要额外的时间等待容器停止;docker compose up -d 直接升级容器时会自动停止并立刻重建新的容器,完全没有必要浪费那些时间。 docker image prune # prune 命令用来删除不再使用的 docker 对象。删除所有未被 tag 标记和未被容器使用的镜像 </code></pre> <p>提示:</p> <pre><code class="language-bash">WARNING! This will remove all dangling images. Are you sure you want to continue? [y/N] </code></pre> <p>输入 <code>y</code></p> <p>利用 Docker 搭建的应用,更新非常容易~</p> <h3 id="8-2-卸载-karakeep">8.2 卸载 karakeep</h3> <p>同样进入安装页面,先停止所有容器。</p> <pre><code class="language-bash">cd /root/data/docker_data/karakeep docker compose down cd .. rm -rf /root/data/docker_data/karakeep # 完全删除 </code></pre> <p>可以卸载得很干净。</p> <h2 id="9--使用简介">9. 使用简介</h2> <p>大家有问题欢迎评论区交流。</p> <h2 id="10--结尾">10. 结尾</h2> <p>祝大家用得开心,有问题可以去 GitHub 提 <a href="https://github.com/karakeep-app/karakeep/issues">Issues</a>,也可以在评论区互相交流探讨。</p> <p>同时,有能力给项目做贡献的同学,也欢迎积极加入到 <a href="https://github.com/karakeep-app/karakeep">项目</a> 中来,贡献自己的一份力量!</p> <p>最后,感谢作者 <a href="https://github.com/karakeep-app/karakeep">@civilblur</a> 的辛苦付出,让我们能用到这么优秀的项目!欢迎大家都去给这个项目点个 star。</p> <h2 id="参考资料">参考资料</h2> <p>官方 GitHub:<a href="https://github.com/karakeep-app/karakeep">https://github.com/karakeep-app/karakeep</a></p>

2025/11/13
阅读更多

十分钟搭建一次性私密信息共享平台——OTS|好玩儿的Docker项目

<img src="https://blog.laoda.de/plugins/feed/assets/telemetry.gif?title=%E5%8D%81%E5%88%86%E9%92%9F%E6%90%AD%E5%BB%BA%E4%B8%80%E6%AC%A1%E6%80%A7%E7%A7%81%E5%AF%86%E4%BF%A1%E6%81%AF%E5%85%B1%E4%BA%AB%E5%B9%B3%E5%8F%B0%E2%80%94%E2%80%94OTS%EF%BD%9C%E5%A5%BD%E7%8E%A9%E5%84%BF%E7%9A%84Docker%E9%A1%B9%E7%9B%AE&amp;url=/archives/docker-compose-install-ots" width="1" height="1" alt="" style="opacity:0;"> <h2 id="1--唠嗑">1. 唠嗑</h2> <p>好久没更新了,这周来和大家分享一个阅后即焚工具,安全高效的一次性私密信息共享平台——OTS(One-Time-Secret)</p> <h2 id="2--OTS-简介">2. OTS 简介</h2> <p>OTS 是一个一次性私密信息分享平台。私密信息在发送到服务器之前,会在浏览器中使用对称的 256 位 AES 加密进行加密。之后会生成一个包含私密信息的 ID 和密码的 URL。密码永远不会发送到服务器,因此服务器无法以合理的方式解密它所传递的私密信息。此外,私密信息在第一次读取后会立即被删除。</p> <p><strong>功能</strong></p> <ul> <li>私密信息在浏览器中通过 AES 256 位加密进行加密</li> <li>服务器永远不会接收到明文私密信息</li> <li>私密信息在第一次读取后即被删除</li> </ul> <p><img src="https://blog.laoda.de/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fimg.laoda.de%2Fi%2F2025%2F10%2F22%2F104028-1.webp&amp;size=m" alt="1fda6d3277a6b618383e3f5d09ecc446.png"></p> <p><img src="https://blog.laoda.de/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fimg.laoda.de%2Fi%2F2025%2F10%2F22%2F104055-1.webp&amp;size=m" alt="c25e783b58219bbb18541bb45c1688e9.png"></p> <h2 id="3--相关地址">3. 相关地址</h2> <p>官方 GitHub 地址:<a href="https://github.com/Luzifer/ots">https://github.com/Luzifer/ots</a> (目前 638 个 star,欢迎大家去给项目点星星!)</p> <p>Demo 地址:<a href="https://ots.fyi/">https://ots.fyi/</a></p> <p>当然如果你看到这边还想自己用 docker 搭建一个,那我们就继续往下!</p> <h2 id="4--搭建环境">4. 搭建环境</h2> <ul> <li>服务器:咕咕这边用的香港的 <a href="http://gao.ee/lca">莱卡云服务器</a>,建议服务器内存 1G 以上,当然你也可以选择其他 <a href="https://blog.laoda.de/vps">高性价比的服务器</a>。</li> <li>系统:Debian 11 (<a href="https://blog.laoda.de/archives/useful-script/">DD 脚本</a> 非必需 DD,用原来的系统也 OK,之后教程都是用 Debian 或者 Ubuntu 搭建~)</li> <li>安装好 Docker、Docker-compose(<a href="https://blog.laoda.de/archives/hello-docker/">相关脚本</a>)</li> <li>【必需】域名一枚,并做好解析到服务器上(<a href="https://blog.laoda.de/archives/namesilo/">域名购买、域名解析</a> <a href="https://www.bilibili.com/video/BV1Sy4y1k7kZ/">视频教程</a>)</li> <li>【非必需】提前安装好宝塔面板海外版本 aapanel,并安装好 Nginx(<a href="https://forum.aapanel.com/d/9-aapanel-linux-panel-6812-installation-tutorial">安装地址</a>)</li> <li>【非必需本教程选用】安装好 Nginx Proxy Manager(<a href="https://blog.laoda.de/archives/nginxproxymanager/">相关教程</a>)</li> </ul> <h2 id="5--搭建视频-过俩天补充-----">5. 搭建视频(过俩天补充 = =)</h2> <h3 id="5-1-YouTube">5.1 YouTube</h3> <p>视频地址:</p> <h3 id="5-2-哔哩哔哩">5.2 哔哩哔哩</h3> <p>哔哩哔哩:</p> <h2 id="6--搭建方式">6. 搭建方式</h2> <h3 id="6-1-安装-Docker-与-Nginx-Proxy-Manager">6.1 安装 Docker 与 Nginx Proxy Manager</h3> <p>可以直接参考这篇内容:</p> <p><a href="https://blog.laoda.de/archives/nginxproxymanager/">https://blog.laoda.de/archives/nginxproxymanager/</a></p> <h3 id="6-2-创建安装目录">6.2 创建安装目录</h3> <p>创建一下安装的目录:</p> <pre><code class="language-bash">sudo -i mkdir -p /root/data/docker_data/OTS cd /root/data/docker_data/OTS </code></pre> <p>接着我们来编辑下<code>docker-compose.yml</code></p> <pre><code class="language-bash">vim docker-compose.yml </code></pre> <pre><code class="language-yaml">services: app: image: ghcr.io/luzifer/ots:latest container_name: ots-app restart: always ports: - 3003:3000 environment: # Optional, see "Customization" in README #CUSTOMIZE: '/etc/ots/customize.yaml' # See README for details REDIS_URL: redis://redis:6379/0 # 168h = 1w SECRET_EXPIRY: "604800" # "mem" or "redis" (See README) STORAGE_TYPE: redis depends_on: - redis redis: image: redis:latest container_name: ots-redis restart: always volumes: - ./data:/data </code></pre> <p>环境变量的说明如下:</p> <table> <thead> <tr> <th>环境变量</th> <th>描述</th> </tr> </thead> <tbody> <tr> <td><code>REDIS_URL</code></td> <td><code>Redis</code> 数据库的连接字符串,格式为 <code>redis://USR:PWD@HOST:PORT/DB</code>。用于指定 <code>OTS</code> 应用连接的 <code>Redis</code> 实例</td> </tr> <tr> <td><code>SECRET_EXPIRY</code></td> <td>秘密的过期时间,单位为秒(默认 <code>0</code> = 无过期)。此示例中设置为 <code>604800</code>(168 小时 = 1 周)</td> </tr> <tr> <td><code>STORAGE_TYPE</code></td> <td>用于指定存储类型,可以是 <code>mem</code>(内存存储)或 <code>redis</code>(使用 <code>Redis</code> 存储秘密)</td> </tr> </tbody> </table> <p>最后,在 <code>ots</code> 目录下执行以下命令一键启动:</p> <p>其中的左边的<code>3003</code>可以改成服务器上没有用过的端口,记得修改自己的用户名和密码,修改完成之后,可以在英文输入法下,按 <code>i</code> 修改,完成之后,按一下 <code>esc</code>,然后 <code>:wq</code> 保存退出。</p> <h3 id="6-3-查看端口是否被占用">6.3 查看端口是否被占用</h3> <p>查看端口是否被占用(以 <code>3003</code> 为例),输入:</p> <pre><code class="language-bash">lsof -i:3003 #查看 3003 端口是否被占用,如果被占用,重新自定义一个端口 </code></pre> <p>如果啥也没出现,表示端口未被占用,我们可以继续下面的操作了~</p> <p>如果出现:</p> <pre><code class="language-bash">-bash: lsof: command not found </code></pre> <p>运行:</p> <pre><code class="language-bash">apt install lsof #安装 lsof </code></pre> <p>如果端口没有被占用(被占用了就修改一下端口,比如改成 <code>8381</code>,注意 docker 命令行里和防火墙都要改)</p> <h3 id="6-4-启动-OTS">6.4 启动 OTS</h3> <pre><code>cd /root/data/docker_data/OTS docker compose up -d # 注意,老版本用户用 docker-compose up -d </code></pre> <p>耐心等待拉取好镜像,出现 <code>done</code>的字样之后,</p> <p>理论上我们就可以输入 <code>http://ip:3003</code> 访问了。</p> <p>但是这边这个服务必须先搞一下反向代理!既然是加密的项目,不能用 http 使用明文传输,我们部署在公网一定要考虑使用反向代理工具配置 SSL!</p> <p>做反向代理前,你需要一个域名!</p> <p><a href="https://loll.cc/ns">namesilo</a> 上面 xyz 后缀的域名一年就 7 块钱,可以年抛。(冷知识,namesilo 上 6 位数字的 xyz 续费永远都是 0.99 美元 = =)</p> <p>如果想要长期使用,还是建议买 com 后缀的域名,更加正规一些,可以输入 <code>laodade</code> 来获得 1 美元的优惠(不知道现在还有没有)</p> <p><a href="https://loll.cc/ns">namesilo</a> 自带隐私保护,咕咕一直在用这家,价格也是这些注册商里面比较低的,关键是他家不像其他家域名注册商,没有七七八八的套路!(就是后台界面有些 <del>丑</del> 古老 = =)</p> <p><a href="https://blog.laoda.de/archives/namesilo/">【域名购买】Namesilo 优惠码和域名解析教程(附带服务器购买推荐和注意事项)</a></p> <p>我们接着往下看!</p> <h2 id="7--反向代理">7. 反向代理</h2> <h3 id="7-1-利用-Nginx-Proxy-Manager">7.1 利用 Nginx Proxy Manager</h3> <p>在添加反向代理之前,确保你已经完成了域名解析,不会的可以看这个:<strong>域名一枚,并做好解析到服务器上</strong>(<a href="https://blog.laoda.de/archives/namesilo/">域名购买、域名解析</a> <a href="https://www.bilibili.com/video/BV1Sy4y1k7kZ/">视频教程</a>) (名称改成你自己想要的域名前缀即可)</p> <img src="https://blog.laoda.de/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fimg.laoda.de%2Fi%2F2025%2F10%2F22%2F104105-1.webp&amp;size=m" alt="08b09ee8604e6250e24354ccb4cd926b.png" style="zoom: 33%;"> <p>之后,登陆 Nginx Proxy Manager(不会的看这个:<strong>安装 Nginx Proxy Manager</strong>(<a href="https://blog.laoda.de/archives/nginxproxymanager/">相关教程</a>))</p> <blockquote> <p><strong>注意:</strong></p> <p>Nginx Proxy Manager(以下简称 NPM)会用到 <code>80</code>、<code>443</code> 端口,所以本机不能占用(比如原来就有 Nginx)</p> </blockquote> <p>直接丢几张图:</p> <img src="https://blog.laoda.de/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fimg.laoda.de%2Fi%2F2025%2F10%2F22%2F104137-1.webp&amp;size=m" alt="78186b53ea22c99caa737ec043e24947.png" style="zoom:50%;"> <img src="https://blog.laoda.de/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fimg.laoda.de%2Fi%2F2025%2F04%2F16%2F205949-1.webp&amp;size=m" alt="c2075b8e6a85bff585396fd4c0bca1ed.png" style="zoom:50%;"> <img src="https://blog.laoda.de/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fimg.laoda.de%2Fi%2F2025%2F10%2F22%2F104125-1.webp&amp;size=m" alt="525e20e0bea1448ff37e3d95dec2876c.png" style="zoom:50%;"> <img src="https://blog.laoda.de/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fimg.laoda.de%2Fi%2F2025%2F04%2F16%2F205935-1.webp&amp;size=m" alt="f5137b06b270769cc2ebca7019f47e35.png" style="zoom:50%;"> <blockquote> <p>注意填写对应的 <code>域名</code>、<code>IP</code> 和 <code>端口</code>,按文章来的话,应该是 <code>3003</code></p> </blockquote> <p><strong>IP 填写:</strong></p> <p>如果 Nginx Proxy Manager 和 OTS 在同一台服务器上,可以在终端输入:</p> <pre><code class="language-bash">ip addr show docker0 </code></pre> <p>查看对应的 Docker 容器内部 IP。</p> <img src="https://blog.laoda.de/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fimg.laoda.de%2Fi%2F2024%2F12%2F12%2F153819-1.webp&amp;size=m" style="zoom:50%;"> <p>否则直接填 OTS 所在的服务器 IP 就行。</p> <p>然后访问域名就可以访问了!</p> <h3 id="7-2-利用宝塔面板">7.2 利用宝塔面板</h3> <p>发现还是有不少小伙伴习惯用宝塔面板,这边也贴一个宝塔面板的反代配置:</p> <p>直接新建一个站点,不要数据库,不要 php,纯静态即可。</p> <p>然后打开下面的配置,修改 Nginx 的配置。</p> <img src="https://blog.laoda.de/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fimg.laoda.de%2Fi%2F2024%2F12%2F12%2F153816-1.webp&amp;size=m" alt="image-20220819150345725" style="zoom: 33%;"> <img src="https://blog.laoda.de/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fimg.laoda.de%2Fi%2F2024%2F12%2F12%2F153814-1.webp&amp;size=m" alt="image-20220819150542867" style="zoom: 33%;"> <p>代码如下:</p> <pre><code class="language-nginx">location / { proxy_pass http://127.0.0.1:3003/; # 注意改成你实际使用的端口 rewrite ^/(.*)$ /$1 break; proxy_redirect off; proxy_set_header Host $host; proxy_set_header X-Forwarded-Proto $scheme; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header Upgrade-Insecure-Requests 1; proxy_set_header X-Forwarded-Proto https; } </code></pre> <p>此方法对 90% 的反向代理都能生效,然后就可以用域名来安装访问了。</p> <p>有同学可能会问,为什么不直接用宝塔自带的反向代理功能。</p> <img src="https://blog.laoda.de/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fimg.laoda.de%2Fi%2F2024%2F12%2F12%2F153804-1.webp&amp;size=m" alt="image-20220819150730128" style="zoom: 33%;"> <p>也可以,不过咕咕自己之前遇到过当有多个网站需要反代的时候,在这边设置会报错的情况 = =</p> <p>所以后来就不用了,直接用上面的方法来操作了。</p> <h2 id="8--使用教程">8. 使用教程</h2> <p>非常简单,所见即所得。</p> <p><img src="https://blog.laoda.de/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fimg.laoda.de%2Fi%2F2025%2F10%2F22%2F104117-1.webp&amp;size=m" alt="19f97b0b247033100477307b379bd7b8.png"></p> <p><img src="https://blog.laoda.de/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fimg.laoda.de%2Fi%2F2025%2F10%2F22%2F104115-1.webp&amp;size=m" alt="6e3f18b4fc5aeef1a5bdb998d4decf6e.png"></p> <p><img src="https://blog.laoda.de/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fimg.laoda.de%2Fi%2F2025%2F10%2F22%2F104114-1.webp&amp;size=m" alt="ddd6171537aa47f30f150dfa9d9f8d6b.png"></p> <h3 id="8-1-更新-OTS">8.1 更新 OTS</h3> <p>这个项目后续会持续有更新,所以提供一个更新的方式。</p> <pre><code class="language-yaml">cd /root/data/docker_data/OTS docker compose pull docker compose up -d # 请不要使用 docker compose stop 来停止容器,因为这么做需要额外的时间等待容器停止;docker compose up -d 直接升级容器时会自动停止并立刻重建新的容器,完全没有必要浪费那些时间。 docker image prune # prune 命令用来删除不再使用的 docker 对象。删除所有未被 tag 标记和未被容器使用的镜像 </code></pre> <p>提示:</p> <pre><code class="language-bash">WARNING! This will remove all dangling images. Are you sure you want to continue? [y/N] </code></pre> <p>输入 <code>y</code></p> <p>利用 Docker 搭建的应用,更新非常容易~</p> <h3 id="8-2-卸载-OTS">8.2 卸载 OTS</h3> <p>同样进入安装页面,先停止所有容器。</p> <pre><code class="language-bash">cd /root/data/docker_data/OTS docker compose down cd .. rm -rf /root/data/docker_data/OTS # 完全删除 </code></pre> <p>可以卸载得很干净。</p> <h2 id="9--常见问题及注意点">9. 常见问题及注意点</h2> <p>大家有问题欢迎评论区交流。</p> <h2 id="10--结尾">10. 结尾</h2> <p>祝大家用得开心,有问题可以去 GitHub 提 <a href="https://github.com/Luzifer/ots/issues">Issues</a>,也可以在评论区互相交流探讨。</p> <p>同时,有能力给项目做贡献的同学,也欢迎积极加入到 <a href="https://github.com/Luzifer/ots">项目</a> 中来,贡献自己的一份力量!</p> <p>最后,感谢作者 <a href="https://github.com/Luzifer/ots">@civilblur</a> 的辛苦付出,让我们能用到这么优秀的项目!欢迎大家都去给这个项目点个 star。</p> <h2 id="参考资料">参考资料</h2> <p>官方 GitHub:<a href="https://github.com/Luzifer/ots">https://github.com/Luzifer/ots</a></p>

2025/10/22
阅读更多

3分钟利用 Paypal 领取免费一年 Perplexity 会员! 解锁AI搜索神器!网页点点就行!

<img src="https://blog.laoda.de/plugins/feed/assets/telemetry.gif?title=3%E5%88%86%E9%92%9F%E5%88%A9%E7%94%A8%20Paypal%20%E9%A2%86%E5%8F%96%E5%85%8D%E8%B4%B9%E4%B8%80%E5%B9%B4%20Perplexity%20%E4%BC%9A%E5%91%98%EF%BC%81%20%E8%A7%A3%E9%94%81AI%E6%90%9C%E7%B4%A2%E7%A5%9E%E5%99%A8%EF%BC%81%E7%BD%91%E9%A1%B5%E7%82%B9%E7%82%B9%E5%B0%B1%E8%A1%8C%EF%BC%81&amp;url=/archives/paypal-perplexity-subscription" width="1" height="1" alt="" style="opacity:0;"> <h3 id="简介">简介</h3> <h4 id="Perplexity是什么-"><strong>Perplexity是什么?</strong></h4> <p>Perplexity 是一个结合了 AI 聊天机器人和搜索引擎功能的工具,旨在快速提供准确、简洁的答案,并附上可靠的来源引用。它通过理解用户问题,从网络上实时抓取信息,整理成清晰的回答,特别适合需要快速查资料或做研究的用户。</p> <h4 id="Pro版本的功能-"><strong>Pro版本的功能:</strong></h4> <p>Perplexity Pro 提供更高级的功能,收费20美元/月,功能包括:</p> <ul> <li><strong>多模型选择</strong>:用户可切换使用 GPT-4o、Claude 3.7、Gemini Flash 等不同语言模型,灵活应对不同需求。</li> <li><strong>Pages报告生成</strong>:可根据用户需求生成定制化的精美简报,适合研究或商务用途。</li> <li><strong>高级搜索功能</strong>:如学术论文模式,专注搜索学术资源(如 Arxiv、SemanticScholar),提升研究效率。</li> <li><strong>更高使用额度</strong>:相比免费版,Pro 版支持更多查询次数和更复杂的任务。</li> </ul> <p>如果你需要快速查资料、做研究或验证信息,Perplexity 是最佳选择,尤其适合学术和专业场景。</p> <h3 id="领取条件">领取条件</h3> <ul> <li>1个PayPal账号(我用的是美区,据网友测试国区也可以)</li> <li>能访问真正的互联网</li> <li>Perplexity 账号(可以现场注册)</li> </ul> <h3 id="领取方式">领取方式</h3> <p>领取地址:<a href="https://www.perplexity.ai/join/p/paypal-subscription">https://www.perplexity.ai/join/p/paypal-subscription</a></p> <p><img src="https://blog.laoda.de/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fimg.laoda.de%2Fi%2F2025%2F09%2F04%2F182400-1.webp&amp;size=m" alt="ca3e75602a808c3b92397198da35363b.png"></p> <p>链接PayPal,</p> <p><img src="https://blog.laoda.de/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fimg.laoda.de%2Fi%2F2025%2F09%2F04%2F182357-1.webp&amp;size=m" alt="b4718b6f264d1c7de5207d0b5b7f9e34.png"></p> <p>我这边登录的是美区的PayPal,</p> <p><img src="https://blog.laoda.de/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fimg.laoda.de%2Fi%2F2025%2F09%2F04%2F182356-1.webp&amp;size=m" alt="a1f67cdf27b91ba2458beef66f8cfa17.png"></p> <p>选择接受优惠, <br> <img src="https://blog.laoda.de/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fimg.laoda.de%2Fi%2F2025%2F09%2F04%2F182353-1.webp&amp;size=m" alt="2f608f0e1364368ec38a54e434a4ac29.png"></p> <p>成功领取!</p> <p><img src="https://blog.laoda.de/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fimg.laoda.de%2Fi%2F2025%2F09%2F04%2F182351-1.webp&amp;size=m" alt="7f1cf6733ecd5e65c02f165a6c8a1964.png"></p> <p>已经可以正常使用了!</p> <p><img src="https://blog.laoda.de/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fimg.laoda.de%2Fi%2F2025%2F09%2F04%2F182350-1.webp&amp;size=m" alt="b12078649e490b14d6fee9c83be13f85.png"></p> <p>如果担心被反薅,记得取消 Perplexity 自动续费订阅,</p> <p>访问:<a href="https://www.perplexity.ai/join/p/paypal-subscription">https://www.perplexity.ai/account/details</a></p> <p><img src="https://blog.laoda.de/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fimg.laoda.de%2Fi%2F2025%2F09%2F04%2F182348-1.webp&amp;size=m" alt="fa470b29a43e2a70b1eb49c74b2f6dcb.png"></p> <p><img src="https://blog.laoda.de/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fimg.laoda.de%2Fi%2F2025%2F09%2F04%2F182345-1.webp&amp;size=m" alt="a6afdd3b8f2a1095fdabd1ee7fe59c19.png"></p> <p>取消订阅即可,不影响使用</p> <p>同样的,Perplexity 还支持Mac桌面端,也可以用起来了!</p> <p><img src="https://blog.laoda.de/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fimg.laoda.de%2Fi%2F2025%2F09%2F04%2F182348-1_1.webp&amp;size=m" alt="257164195481a94f33de0c5b62a87130.png"></p> <h3 id="注意点">注意点</h3> <ul> <li>这次似乎国区的PayPal也可以领取。</li> <li>有不少网友评价Perplexity玩不起,之前三星的泄漏的一年优惠码,没用几天被收回了。</li> <li>也有网友表示现在这次打开会提示开通过了。 You are already subscribed or have previously subscribed to Perplexity Pro.</li> <li>还有一些一注册上就风控,即使美区也是,原先注册好的死活都不让领说领过了,换无数个邮箱尝试依旧如此,这个时候可以考虑换一个干净的IP再试试。</li> </ul>

2025/9/4
阅读更多

给你的Docker应用加一层保险!10分钟搭建一个简单好用的认证中间件—— Tinyauth

<img src="https://blog.laoda.de/plugins/feed/assets/telemetry.gif?title=%E7%BB%99%E4%BD%A0%E7%9A%84Docker%E5%BA%94%E7%94%A8%E5%8A%A0%E4%B8%80%E5%B1%82%E4%BF%9D%E9%99%A9%EF%BC%8110%E5%88%86%E9%92%9F%E6%90%AD%E5%BB%BA%E4%B8%80%E4%B8%AA%E7%AE%80%E5%8D%95%E5%A5%BD%E7%94%A8%E7%9A%84%E8%AE%A4%E8%AF%81%E4%B8%AD%E9%97%B4%E4%BB%B6%E2%80%94%E2%80%94%20Tinyauth&amp;url=/archives/docker-compose-install-tinyauth" width="1" height="1" alt="" style="opacity:0;"> <h2 id="1--唠嗑">1. 唠嗑</h2> <p>这两三年来,我们搭建了不少好玩的Docker应用,其实一直陆陆续续有小伙伴在视频评论区和博客的评论区询问,怎么样才能让我的Docker应用更安全一点?</p> <p>这一期,我们就来介绍这样一个工具,用最简单的方式来给我们的Docker应用加一层保险!</p> <p>效果展示:</p> <p>保存之后。</p> <p>再次打开简历这个网页。</p> <p>提示需要登录了。</p> <p><img src="https://blog.laoda.de/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fimg.laoda.de%2Fi%2F2025%2F09%2F03%2F161435-1.webp&amp;size=m" alt="ae23d63f56a62d74560f7997f8537212.png"></p> <p>输入账号密码,继续</p> <p><img src="https://blog.laoda.de/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fimg.laoda.de%2Fi%2F2025%2F09%2F03%2F161434-1.webp&amp;size=m" alt="9cb7f756e503807085b95ed778e742c8.png"></p> <p>进来了。</p> <p><img src="https://blog.laoda.de/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fimg.laoda.de%2Fi%2F2025%2F09%2F03%2F161417-1.webp&amp;size=m" alt="cd86b2609a4ec471929f0fbc4d43d825.png"></p> <h2 id="2--Tinyauth简介">2. Tinyauth简介</h2> <p>Tinyauth 是个简单好用的认证中间件,能给你的 Docker 应用加个简洁的登录页面,或者支持 Google、GitHub 以及其他平台的 OAuth 认证。它跟主流代理比如 Nginx Proxy Manager、Traefik、Nginx 和 Caddy 都能无缝配合。</p> <p><img src="https://blog.laoda.de/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fimg.laoda.de%2Fi%2F2025%2F09%2F03%2F161535-1.webp&amp;size=m" alt="355959a7fc641c514e84dc5272ba9893.png"></p> <h2 id="3--相关地址">3. 相关地址</h2> <p>官方GitHub地址:<a href="https://github.com/steveiliop56/tinyauth">https://github.com/steveiliop56/tinyauth</a> (目前5.3K个star,欢迎大家去给项目点星星!)</p> <p>文档地址:<a href="https://tinyauth.app/docs/guides/nginx-proxy-manager">https://tinyauth.app/docs/guides/nginx-proxy-manager</a></p> <p>Demo地址:<a href="https://tinyauth.app/">https://tinyauth.app/</a></p> <p>下面我们就用 Tinyauth配合 Nginx Proxy Manager,简单分享一下是怎么搭建这个中间件从而来保护我们的Docker应用的。</p> <h2 id="4--搭建环境">4. 搭建环境</h2> <ul> <li>服务器:这边强烈建议用非大陆的服务器,咕咕这边用的香港的<a href="https://gao.ee/lca">莱卡云轻量应用服务器</a>,建议服务器内存1G以上,当然你也可以选择其他<a href="https://blog.laoda.de/vps">高性价比的服务器</a>。</li> <li>系统:Debian 11 (<a href="https://blog.laoda.de/archives/useful-script/">DD 脚本</a> 非必需 DD,用原来的系统也 OK,之后教程都是用 Debian 或者 Ubuntu 搭建~)</li> <li>安装好 Docker、Docker-compose(<a href="https://blog.laoda.de/archives/hello-docker/">相关脚本</a>)</li> <li>【必需】域名一枚,并做好解析到服务器上(<a href="https://blog.laoda.de/archives/namesilo/">域名购买、域名解析</a> <a href="https://www.bilibili.com/video/BV1Sy4y1k7kZ/">视频教程</a>)</li> <li>【非必需】提前安装好宝塔面板海外版本 aapanel,并安装好 Nginx(<a href="https://forum.aapanel.com/d/9-aapanel-linux-panel-6812-installation-tutorial">安装地址</a>)</li> <li>【非必需本教程选用】安装好 Nginx Proxy Manager(<a href="https://blog.laoda.de/archives/nginxproxymanager/">相关教程</a>)</li> </ul> <h2 id="5--搭建视频-过俩天补充-----">5. 搭建视频(过俩天补充 = =)</h2> <h3 id="5-1-YouTube">5.1 YouTube</h3> <p>视频地址:</p> <h3 id="5-2-哔哩哔哩">5.2 哔哩哔哩</h3> <p>哔哩哔哩:</p> <h2 id="6--搭建方式">6. 搭建方式</h2> <h3 id="6-1-安装-Docker-与-Nginx-Proxy-Manager">6.1 安装 Docker 与 Nginx Proxy Manager</h3> <p>可以直接参考这篇内容:</p> <p><a href="https://blog.laoda.de/archives/nginxproxymanager/">https://blog.laoda.de/archives/nginxproxymanager/</a></p> <p>我们这边假设大家都装好了Nginx Proxy Manger,</p> <p>假设Nginx Proxy Manger在服务器上的路径是<code>~/data/docker_data/npm</code></p> <p>这边的话就只需要修改一下即可。</p> <p>官方文档给的参考如下:</p> <pre><code class="language-yaml">services: npm: container_name: npm image: jc21/nginx-proxy-manager:2 restart: unless-stopped ports: - 80:80 - 443:443 - 81:81 volumes: - npm-data:/data - npm-letsencrypt:/etc/letsencrypt nginx: container_name: nginx image: nginx:latest restart: unless-stopped tinyauth: container_name: tinyauth image: ghcr.io/steveiliop56/tinyauth:v3 restart: unless-stopped environment: - SECRET_FILE=/secret.txt - APP_URL=http://tinyauth.example.com - USERS=user:$$2a$$10$$UdLYoJ5lgPsC0RKqYH/jMua7zIn0g9kPqWmhYayJYLaZQ/FTmH2/u # user:password volumes: npm-data: npm-letsencrypt: </code></pre> <p>如果直接使用,你一定会遇到很多问题。</p> <p> <del>我不会告诉你我用官方的这个配置文件,折腾了大半小时 = =</del> </p> <p>这边直接用咕咕修改过的这个:</p> <p>重新编辑你的NPM的<code>docker-compose.yaml</code>文件,改成下面这个:</p> <pre><code class="language-yaml">services: npm: container_name: npm image: jc21/nginx-proxy-manager:2 restart: unless-stopped ports: - 80:80 - 443:443 - 81:81 volumes: - ./data:/data # 冒号左边的 ./data改成你自己实际的路径 - ./letsencrypt:/etc/letsencrypt # 冒号左边的 ./letsencrypt改成你自己实际的路径 tinyauth: container_name: tinyauth image: ghcr.io/steveiliop56/tinyauth:v3 restart: unless-stopped environment: - SECRET=kX9mPqW3zT7rY2vN8bL4jF6hD1cA5eK2 # 这个也要修改 - APP_URL=https://auth.gugu.ovh #这个也要修改 - USERS=roy:$$2a$$10$$oXMqgLV/S1hrknYQht.WYu7YfWxkI4VriQn/y # user:password 这边要修改 </code></pre> <p>然后我们来看需要修改的部分。</p> <p><code>SECRET</code>,这个的话,直接在命令行操作:</p> <p>生成secret:</p> <pre><code class="language-bash">openssl rand -base64 32 | tr -dc 'a-zA-Z0-9' | head -c 32 &amp;&amp; echo </code></pre> <p>输出类似:<code>kX9mPqW3zT7rY2vN8bL4jF6hD1cA5eK2</code> 填入yaml即可。</p> <p><code>APP_URL</code> 这里改成你的tinyauth将来想要使用的域名,比如我这边<code>https://auth.gugu.ovh</code></p> <p><code>USERS</code> ,这个官方也提供了方法,生成用户名和密码。</p> <pre><code class="language-bash">docker run -i -t --rm ghcr.io/steveiliop56/tinyauth:v3 user create --interactive </code></pre> <p><img src="https://blog.laoda.de/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fimg.laoda.de%2Fi%2F2025%2F09%2F03%2F161524-1.webp&amp;size=m" alt="6788be9f0dedebd6fa8cd177d5dfe651.png"></p> <p>为docker格式化输出这个,记得选<code>yes</code>(按住shift再按小键盘方向键就可以选)</p> <p>原因是USERS这边在yaml文件里要做哈希转换(比如哈希中的 <code>$</code> 需要正确转义为 <code>$$</code>,命令行的 Docker 格式会自动处理。),如果不格式化输出会有问题。</p> <p><img src="https://blog.laoda.de/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fimg.laoda.de%2Fi%2F2025%2F09%2F03%2F161522-1.webp&amp;size=m" alt="c60fa52194d0d7b685327187290f75de.png"></p> <p>当然也支持多个用户,可以再打一遍代码,设置第二个用户,</p> <p><img src="https://blog.laoda.de/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fimg.laoda.de%2Fi%2F2025%2F09%2F03%2F161521-1.webp&amp;size=m" alt="595a1c77b1cc3807c6f4d398609c5c20.png"></p> <p>如果是一个用户,我们直接贴到yaml文件里即可,但是如果像我们现在这样,有三个用户,直接放进yaml文件会有点长,而且也不好维护。</p> <p>所以,我们可以把用户配置放入 <code>.env</code> 文件,可以这样做:</p> <p>编辑 <code>~/data/docker_data/npm/.env</code>:</p> <pre><code class="language-env">SECRET=kX9mPqW3zT7rY2vN8bL4jF6hD1cA5eK2 APP_URL=https://auth.gugu.ovh USERS=roy:$$2a$$10$$oXMqgLV/S1hrknYQht.WYu7YfEjBdSB5rIicj23O8WxkI4VriQn/y,gugu:$$2a$$10$$s.nq/KZLBlFwUiSUNyRUGe0j2pp.A9/.tecuX7QsCBq242baKgKJ.,xixi:$$2a$$10$$6smj5Y/PD.jB1a2Z2gDAxOVnlcv8HfINtLrzCmmTjMZLObBwV8DoW </code></pre> <p>修改 <code>docker-compose.yaml</code> 以引用<code> .env</code>:</p> <pre><code class="language-yaml">services: npm: container_name: npm image: jc21/nginx-proxy-manager:2 restart: unless-stopped ports: - 80:80 - 443:443 - 81:81 volumes: - ./data:/data - ./letsencrypt:/etc/letsencrypt nginx: container_name: nginx image: nginx:latest restart: unless-stopped tinyauth: container_name: tinyauth image: ghcr.io/steveiliop56/tinyauth:v3 restart: unless-stopped env_file: - .env # environment: # - SECRET=kX9mPqW3zT7rY2vN8bL4jF6hD1cA5eK2 # - APP_URL=https://auth.gugu.ovh # - USERS=roy:$$2a$$10$$oXMqgLV/S1hrknYQht.WYu7YfEjBdSB5rIicj23O8WxkI4VriQn/y # user:password </code></pre> <p>这样基本上就OK啦。</p> <p>重新启动NPM</p> <pre><code class="language-bash">cd ~/data/docker_data/npm docker compose down docker compose up -d docker compose logs tinyauth </code></pre> <p>出现类似,代表成功。</p> <p><img src="https://blog.laoda.de/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fimg.laoda.de%2Fi%2F2025%2F09%2F03%2F161517-1.webp&amp;size=m" alt="d9f4bfbde7c1b712d735d834b0cffb87.png"></p> <p>这边我们先搞一下反向代理!</p> <p>做反向代理前,你需要一个域名!</p> <p><a href="https://loll.cc/ns">namesilo</a> 上面 xyz 后缀的域名一年就 7 块钱,可以年抛。(冷知识,namesilo上 6位数字的xyz续费永远都是0.99美元 = =)</p> <p>如果想要长期使用,还是建议买 com 后缀的域名,更加正规一些,可以输入 <code>laodade</code> 来获得 1 美元的优惠(不知道现在还有没有)</p> <p><a href="https://loll.cc/ns">namesilo</a> 自带隐私保护,咕咕一直在用这家,价格也是这些注册商里面比较低的,关键是他家不像其他家域名注册商,没有七七八八的套路!(就是后台界面有些 <del>丑</del> 古老 = =)</p> <p><a href="https://blog.laoda.de/archives/namesilo/">【域名购买】Namesilo 优惠码和域名解析教程(附带服务器购买推荐和注意事项)</a></p> <p>我们接着往下看!</p> <h2 id="7--反向代理">7. 反向代理</h2> <h3 id="7-1-配置Tinyauth的反向代理">7.1 配置Tinyauth的反向代理</h3> <p>在添加反向代理之前,确保你已经完成了域名解析,不会的可以看这个:<strong>域名一枚,并做好解析到服务器上</strong>(<a href="https://blog.laoda.de/archives/namesilo/">域名购买、域名解析</a> <a href="https://www.bilibili.com/video/BV1Sy4y1k7kZ/">视频教程</a>) (名称改成你自己想要的域名前缀即可)</p> <p><img src="https://blog.laoda.de/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fimg.laoda.de%2Fi%2F2025%2F09%2F03%2F161515-1.webp&amp;size=m" alt="343d7766a70c301276c8bfae52fae8e3.png"></p> <p>之后,登陆 Nginx Proxy Manager(不会的看这个:<strong>安装 Nginx Proxy Manager</strong>(<a href="https://blog.laoda.de/archives/nginxproxymanager/">相关教程</a>))</p> <blockquote> <p><strong>注意:</strong></p> <p>Nginx Proxy Manager(以下简称 NPM)会用到 <code>80</code>、<code>443</code> 端口,所以本机不能占用(比如原来就有 Nginx)</p> </blockquote> <p>直接丢几张图:</p> <img src="https://blog.laoda.de/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fimg.laoda.de%2Fi%2F2025%2F09%2F03%2F161509-1.webp&amp;size=m" alt="aca0ebcc28a73586bb9c43131b0490cf.png" style="zoom:50%;"> <p>确保“阻止常见漏洞”(Bolck Common Exploits)选项是关掉的。如果这选项开了,Nginx 会把 URL 里的查询参数给拦下来,而 Tinyauth 得靠这些参数才能正常工作。</p> <p><code>tinyauth</code>这个是容器的名字,因为它和NPM在一个网络下,所以可以用容器名字来代替域名,<code>3000</code>是内部的端口,保持即可不要更改。</p> <img src="https://blog.laoda.de/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fimg.laoda.de%2Fi%2F2025%2F09%2F03%2F161506-1.webp&amp;size=m" alt="8ecf402ef51f5a3ec7ca759a85ae647c.png" style="zoom:50%;"> <img src="https://blog.laoda.de/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fimg.laoda.de%2Fi%2F2025%2F09%2F03%2F161457-1.webp&amp;size=m" alt="48c4db84474ea229406a39104f965c33.png" style="zoom:50%;"> <img src="https://blog.laoda.de/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fimg.laoda.de%2Fi%2F2025%2F09%2F03%2F161454-1.webp&amp;size=m" alt="b1f1a1aec3c198cc06b60758a3fad1e5.png" style="zoom:50%;"> <p>然后访问域名就可以访问了!</p> <img src="https://blog.laoda.de/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fimg.laoda.de%2Fi%2F2025%2F09%2F03%2F161450-1.webp&amp;size=m" alt="5c42cfa09961f64c36e18ea9d2ba4aeb.png" style="zoom:50%;"> <h3 id="7-2-配置需要保护的应用的反向代理">7.2 配置需要保护的应用的反向代理</h3> <p>我这边以之前介绍的简历应用(<a href="https://blog.laoda.de/archives/docker-compose-install-magic-resume">让写简历变得简单且智能!十分钟搭建一个在线简历编辑器——Magic Resume|好玩儿的Docker项目</a>)为例子,</p> <img src="https://blog.laoda.de/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fimg.laoda.de%2Fi%2F2025%2F09%2F03%2F161443-1.webp&amp;size=m" alt="084d9f5958a47f6b0e867f163ddc06de.png" style="zoom:50%;"> <p>找到你需要添加登录页的应用的对应的NPM配置,在Advance里面添加:</p> <pre><code class="language-nginx"># Root location location / { # Pass the request to the app proxy_pass $forward_scheme://$server:$port; # Add other app specific config here # Tinyauth auth request auth_request /tinyauth; error_page 401 = @tinyauth_login; } # Tinyauth auth request location /tinyauth { # Pass request to Tinyauth proxy_pass http://tinyauth:3000/api/auth/nginx; # Pass the request headers proxy_set_header x-forwarded-proto $scheme; proxy_set_header x-forwarded-host $http_host; proxy_set_header x-forwarded-uri $request_uri; } # Tinyauth login redirect location @tinyauth_login { return 302 https://auth.gugu.ovh/login?redirect_uri=$scheme://$http_host$request_uri; # Make sure to replace the https://auth.gugu.ovh with your own app URL } </code></pre> <p>其他地方都不用管,只要把<code>https://auth.gugu.ovh</code> 替换成你自己的tinyauth的域名即可。</p> <img src="https://blog.laoda.de/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fimg.laoda.de%2Fi%2F2025%2F09%2F03%2F161437-1.webp&amp;size=m" alt="75bde1737d7b3d2d605860bb839fa567.png" style="zoom: 50%;"> <p>保存之后。</p> <p>再次打开简历这个网页。</p> <p>提示需要登录了。</p> <p><img src="https://blog.laoda.de/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fimg.laoda.de%2Fi%2F2025%2F09%2F03%2F161435-1.webp&amp;size=m" alt="ae23d63f56a62d74560f7997f8537212.png"></p> <p>输入账号密码,继续</p> <p><img src="https://blog.laoda.de/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fimg.laoda.de%2Fi%2F2025%2F09%2F03%2F161434-1.webp&amp;size=m" alt="9cb7f756e503807085b95ed778e742c8.png"></p> <p>进来了。</p> <p><img src="https://blog.laoda.de/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fimg.laoda.de%2Fi%2F2025%2F09%2F03%2F161417-1.webp&amp;size=m" alt="cd86b2609a4ec471929f0fbc4d43d825.png"></p> <h2 id="8--使用教程">8. 使用教程</h2> <p>其他的也类似,只需要在对应应用的NPM的Advance里面加:</p> <pre><code class="language-nginx"># Root location location / { # Pass the request to the app proxy_pass $forward_scheme://$server:$port; # Add other app specific config here # Tinyauth auth request auth_request /tinyauth; error_page 401 = @tinyauth_login; } # Tinyauth auth request location /tinyauth { # Pass request to Tinyauth proxy_pass http://tinyauth:3000/api/auth/nginx; # Pass the request headers proxy_set_header x-forwarded-proto $scheme; proxy_set_header x-forwarded-host $http_host; proxy_set_header x-forwarded-uri $request_uri; } # Tinyauth login redirect location @tinyauth_login { return 302 https://auth.gugu.ovh/login?redirect_uri=$scheme://$http_host$request_uri; # Make sure to replace the https://auth.gugu.ovh with your own app URL } </code></pre> <p>即可。</p> <p>PS: <code>/tinyauth</code> 这个路径可以随便改,指南里用这个名字只是为了方便。</p> <h2 id="9--常见问题及注意点">9. 常见问题及注意点</h2> <p>大家有问题欢迎评论区交流。</p> <h2 id="10--结尾">10. 结尾</h2> <p>祝大家用得开心,有问题可以去 GitHub 提 <a href="https://github.com/steveiliop56/tinyauth/issues">Issues</a>,也可以在评论区互相交流探讨。</p> <p>同时,有能力给项目做贡献的同学,也欢迎积极加入到 <a href="https://github.com/steveiliop56/tinyauth">项目</a> 中来,贡献自己的一份力量!</p> <p>最后,感谢作者<a href="https://github.com/steveiliop56">@steveiliop56</a>的辛苦付出,让我们能用到这么优秀的项目!欢迎大家都去给这个项目点个 star ⭐️</p> <h2 id="参考资料">参考资料</h2> <p>官方GitHub:<a href="https://github.com/steveiliop56/tinyauth">https://github.com/steveiliop56/tinyauth</a></p>

2025/9/3
阅读更多

Dify本地部署上手攻略:轻松打造你的专属AI神器|好玩儿的Docker项目

<img src="https://blog.laoda.de/plugins/feed/assets/telemetry.gif?title=Dify%E6%9C%AC%E5%9C%B0%E9%83%A8%E7%BD%B2%E4%B8%8A%E6%89%8B%E6%94%BB%E7%95%A5%EF%BC%9A%E8%BD%BB%E6%9D%BE%E6%89%93%E9%80%A0%E4%BD%A0%E7%9A%84%E4%B8%93%E5%B1%9EAI%E7%A5%9E%E5%99%A8%EF%BD%9C%E5%A5%BD%E7%8E%A9%E5%84%BF%E7%9A%84Docker%E9%A1%B9%E7%9B%AE&amp;url=/archives/docker-compose-install-dify" width="1" height="1" alt="" style="opacity:0;"> <h2 id="前言">前言</h2> <p>Dify是一个开源的AI应用开发平台,简单来说,它能让普通人也能轻松创建智能聊天机器人或AI助手。</p> <p>它通过拖拽式的界面,整合了各种大语言模型和工具,比如可以连接企业知识库或外部API,让AI能回答专业问题或完成复杂任务。无论是程序员还是非技术人员,都能用它快速搭建AI应用,省时省力。Dify还支持本地部署,数据安全有保障,特别适合想快速上手AI的团队或个人。</p> <p>其实Dify已经火了有很长时间了,之前嫌麻烦,我一直没怎么用。最近,公司里的一批小伙伴都换了配置更高的新电脑,纷纷在本地部署起了Dify,这一期就来一个小白级别的教程,和大家简单分享一下Dify的本地部署。</p> <h2 id="准备工作">准备工作</h2> <p>Dify本身官方对电脑硬件的建议要求是:</p> <ul> <li>CPU &gt;= 2 Core</li> <li>RAM &gt;= 4 GiB</li> </ul> <p>如果你打算做完全本地的部署,即不用云端的大模型,</p> <p>如果你是windows电脑,建议配置如下:</p> <p><strong>内存</strong>:至少 16GB 系统内存(推荐 24GB 以上,留 8GB 给操作系统和其他软件)。</p> <p><strong>GPU</strong>(可选但推荐):至少 16GB VRAM 的 GPU(如 AMD Radeon RX 9070 XT 或 NVIDIA RTX 3090),支持 MXFP4 量化。GPU 内存带宽需高(如 GDDR6X/GDDR7,1000+ GB/s)。</p> <p><strong>CPU</strong>:支持 CPU 推理,但速度较慢。推荐高性能 CPU(如 AMD Ryzen 9 5900X 或 Intel Core i9)。</p> <p>如果是Mac的话,</p> <p><strong>硬件</strong>:Apple Silicon(M1/M2/M3/M4),至少 16GB 统一内存(推荐 32GB)。</p> <p>我自己这边用的是Mac,部署起来相对简单一些,用Windows的小伙伴可能会稍微复杂一丢丢。</p> <p>Windows安装Docker可以参考这个:https://docs.docker.com/desktop/windows/install/#wsl-2-backend</p> <p><strong>本质都是先在本地电脑安装好Docker,然后通过Docker来部署Dify。</strong></p> <h2 id="安装Docker">安装Docker</h2> <p>本地装docker,可以去docker官网直接下载一个安装包。</p> <p>官方地址:<code>https://www.docker.com/products/docker-desktop/</code></p> <p><img src="https://blog.laoda.de/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fimg.laoda.de%2Fi%2F2025%2F08%2F28%2F160558-1.webp&amp;size=m" alt="img"></p> <p>下载之后,就像安装普通软件一样,安装一下。</p> <p>然后打开docker。</p> <p>打开Mac的终端,输入<code>docker -v</code>,有版本号说明安装好了。</p> <p><img src="https://blog.laoda.de/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fimg.laoda.de%2Fi%2F2025%2F08%2F28%2F160555-1.webp&amp;size=m" alt="img"></p> <p><img src="https://blog.laoda.de/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fimg.laoda.de%2Fi%2F2025%2F08%2F28%2F160553-1.webp&amp;size=m" alt="img"></p> <p>这边还会涉及docker换源,由于防火墙的原因,我们大陆可能无法访问docker的镜像仓库,建议大家换成中科大的源:<code>https://docker.mirrors.ustc.edu.cn/</code></p> <p><img src="https://blog.laoda.de/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fimg.laoda.de%2Fi%2F2025%2F08%2F28%2F160551-1.webp&amp;size=m" alt="img"></p> <p>改成这个:</p> <pre><code>{ "builder": { "gc": { "defaultKeepStorage": "20GB", "enabled": true } }, "experimental": false, "features": { "buildkit": true }, "registry-mirrors": [ "https://docker.mirrors.ustc.edu.cn" ] } </code></pre> <p><img src="https://blog.laoda.de/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fimg.laoda.de%2Fi%2F2025%2F08%2F28%2F160545-1.webp&amp;size=m" alt="img"></p> <p>然后记得点击应用并重启。</p> <p>使用如下命令查看Docker配置,</p> <pre><code class="language-bash">docker info </code></pre> <p><img src="https://blog.laoda.de/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fimg.laoda.de%2Fi%2F2025%2F08%2F28%2F160540-1.webp&amp;size=m" alt="3c158daac7e908df23db4bede4f8db8d.png"></p> <p>就可以了。</p> <h2 id="安装Dify">安装Dify</h2> <p>Docker安装好之后,接下来安装我们今天的主角——Dify。</p> <p>文档地址:<code>https://docs.dify.ai/en/introduction</code></p> <p>这边一直关注咕咕的小伙伴可以很轻松的安装好。</p> <p>Mac和Linux其实差不多,我们直接像操作VPS一样来就行。</p> <pre><code class="language-bash">mkdir ./data/docker_data cd data/docker_data </code></pre> <p><img src="https://blog.laoda.de/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fimg.laoda.de%2Fi%2F2025%2F08%2F28%2F160529-1.webp&amp;size=m" alt="img"></p> <pre><code class="language-bash">git clone --branch "$(curl -s https://api.github.com/repos/langgenius/dify/releases/latest | jq -r .tag_name)" https://github.com/langgenius/dify.git # 从GitHub下载dify源码 cd dify/docker # 进入文件夹 cp .env.example .env # 拷贝一份配置文件 </code></pre> <p>这里如果后续有需求,可以自行修改这个配置文件,一起来看一下</p> <pre><code class="language-env"># ------------------------------ # Environment Variables for API service &amp; worker # ------------------------------ # ------------------------------ # Common Variables # ------------------------------ # The backend URL of the console API, # used to concatenate the authorization callback. # If empty, it is the same domain. # Example: https://api.console.dify.ai CONSOLE_API_URL= # The front-end URL of the console web, # used to concatenate some front-end addresses and for CORS configuration use. # If empty, it is the same domain. # Example: https://console.dify.ai CONSOLE_WEB_URL= # Service API Url, # used to display Service API Base Url to the front-end. # If empty, it is the same domain. # Example: https://api.dify.ai SERVICE_API_URL= # WebApp API backend Url, # used to declare the back-end URL for the front-end API. # If empty, it is the same domain. # Example: https://api.app.dify.ai APP_API_URL= # WebApp Url, # used to display WebAPP API Base Url to the front-end. # If empty, it is the same domain. # Example: https://app.dify.ai APP_WEB_URL= # File preview or download Url prefix. # used to display File preview or download Url to the front-end or as Multi-model inputs; # Url is signed and has expiration time. # Setting FILES_URL is required for file processing plugins. # - For https://example.com, use FILES_URL=https://example.com # - For http://example.com, use FILES_URL=http://example.com # Recommendation: use a dedicated domain (e.g., https://upload.example.com). # Alternatively, use http://&lt;your-ip&gt;:5001 or http://api:5001, # ensuring port 5001 is externally accessible (see docker-compose.yaml). FILES_URL= # INTERNAL_FILES_URL is used for plugin daemon communication within Docker network. # Set this to the internal Docker service URL for proper plugin file access. # Example: INTERNAL_FILES_URL=http://api:5001 INTERNAL_FILES_URL= # Ensure UTF-8 encoding LANG=en_US.UTF-8 LC_ALL=en_US.UTF-8 PYTHONIOENCODING=utf-8 # ------------------------------ # Server Configuration # ------------------------------ # The log level for the application. # Supported values are `DEBUG`, `INFO`, `WARNING`, `ERROR`, `CRITICAL` LOG_LEVEL=INFO # Log file path LOG_FILE=/app/logs/server.log # Log file max size, the unit is MB LOG_FILE_MAX_SIZE=20 # Log file max backup count LOG_FILE_BACKUP_COUNT=5 # Log dateformat LOG_DATEFORMAT=%Y-%m-%d %H:%M:%S # Log Timezone LOG_TZ=UTC # Debug mode, default is false. # It is recommended to turn on this configuration for local development # to prevent some problems caused by monkey patch. DEBUG=false # Flask debug mode, it can output trace information at the interface when turned on, # which is convenient for debugging. FLASK_DEBUG=false # Enable request logging, which will log the request and response information. # And the log level is DEBUG ENABLE_REQUEST_LOGGING=False # A secret key that is used for securely signing the session cookie # and encrypting sensitive information on the database. # You can generate a strong key using `openssl rand -base64 42`. SECRET_KEY=sk-9f73s3ljTXVcMT3Blb3ljTqtsKiGHXVcMT3BlbkFJLK7U # Password for admin user initialization. # If left unset, admin user will not be prompted for a password # when creating the initial admin account. # The length of the password cannot exceed 30 characters. INIT_PASSWORD= # Deployment environment. # Supported values are `PRODUCTION`, `TESTING`. Default is `PRODUCTION`. # Testing environment. There will be a distinct color label on the front-end page, # indicating that this environment is a testing environment. DEPLOY_ENV=PRODUCTION # Whether to enable the version check policy. # If set to empty, https://updates.dify.ai will be called for version check. CHECK_UPDATE_URL=https://updates.dify.ai # Used to change the OpenAI base address, default is https://api.openai.com/v1. # When OpenAI cannot be accessed in China, replace it with a domestic mirror address, # or when a local model provides OpenAI compatible API, it can be replaced. OPENAI_API_BASE=https://api.openai.com/v1 # When enabled, migrations will be executed prior to application startup # and the application will start after the migrations have completed. MIGRATION_ENABLED=true # File Access Time specifies a time interval in seconds for the file to be accessed. # The default value is 300 seconds. FILES_ACCESS_TIMEOUT=300 # Access token expiration time in minutes ACCESS_TOKEN_EXPIRE_MINUTES=60 # Refresh token expiration time in days REFRESH_TOKEN_EXPIRE_DAYS=30 # The maximum number of active requests for the application, where 0 means unlimited, should be a non-negative integer. APP_MAX_ACTIVE_REQUESTS=0 APP_MAX_EXECUTION_TIME=1200 # ------------------------------ # Container Startup Related Configuration # Only effective when starting with docker image or docker-compose. # ------------------------------ # API service binding address, default: 0.0.0.0, i.e., all addresses can be accessed. DIFY_BIND_ADDRESS=0.0.0.0 # API service binding port number, default 5001. DIFY_PORT=5001 # The number of API server workers, i.e., the number of workers. # Formula: number of cpu cores x 2 + 1 for sync, 1 for Gevent # Reference: https://docs.gunicorn.org/en/stable/design.html#how-many-workers SERVER_WORKER_AMOUNT=1 # Defaults to gevent. If using windows, it can be switched to sync or solo. SERVER_WORKER_CLASS=gevent # Default number of worker connections, the default is 10. SERVER_WORKER_CONNECTIONS=10 # Similar to SERVER_WORKER_CLASS. # If using windows, it can be switched to sync or solo. CELERY_WORKER_CLASS= # Request handling timeout. The default is 200, # it is recommended to set it to 360 to support a longer sse connection time. GUNICORN_TIMEOUT=360 # The number of Celery workers. The default is 1, and can be set as needed. CELERY_WORKER_AMOUNT= # Flag indicating whether to enable autoscaling of Celery workers. # # Autoscaling is useful when tasks are CPU intensive and can be dynamically # allocated and deallocated based on the workload. # # When autoscaling is enabled, the maximum and minimum number of workers can # be specified. The autoscaling algorithm will dynamically adjust the number # of workers within the specified range. # # Default is false (i.e., autoscaling is disabled). # # Example: # CELERY_AUTO_SCALE=true CELERY_AUTO_SCALE=false # The maximum number of Celery workers that can be autoscaled. # This is optional and only used when autoscaling is enabled. # Default is not set. CELERY_MAX_WORKERS= # The minimum number of Celery workers that can be autoscaled. # This is optional and only used when autoscaling is enabled. # Default is not set. CELERY_MIN_WORKERS= # API Tool configuration API_TOOL_DEFAULT_CONNECT_TIMEOUT=10 API_TOOL_DEFAULT_READ_TIMEOUT=60 # ------------------------------- # Datasource Configuration # -------------------------------- ENABLE_WEBSITE_JINAREADER=true ENABLE_WEBSITE_FIRECRAWL=true ENABLE_WEBSITE_WATERCRAWL=true # ------------------------------ # Database Configuration # The database uses PostgreSQL. Please use the public schema. # It is consistent with the configuration in the 'db' service below. # ------------------------------ DB_USERNAME=postgres DB_PASSWORD=difyai123456 DB_HOST=db DB_PORT=5432 DB_DATABASE=dify # The size of the database connection pool. # The default is 30 connections, which can be appropriately increased. SQLALCHEMY_POOL_SIZE=30 # The default is 10 connections, which allows temporary overflow beyond the pool size. SQLALCHEMY_MAX_OVERFLOW=10 # Database connection pool recycling time, the default is 3600 seconds. SQLALCHEMY_POOL_RECYCLE=3600 # Whether to print SQL, default is false. SQLALCHEMY_ECHO=false # If True, will test connections for liveness upon each checkout SQLALCHEMY_POOL_PRE_PING=false # Whether to enable the Last in first out option or use default FIFO queue if is false SQLALCHEMY_POOL_USE_LIFO=false # Maximum number of connections to the database # Default is 100 # # Reference: https://www.postgresql.org/docs/current/runtime-config-connection.html#GUC-MAX-CONNECTIONS POSTGRES_MAX_CONNECTIONS=100 # Sets the amount of shared memory used for postgres's shared buffers. # Default is 128MB # Recommended value: 25% of available memory # Reference: https://www.postgresql.org/docs/current/runtime-config-resource.html#GUC-SHARED-BUFFERS POSTGRES_SHARED_BUFFERS=128MB # Sets the amount of memory used by each database worker for working space. # Default is 4MB # # Reference: https://www.postgresql.org/docs/current/runtime-config-resource.html#GUC-WORK-MEM POSTGRES_WORK_MEM=4MB # Sets the amount of memory reserved for maintenance activities. # Default is 64MB # # Reference: https://www.postgresql.org/docs/current/runtime-config-resource.html#GUC-MAINTENANCE-WORK-MEM POSTGRES_MAINTENANCE_WORK_MEM=64MB # Sets the planner's assumption about the effective cache size. # Default is 4096MB # # Reference: https://www.postgresql.org/docs/current/runtime-config-query.html#GUC-EFFECTIVE-CACHE-SIZE POSTGRES_EFFECTIVE_CACHE_SIZE=4096MB # ------------------------------ # Redis Configuration # This Redis configuration is used for caching and for pub/sub during conversation. # ------------------------------ REDIS_HOST=redis REDIS_PORT=6379 REDIS_USERNAME= REDIS_PASSWORD=difyai123456 REDIS_USE_SSL=false # SSL configuration for Redis (when REDIS_USE_SSL=true) REDIS_SSL_CERT_REQS=CERT_NONE # Options: CERT_NONE, CERT_OPTIONAL, CERT_REQUIRED REDIS_SSL_CA_CERTS= # Path to CA certificate file for SSL verification REDIS_SSL_CERTFILE= # Path to client certificate file for SSL authentication REDIS_SSL_KEYFILE= # Path to client private key file for SSL authentication REDIS_DB=0 # Whether to use Redis Sentinel mode. # If set to true, the application will automatically discover and connect to the master node through Sentinel. REDIS_USE_SENTINEL=false # List of Redis Sentinel nodes. If Sentinel mode is enabled, provide at least one Sentinel IP and port. # Format: `&lt;sentinel1_ip&gt;:&lt;sentinel1_port&gt;,&lt;sentinel2_ip&gt;:&lt;sentinel2_port&gt;,&lt;sentinel3_ip&gt;:&lt;sentinel3_port&gt;` REDIS_SENTINELS= REDIS_SENTINEL_SERVICE_NAME= REDIS_SENTINEL_USERNAME= REDIS_SENTINEL_PASSWORD= REDIS_SENTINEL_SOCKET_TIMEOUT=0.1 # List of Redis Cluster nodes. If Cluster mode is enabled, provide at least one Cluster IP and port. # Format: `&lt;Cluster1_ip&gt;:&lt;Cluster1_port&gt;,&lt;Cluster2_ip&gt;:&lt;Cluster2_port&gt;,&lt;Cluster3_ip&gt;:&lt;Cluster3_port&gt;` REDIS_USE_CLUSTERS=false REDIS_CLUSTERS= REDIS_CLUSTERS_PASSWORD= # ------------------------------ # Celery Configuration # ------------------------------ # Use standalone redis as the broker, and redis db 1 for celery broker. (redis_username is usually set by defualt as empty) # Format as follows: `redis://&lt;redis_username&gt;:&lt;redis_password&gt;@&lt;redis_host&gt;:&lt;redis_port&gt;/&lt;redis_database&gt;`. # Example: redis://:difyai123456@redis:6379/1 # If use Redis Sentinel, format as follows: `sentinel://&lt;redis_username&gt;:&lt;redis_password&gt;@&lt;sentinel_host1&gt;:&lt;sentinel_port&gt;/&lt;redis_database&gt;` # For high availability, you can configure multiple Sentinel nodes (if provided) separated by semicolons like below example: # Example: sentinel://:difyai123456@localhost:26379/1;sentinel://:difyai12345@localhost:26379/1;sentinel://:difyai12345@localhost:26379/1 CELERY_BROKER_URL=redis://:difyai123456@redis:6379/1 CELERY_BACKEND=redis BROKER_USE_SSL=false # If you are using Redis Sentinel for high availability, configure the following settings. CELERY_USE_SENTINEL=false CELERY_SENTINEL_MASTER_NAME= CELERY_SENTINEL_PASSWORD= CELERY_SENTINEL_SOCKET_TIMEOUT=0.1 # ------------------------------ # CORS Configuration # Used to set the front-end cross-domain access policy. # ------------------------------ # Specifies the allowed origins for cross-origin requests to the Web API, # e.g. https://dify.app or * for all origins. WEB_API_CORS_ALLOW_ORIGINS=* # Specifies the allowed origins for cross-origin requests to the console API, # e.g. https://cloud.dify.ai or * for all origins. CONSOLE_CORS_ALLOW_ORIGINS=* # ------------------------------ # File Storage Configuration # ------------------------------ # The type of storage to use for storing user files. STORAGE_TYPE=opendal # Apache OpenDAL Configuration # The configuration for OpenDAL consists of the following format: OPENDAL_&lt;SCHEME_NAME&gt;_&lt;CONFIG_NAME&gt;. # You can find all the service configurations (CONFIG_NAME) in the repository at: https://github.com/apache/opendal/tree/main/core/src/services. # Dify will scan configurations starting with OPENDAL_&lt;SCHEME_NAME&gt; and automatically apply them. # The scheme name for the OpenDAL storage. OPENDAL_SCHEME=fs # Configurations for OpenDAL Local File System. OPENDAL_FS_ROOT=storage # ClickZetta Volume Configuration (for storage backend) # To use ClickZetta Volume as storage backend, set STORAGE_TYPE=clickzetta-volume # Note: ClickZetta Volume will reuse the existing CLICKZETTA_* connection parameters # Volume type selection (three types available): # - user: Personal/small team use, simple config, user-level permissions # - table: Enterprise multi-tenant, smart routing, table-level + user-level permissions # - external: Data lake integration, external storage connection, volume-level + storage-level permissions CLICKZETTA_VOLUME_TYPE=user # External Volume name (required only when TYPE=external) CLICKZETTA_VOLUME_NAME= # Table Volume table prefix (used only when TYPE=table) CLICKZETTA_VOLUME_TABLE_PREFIX=dataset_ # Dify file directory prefix (isolates from other apps, recommended to keep default) CLICKZETTA_VOLUME_DIFY_PREFIX=dify_km # S3 Configuration # S3_ENDPOINT= S3_REGION=us-east-1 S3_BUCKET_NAME=difyai S3_ACCESS_KEY= S3_SECRET_KEY= # Whether to use AWS managed IAM roles for authenticating with the S3 service. # If set to false, the access key and secret key must be provided. S3_USE_AWS_MANAGED_IAM=false # Azure Blob Configuration # AZURE_BLOB_ACCOUNT_NAME=difyai AZURE_BLOB_ACCOUNT_KEY=difyai AZURE_BLOB_CONTAINER_NAME=difyai-container AZURE_BLOB_ACCOUNT_URL=https://&lt;your_account_name&gt;.blob.core.windows.net # Google Storage Configuration # GOOGLE_STORAGE_BUCKET_NAME=your-bucket-name GOOGLE_STORAGE_SERVICE_ACCOUNT_JSON_BASE64= # The Alibaba Cloud OSS configurations, # ALIYUN_OSS_BUCKET_NAME=your-bucket-name ALIYUN_OSS_ACCESS_KEY=your-access-key ALIYUN_OSS_SECRET_KEY=your-secret-key ALIYUN_OSS_ENDPOINT=https://oss-ap-southeast-1-internal.aliyuncs.com ALIYUN_OSS_REGION=ap-southeast-1 ALIYUN_OSS_AUTH_VERSION=v4 # Don't start with '/'. OSS doesn't support leading slash in object names. ALIYUN_OSS_PATH=your-path # Tencent COS Configuration # TENCENT_COS_BUCKET_NAME=your-bucket-name TENCENT_COS_SECRET_KEY=your-secret-key TENCENT_COS_SECRET_ID=your-secret-id TENCENT_COS_REGION=your-region TENCENT_COS_SCHEME=your-scheme # Oracle Storage Configuration # OCI_ENDPOINT=https://your-object-storage-namespace.compat.objectstorage.us-ashburn-1.oraclecloud.com OCI_BUCKET_NAME=your-bucket-name OCI_ACCESS_KEY=your-access-key OCI_SECRET_KEY=your-secret-key OCI_REGION=us-ashburn-1 # Huawei OBS Configuration # HUAWEI_OBS_BUCKET_NAME=your-bucket-name HUAWEI_OBS_SECRET_KEY=your-secret-key HUAWEI_OBS_ACCESS_KEY=your-access-key HUAWEI_OBS_SERVER=your-server-url # Volcengine TOS Configuration # VOLCENGINE_TOS_BUCKET_NAME=your-bucket-name VOLCENGINE_TOS_SECRET_KEY=your-secret-key VOLCENGINE_TOS_ACCESS_KEY=your-access-key VOLCENGINE_TOS_ENDPOINT=your-server-url VOLCENGINE_TOS_REGION=your-region # Baidu OBS Storage Configuration # BAIDU_OBS_BUCKET_NAME=your-bucket-name BAIDU_OBS_SECRET_KEY=your-secret-key BAIDU_OBS_ACCESS_KEY=your-access-key BAIDU_OBS_ENDPOINT=your-server-url # Supabase Storage Configuration # SUPABASE_BUCKET_NAME=your-bucket-name SUPABASE_API_KEY=your-access-key SUPABASE_URL=your-server-url # ------------------------------ # Vector Database Configuration # ------------------------------ # The type of vector store to use. # Supported values are `weaviate`, `qdrant`, `milvus`, `myscale`, `relyt`, `pgvector`, `pgvecto-rs`, `chroma`, `opensearch`, `oracle`, `tencent`, `elasticsearch`, `elasticsearch-ja`, `analyticdb`, `couchbase`, `vikingdb`, `oceanbase`, `opengauss`, `tablestore`,`vastbase`,`tidb`,`tidb_on_qdrant`,`baidu`,`lindorm`,`huawei_cloud`,`upstash`, `matrixone`, `clickzetta`. VECTOR_STORE=weaviate # Prefix used to create collection name in vector database VECTOR_INDEX_NAME_PREFIX=Vector_index # The Weaviate endpoint URL. Only available when VECTOR_STORE is `weaviate`. WEAVIATE_ENDPOINT=http://weaviate:8080 WEAVIATE_API_KEY=WVF5YThaHlkYwhGUSmCRgsX3tD5ngdN8pkih # The Qdrant endpoint URL. Only available when VECTOR_STORE is `qdrant`. QDRANT_URL=http://qdrant:6333 QDRANT_API_KEY=difyai123456 QDRANT_CLIENT_TIMEOUT=20 QDRANT_GRPC_ENABLED=false QDRANT_GRPC_PORT=6334 QDRANT_REPLICATION_FACTOR=1 # Milvus configuration. Only available when VECTOR_STORE is `milvus`. # The milvus uri. MILVUS_URI=http://host.docker.internal:19530 MILVUS_DATABASE= MILVUS_TOKEN= MILVUS_USER= MILVUS_PASSWORD= MILVUS_ENABLE_HYBRID_SEARCH=False MILVUS_ANALYZER_PARAMS= # MyScale configuration, only available when VECTOR_STORE is `myscale` # For multi-language support, please set MYSCALE_FTS_PARAMS with referring to: # https://myscale.com/docs/en/text-search/#understanding-fts-index-parameters MYSCALE_HOST=myscale MYSCALE_PORT=8123 MYSCALE_USER=default MYSCALE_PASSWORD= MYSCALE_DATABASE=dify MYSCALE_FTS_PARAMS= # Couchbase configurations, only available when VECTOR_STORE is `couchbase` # The connection string must include hostname defined in the docker-compose file (couchbase-server in this case) COUCHBASE_CONNECTION_STRING=couchbase://couchbase-server COUCHBASE_USER=Administrator COUCHBASE_PASSWORD=password COUCHBASE_BUCKET_NAME=Embeddings COUCHBASE_SCOPE_NAME=_default # pgvector configurations, only available when VECTOR_STORE is `pgvector` PGVECTOR_HOST=pgvector PGVECTOR_PORT=5432 PGVECTOR_USER=postgres PGVECTOR_PASSWORD=difyai123456 PGVECTOR_DATABASE=dify PGVECTOR_MIN_CONNECTION=1 PGVECTOR_MAX_CONNECTION=5 PGVECTOR_PG_BIGM=false PGVECTOR_PG_BIGM_VERSION=1.2-20240606 # vastbase configurations, only available when VECTOR_STORE is `vastbase` VASTBASE_HOST=vastbase VASTBASE_PORT=5432 VASTBASE_USER=dify VASTBASE_PASSWORD=Difyai123456 VASTBASE_DATABASE=dify VASTBASE_MIN_CONNECTION=1 VASTBASE_MAX_CONNECTION=5 # pgvecto-rs configurations, only available when VECTOR_STORE is `pgvecto-rs` PGVECTO_RS_HOST=pgvecto-rs PGVECTO_RS_PORT=5432 PGVECTO_RS_USER=postgres PGVECTO_RS_PASSWORD=difyai123456 PGVECTO_RS_DATABASE=dify # analyticdb configurations, only available when VECTOR_STORE is `analyticdb` ANALYTICDB_KEY_ID=your-ak ANALYTICDB_KEY_SECRET=your-sk ANALYTICDB_REGION_ID=cn-hangzhou ANALYTICDB_INSTANCE_ID=gp-ab123456 ANALYTICDB_ACCOUNT=testaccount ANALYTICDB_PASSWORD=testpassword ANALYTICDB_NAMESPACE=dify ANALYTICDB_NAMESPACE_PASSWORD=difypassword ANALYTICDB_HOST=gp-test.aliyuncs.com ANALYTICDB_PORT=5432 ANALYTICDB_MIN_CONNECTION=1 ANALYTICDB_MAX_CONNECTION=5 # TiDB vector configurations, only available when VECTOR_STORE is `tidb_vector` TIDB_VECTOR_HOST=tidb TIDB_VECTOR_PORT=4000 TIDB_VECTOR_USER= TIDB_VECTOR_PASSWORD= TIDB_VECTOR_DATABASE=dify # Matrixone vector configurations. MATRIXONE_HOST=matrixone MATRIXONE_PORT=6001 MATRIXONE_USER=dump MATRIXONE_PASSWORD=111 MATRIXONE_DATABASE=dify # Tidb on qdrant configuration, only available when VECTOR_STORE is `tidb_on_qdrant` TIDB_ON_QDRANT_URL=http://127.0.0.1 TIDB_ON_QDRANT_API_KEY=dify TIDB_ON_QDRANT_CLIENT_TIMEOUT=20 TIDB_ON_QDRANT_GRPC_ENABLED=false TIDB_ON_QDRANT_GRPC_PORT=6334 TIDB_PUBLIC_KEY=dify TIDB_PRIVATE_KEY=dify TIDB_API_URL=http://127.0.0.1 TIDB_IAM_API_URL=http://127.0.0.1 TIDB_REGION=regions/aws-us-east-1 TIDB_PROJECT_ID=dify TIDB_SPEND_LIMIT=100 # Chroma configuration, only available when VECTOR_STORE is `chroma` CHROMA_HOST=127.0.0.1 CHROMA_PORT=8000 CHROMA_TENANT=default_tenant CHROMA_DATABASE=default_database CHROMA_AUTH_PROVIDER=chromadb.auth.token_authn.TokenAuthClientProvider CHROMA_AUTH_CREDENTIALS= # Oracle configuration, only available when VECTOR_STORE is `oracle` ORACLE_USER=dify ORACLE_PASSWORD=dify ORACLE_DSN=oracle:1521/FREEPDB1 ORACLE_CONFIG_DIR=/app/api/storage/wallet ORACLE_WALLET_LOCATION=/app/api/storage/wallet ORACLE_WALLET_PASSWORD=dify ORACLE_IS_AUTONOMOUS=false # relyt configurations, only available when VECTOR_STORE is `relyt` RELYT_HOST=db RELYT_PORT=5432 RELYT_USER=postgres RELYT_PASSWORD=difyai123456 RELYT_DATABASE=postgres # open search configuration, only available when VECTOR_STORE is `opensearch` OPENSEARCH_HOST=opensearch OPENSEARCH_PORT=9200 OPENSEARCH_SECURE=true OPENSEARCH_VERIFY_CERTS=true OPENSEARCH_AUTH_METHOD=basic OPENSEARCH_USER=admin OPENSEARCH_PASSWORD=admin # If using AWS managed IAM, e.g. Managed Cluster or OpenSearch Serverless OPENSEARCH_AWS_REGION=ap-southeast-1 OPENSEARCH_AWS_SERVICE=aoss # tencent vector configurations, only available when VECTOR_STORE is `tencent` TENCENT_VECTOR_DB_URL=http://127.0.0.1 TENCENT_VECTOR_DB_API_KEY=dify TENCENT_VECTOR_DB_TIMEOUT=30 TENCENT_VECTOR_DB_USERNAME=dify TENCENT_VECTOR_DB_DATABASE=dify TENCENT_VECTOR_DB_SHARD=1 TENCENT_VECTOR_DB_REPLICAS=2 TENCENT_VECTOR_DB_ENABLE_HYBRID_SEARCH=false # ElasticSearch configuration, only available when VECTOR_STORE is `elasticsearch` ELASTICSEARCH_HOST=0.0.0.0 ELASTICSEARCH_PORT=9200 ELASTICSEARCH_USERNAME=elastic ELASTICSEARCH_PASSWORD=elastic KIBANA_PORT=5601 # Using ElasticSearch Cloud Serverless, or not. ELASTICSEARCH_USE_CLOUD=false ELASTICSEARCH_CLOUD_URL=YOUR-ELASTICSEARCH_CLOUD_URL ELASTICSEARCH_API_KEY=YOUR-ELASTICSEARCH_API_KEY ELASTICSEARCH_VERIFY_CERTS=False ELASTICSEARCH_CA_CERTS= ELASTICSEARCH_REQUEST_TIMEOUT=100000 ELASTICSEARCH_RETRY_ON_TIMEOUT=True ELASTICSEARCH_MAX_RETRIES=10 # baidu vector configurations, only available when VECTOR_STORE is `baidu` BAIDU_VECTOR_DB_ENDPOINT=http://127.0.0.1:5287 BAIDU_VECTOR_DB_CONNECTION_TIMEOUT_MS=30000 BAIDU_VECTOR_DB_ACCOUNT=root BAIDU_VECTOR_DB_API_KEY=dify BAIDU_VECTOR_DB_DATABASE=dify BAIDU_VECTOR_DB_SHARD=1 BAIDU_VECTOR_DB_REPLICAS=3 # VikingDB configurations, only available when VECTOR_STORE is `vikingdb` VIKINGDB_ACCESS_KEY=your-ak VIKINGDB_SECRET_KEY=your-sk VIKINGDB_REGION=cn-shanghai VIKINGDB_HOST=api-vikingdb.xxx.volces.com VIKINGDB_SCHEMA=http VIKINGDB_CONNECTION_TIMEOUT=30 VIKINGDB_SOCKET_TIMEOUT=30 # Lindorm configuration, only available when VECTOR_STORE is `lindorm` LINDORM_URL=http://lindorm:30070 LINDORM_USERNAME=lindorm LINDORM_PASSWORD=lindorm LINDORM_QUERY_TIMEOUT=1 # OceanBase Vector configuration, only available when VECTOR_STORE is `oceanbase` OCEANBASE_VECTOR_HOST=oceanbase OCEANBASE_VECTOR_PORT=2881 OCEANBASE_VECTOR_USER=root@test OCEANBASE_VECTOR_PASSWORD=difyai123456 OCEANBASE_VECTOR_DATABASE=test OCEANBASE_CLUSTER_NAME=difyai OCEANBASE_MEMORY_LIMIT=6G OCEANBASE_ENABLE_HYBRID_SEARCH=false # opengauss configurations, only available when VECTOR_STORE is `opengauss` OPENGAUSS_HOST=opengauss OPENGAUSS_PORT=6600 OPENGAUSS_USER=postgres OPENGAUSS_PASSWORD=Dify@123 OPENGAUSS_DATABASE=dify OPENGAUSS_MIN_CONNECTION=1 OPENGAUSS_MAX_CONNECTION=5 OPENGAUSS_ENABLE_PQ=false # huawei cloud search service vector configurations, only available when VECTOR_STORE is `huawei_cloud` HUAWEI_CLOUD_HOSTS=https://127.0.0.1:9200 HUAWEI_CLOUD_USER=admin HUAWEI_CLOUD_PASSWORD=admin # Upstash Vector configuration, only available when VECTOR_STORE is `upstash` UPSTASH_VECTOR_URL=https://xxx-vector.upstash.io UPSTASH_VECTOR_TOKEN=dify # TableStore Vector configuration # (only used when VECTOR_STORE is tablestore) TABLESTORE_ENDPOINT=https://instance-name.cn-hangzhou.ots.aliyuncs.com TABLESTORE_INSTANCE_NAME=instance-name TABLESTORE_ACCESS_KEY_ID=xxx TABLESTORE_ACCESS_KEY_SECRET=xxx TABLESTORE_NORMALIZE_FULLTEXT_BM25_SCORE=false # Clickzetta configuration, only available when VECTOR_STORE is `clickzetta` CLICKZETTA_USERNAME= CLICKZETTA_PASSWORD= CLICKZETTA_INSTANCE= CLICKZETTA_SERVICE=api.clickzetta.com CLICKZETTA_WORKSPACE=quick_start CLICKZETTA_VCLUSTER=default_ap CLICKZETTA_SCHEMA=dify CLICKZETTA_BATCH_SIZE=100 CLICKZETTA_ENABLE_INVERTED_INDEX=true CLICKZETTA_ANALYZER_TYPE=chinese CLICKZETTA_ANALYZER_MODE=smart CLICKZETTA_VECTOR_DISTANCE_FUNCTION=cosine_distance # ------------------------------ # Knowledge Configuration # ------------------------------ # Upload file size limit, default 15M. UPLOAD_FILE_SIZE_LIMIT=15 # The maximum number of files that can be uploaded at a time, default 5. UPLOAD_FILE_BATCH_LIMIT=5 # ETL type, support: `dify`, `Unstructured` # `dify` Dify's proprietary file extraction scheme # `Unstructured` Unstructured.io file extraction scheme ETL_TYPE=dify # Unstructured API path and API key, needs to be configured when ETL_TYPE is Unstructured # Or using Unstructured for document extractor node for pptx. # For example: http://unstructured:8000/general/v0/general UNSTRUCTURED_API_URL= UNSTRUCTURED_API_KEY= SCARF_NO_ANALYTICS=true # ------------------------------ # Model Configuration # ------------------------------ # The maximum number of tokens allowed for prompt generation. # This setting controls the upper limit of tokens that can be used by the LLM # when generating a prompt in the prompt generation tool. # Default: 512 tokens. PROMPT_GENERATION_MAX_TOKENS=512 # The maximum number of tokens allowed for code generation. # This setting controls the upper limit of tokens that can be used by the LLM # when generating code in the code generation tool. # Default: 1024 tokens. CODE_GENERATION_MAX_TOKENS=1024 # Enable or disable plugin based token counting. If disabled, token counting will return 0. # This can improve performance by skipping token counting operations. # Default: false (disabled). PLUGIN_BASED_TOKEN_COUNTING_ENABLED=false # ------------------------------ # Multi-modal Configuration # ------------------------------ # The format of the image/video/audio/document sent when the multi-modal model is input, # the default is base64, optional url. # The delay of the call in url mode will be lower than that in base64 mode. # It is generally recommended to use the more compatible base64 mode. # If configured as url, you need to configure FILES_URL as an externally accessible address so that the multi-modal model can access the image/video/audio/document. MULTIMODAL_SEND_FORMAT=base64 # Upload image file size limit, default 10M. UPLOAD_IMAGE_FILE_SIZE_LIMIT=10 # Upload video file size limit, default 100M. UPLOAD_VIDEO_FILE_SIZE_LIMIT=100 # Upload audio file size limit, default 50M. UPLOAD_AUDIO_FILE_SIZE_LIMIT=50 # ------------------------------ # Sentry Configuration # Used for application monitoring and error log tracking. # ------------------------------ SENTRY_DSN= # API Service Sentry DSN address, default is empty, when empty, # all monitoring information is not reported to Sentry. # If not set, Sentry error reporting will be disabled. API_SENTRY_DSN= # API Service The reporting ratio of Sentry events, if it is 0.01, it is 1%. API_SENTRY_TRACES_SAMPLE_RATE=1.0 # API Service The reporting ratio of Sentry profiles, if it is 0.01, it is 1%. API_SENTRY_PROFILES_SAMPLE_RATE=1.0 # Web Service Sentry DSN address, default is empty, when empty, # all monitoring information is not reported to Sentry. # If not set, Sentry error reporting will be disabled. WEB_SENTRY_DSN= # ------------------------------ # Notion Integration Configuration # Variables can be obtained by applying for Notion integration: https://www.notion.so/my-integrations # ------------------------------ # Configure as "public" or "internal". # Since Notion's OAuth redirect URL only supports HTTPS, # if deploying locally, please use Notion's internal integration. NOTION_INTEGRATION_TYPE=public # Notion OAuth client secret (used for public integration type) NOTION_CLIENT_SECRET= # Notion OAuth client id (used for public integration type) NOTION_CLIENT_ID= # Notion internal integration secret. # If the value of NOTION_INTEGRATION_TYPE is "internal", # you need to configure this variable. NOTION_INTERNAL_SECRET= # ------------------------------ # Mail related configuration # ------------------------------ # Mail type, support: resend, smtp, sendgrid MAIL_TYPE=resend # Default send from email address, if not specified # If using SendGrid, use the 'from' field for authentication if necessary. 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 # SMTP server configuration, used when MAIL_TYPE is `smtp` SMTP_SERVER= SMTP_PORT=465 SMTP_USERNAME= SMTP_PASSWORD= SMTP_USE_TLS=true SMTP_OPPORTUNISTIC_TLS=false # Sendgid configuration SENDGRID_API_KEY= # ------------------------------ # Others Configuration # ------------------------------ # Maximum length of segmentation tokens for indexing INDEXING_MAX_SEGMENTATION_TOKENS_LENGTH=4000 # Member invitation link valid time (hours), # Default: 72. INVITE_EXPIRY_HOURS=72 # Reset password token valid time (minutes), RESET_PASSWORD_TOKEN_EXPIRY_MINUTES=5 CHANGE_EMAIL_TOKEN_EXPIRY_MINUTES=5 OWNER_TRANSFER_TOKEN_EXPIRY_MINUTES=5 # The sandbox service endpoint. CODE_EXECUTION_ENDPOINT=http://sandbox:8194 CODE_EXECUTION_API_KEY=dify-sandbox CODE_MAX_NUMBER=9223372036854775807 CODE_MIN_NUMBER=-9223372036854775808 CODE_MAX_DEPTH=5 CODE_MAX_PRECISION=20 CODE_MAX_STRING_LENGTH=80000 CODE_MAX_STRING_ARRAY_LENGTH=30 CODE_MAX_OBJECT_ARRAY_LENGTH=30 CODE_MAX_NUMBER_ARRAY_LENGTH=1000 CODE_EXECUTION_CONNECT_TIMEOUT=10 CODE_EXECUTION_READ_TIMEOUT=60 CODE_EXECUTION_WRITE_TIMEOUT=10 TEMPLATE_TRANSFORM_MAX_LENGTH=80000 # Workflow runtime configuration WORKFLOW_MAX_EXECUTION_STEPS=500 WORKFLOW_MAX_EXECUTION_TIME=1200 WORKFLOW_CALL_MAX_DEPTH=5 MAX_VARIABLE_SIZE=204800 WORKFLOW_PARALLEL_DEPTH_LIMIT=3 WORKFLOW_FILE_UPLOAD_LIMIT=10 # Workflow storage configuration # Options: rdbms, hybrid # rdbms: Use only the relational database (default) # hybrid: Save new data to object storage, read from both object storage and RDBMS WORKFLOW_NODE_EXECUTION_STORAGE=rdbms # Repository configuration # Core workflow execution repository implementation # Options: # - core.repositories.sqlalchemy_workflow_execution_repository.SQLAlchemyWorkflowExecutionRepository (default) # - core.repositories.celery_workflow_execution_repository.CeleryWorkflowExecutionRepository CORE_WORKFLOW_EXECUTION_REPOSITORY=core.repositories.sqlalchemy_workflow_execution_repository.SQLAlchemyWorkflowExecutionRepository # Core workflow node execution repository implementation # Options: # - core.repositories.sqlalchemy_workflow_node_execution_repository.SQLAlchemyWorkflowNodeExecutionRepository (default) # - core.repositories.celery_workflow_node_execution_repository.CeleryWorkflowNodeExecutionRepository CORE_WORKFLOW_NODE_EXECUTION_REPOSITORY=core.repositories.sqlalchemy_workflow_node_execution_repository.SQLAlchemyWorkflowNodeExecutionRepository # API workflow run repository implementation API_WORKFLOW_RUN_REPOSITORY=repositories.sqlalchemy_api_workflow_run_repository.DifyAPISQLAlchemyWorkflowRunRepository # API workflow node execution repository implementation API_WORKFLOW_NODE_EXECUTION_REPOSITORY=repositories.sqlalchemy_api_workflow_node_execution_repository.DifyAPISQLAlchemyWorkflowNodeExecutionRepository # Workflow log cleanup configuration # Enable automatic cleanup of workflow run logs to manage database size WORKFLOW_LOG_CLEANUP_ENABLED=false # Number of days to retain workflow run logs (default: 30 days) WORKFLOW_LOG_RETENTION_DAYS=30 # Batch size for workflow log cleanup operations (default: 100) WORKFLOW_LOG_CLEANUP_BATCH_SIZE=100 # HTTP request node in workflow configuration HTTP_REQUEST_NODE_MAX_BINARY_SIZE=10485760 HTTP_REQUEST_NODE_MAX_TEXT_SIZE=1048576 HTTP_REQUEST_NODE_SSL_VERIFY=True # Respect X-* headers to redirect clients RESPECT_XFORWARD_HEADERS_ENABLED=false # SSRF Proxy server HTTP URL SSRF_PROXY_HTTP_URL=http://ssrf_proxy:3128 # SSRF Proxy server HTTPS URL SSRF_PROXY_HTTPS_URL=http://ssrf_proxy:3128 # Maximum loop count in the workflow LOOP_NODE_MAX_COUNT=100 # The maximum number of tools that can be used in the agent. MAX_TOOLS_NUM=10 # Maximum number of Parallelism branches in the workflow MAX_PARALLEL_LIMIT=10 # The maximum number of iterations for agent setting MAX_ITERATIONS_NUM=99 # ------------------------------ # Environment Variables for web Service # ------------------------------ # The timeout for the text generation in millisecond TEXT_GENERATION_TIMEOUT_MS=60000 # Allow rendering unsafe URLs which have "data:" scheme. ALLOW_UNSAFE_DATA_SCHEME=false # Maximum number of tree depth in the workflow MAX_TREE_DEPTH=50 # ------------------------------ # Environment Variables for db Service # ------------------------------ # The name of the default postgres user. POSTGRES_USER=${DB_USERNAME} # The password for the default postgres user. POSTGRES_PASSWORD=${DB_PASSWORD} # The name of the default postgres database. POSTGRES_DB=${DB_DATABASE} # postgres data directory PGDATA=/var/lib/postgresql/data/pgdata # ------------------------------ # Environment Variables for sandbox Service # ------------------------------ # The API key for the sandbox service SANDBOX_API_KEY=dify-sandbox # The mode in which the Gin framework runs SANDBOX_GIN_MODE=release # The timeout for the worker in seconds SANDBOX_WORKER_TIMEOUT=15 # Enable network for the sandbox service SANDBOX_ENABLE_NETWORK=true # HTTP proxy URL for SSRF protection SANDBOX_HTTP_PROXY=http://ssrf_proxy:3128 # HTTPS proxy URL for SSRF protection SANDBOX_HTTPS_PROXY=http://ssrf_proxy:3128 # The port on which the sandbox service runs SANDBOX_PORT=8194 # ------------------------------ # Environment Variables for weaviate Service # (only used when VECTOR_STORE is weaviate) # ------------------------------ WEAVIATE_PERSISTENCE_DATA_PATH=/var/lib/weaviate WEAVIATE_QUERY_DEFAULTS_LIMIT=25 WEAVIATE_AUTHENTICATION_ANONYMOUS_ACCESS_ENABLED=true WEAVIATE_DEFAULT_VECTORIZER_MODULE=none WEAVIATE_CLUSTER_HOSTNAME=node1 WEAVIATE_AUTHENTICATION_APIKEY_ENABLED=true WEAVIATE_AUTHENTICATION_APIKEY_ALLOWED_KEYS=WVF5YThaHlkYwhGUSmCRgsX3tD5ngdN8pkih WEAVIATE_AUTHENTICATION_APIKEY_USERS=hello@dify.ai WEAVIATE_AUTHORIZATION_ADMINLIST_ENABLED=true WEAVIATE_AUTHORIZATION_ADMINLIST_USERS=hello@dify.ai # ------------------------------ # Environment Variables for Chroma # (only used when VECTOR_STORE is chroma) # ------------------------------ # Authentication credentials for Chroma server CHROMA_SERVER_AUTHN_CREDENTIALS=difyai123456 # Authentication provider for Chroma server CHROMA_SERVER_AUTHN_PROVIDER=chromadb.auth.token_authn.TokenAuthenticationServerProvider # Persistence setting for Chroma server CHROMA_IS_PERSISTENT=TRUE # ------------------------------ # Environment Variables for Oracle Service # (only used when VECTOR_STORE is oracle) # ------------------------------ ORACLE_PWD=Dify123456 ORACLE_CHARACTERSET=AL32UTF8 # ------------------------------ # Environment Variables for milvus Service # (only used when VECTOR_STORE is milvus) # ------------------------------ # ETCD configuration for auto compaction mode ETCD_AUTO_COMPACTION_MODE=revision # ETCD configuration for auto compaction retention in terms of number of revisions ETCD_AUTO_COMPACTION_RETENTION=1000 # ETCD configuration for backend quota in bytes ETCD_QUOTA_BACKEND_BYTES=4294967296 # ETCD configuration for the number of changes before triggering a snapshot ETCD_SNAPSHOT_COUNT=50000 # MinIO access key for authentication MINIO_ACCESS_KEY=minioadmin # MinIO secret key for authentication MINIO_SECRET_KEY=minioadmin # ETCD service endpoints ETCD_ENDPOINTS=etcd:2379 # MinIO service address MINIO_ADDRESS=minio:9000 # Enable or disable security authorization MILVUS_AUTHORIZATION_ENABLED=true # ------------------------------ # Environment Variables for pgvector / pgvector-rs Service # (only used when VECTOR_STORE is pgvector / pgvector-rs) # ------------------------------ PGVECTOR_PGUSER=postgres # The password for the default postgres user. PGVECTOR_POSTGRES_PASSWORD=difyai123456 # The name of the default postgres database. PGVECTOR_POSTGRES_DB=dify # postgres data directory PGVECTOR_PGDATA=/var/lib/postgresql/data/pgdata # ------------------------------ # Environment Variables for opensearch # (only used when VECTOR_STORE is opensearch) # ------------------------------ OPENSEARCH_DISCOVERY_TYPE=single-node OPENSEARCH_BOOTSTRAP_MEMORY_LOCK=true OPENSEARCH_JAVA_OPTS_MIN=512m OPENSEARCH_JAVA_OPTS_MAX=1024m OPENSEARCH_INITIAL_ADMIN_PASSWORD=Qazwsxedc!@#123 OPENSEARCH_MEMLOCK_SOFT=-1 OPENSEARCH_MEMLOCK_HARD=-1 OPENSEARCH_NOFILE_SOFT=65536 OPENSEARCH_NOFILE_HARD=65536 # ------------------------------ # Environment Variables for Nginx reverse proxy # ------------------------------ NGINX_SERVER_NAME=_ NGINX_HTTPS_ENABLED=false # HTTP port NGINX_PORT=80 # SSL settings are only applied when HTTPS_ENABLED is true NGINX_SSL_PORT=443 # if HTTPS_ENABLED is true, you're required to add your own SSL certificates/keys to the `./nginx/ssl` directory # and modify the env vars below accordingly. NGINX_SSL_CERT_FILENAME=dify.crt NGINX_SSL_CERT_KEY_FILENAME=dify.key NGINX_SSL_PROTOCOLS=TLSv1.1 TLSv1.2 TLSv1.3 # Nginx performance tuning NGINX_WORKER_PROCESSES=auto NGINX_CLIENT_MAX_BODY_SIZE=100M NGINX_KEEPALIVE_TIMEOUT=65 # Proxy settings NGINX_PROXY_READ_TIMEOUT=3600s NGINX_PROXY_SEND_TIMEOUT=3600s # Set true to accept requests for /.well-known/acme-challenge/ NGINX_ENABLE_CERTBOT_CHALLENGE=false # ------------------------------ # Certbot Configuration # ------------------------------ # Email address (required to get certificates from Let's Encrypt) CERTBOT_EMAIL=your_email@example.com # Domain name CERTBOT_DOMAIN=your_domain.com # certbot command options # i.e: --force-renewal --dry-run --test-cert --debug CERTBOT_OPTIONS= # ------------------------------ # Environment Variables for SSRF Proxy # ------------------------------ SSRF_HTTP_PORT=3128 SSRF_COREDUMP_DIR=/var/spool/squid SSRF_REVERSE_PROXY_PORT=8194 SSRF_SANDBOX_HOST=sandbox SSRF_DEFAULT_TIME_OUT=5 SSRF_DEFAULT_CONNECT_TIME_OUT=5 SSRF_DEFAULT_READ_TIME_OUT=5 SSRF_DEFAULT_WRITE_TIME_OUT=5 # ------------------------------ # docker env var for specifying vector db type at startup # (based on the vector db type, the corresponding docker # compose profile will be used) # if you want to use unstructured, add ',unstructured' to the end # ------------------------------ COMPOSE_PROFILES=${VECTOR_STORE:-weaviate} # ------------------------------ # Docker Compose Service Expose Host Port Configurations # ------------------------------ EXPOSE_NGINX_PORT=80 EXPOSE_NGINX_SSL_PORT=443 # ---------------------------------------------------------------------------- # ModelProvider &amp; Tool Position Configuration # Used to specify the model providers and tools that can be used in the app. # ---------------------------------------------------------------------------- # Pin, include, and exclude tools # Use comma-separated values with no spaces between items. # Example: POSITION_TOOL_PINS=bing,google POSITION_TOOL_PINS= POSITION_TOOL_INCLUDES= POSITION_TOOL_EXCLUDES= # Pin, include, and exclude model providers # Use comma-separated values with no spaces between items. # Example: POSITION_PROVIDER_PINS=openai,openllm POSITION_PROVIDER_PINS= POSITION_PROVIDER_INCLUDES= POSITION_PROVIDER_EXCLUDES= # CSP https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP CSP_WHITELIST= # Enable or disable create tidb service job CREATE_TIDB_SERVICE_JOB_ENABLED=false # Maximum number of submitted thread count in a ThreadPool for parallel node execution MAX_SUBMIT_COUNT=100 # The maximum number of top-k value for RAG. TOP_K_MAX_VALUE=10 # ------------------------------ # Plugin Daemon Configuration # ------------------------------ DB_PLUGIN_DATABASE=dify_plugin EXPOSE_PLUGIN_DAEMON_PORT=5002 PLUGIN_DAEMON_PORT=5002 PLUGIN_DAEMON_KEY=lYkiYYT6owG+71oLerGzA7GXCgOT++6ovaezWAjpCjf+Sjc3ZtU+qUEi PLUGIN_DAEMON_URL=http://plugin_daemon:5002 PLUGIN_MAX_PACKAGE_SIZE=52428800 PLUGIN_PPROF_ENABLED=false PLUGIN_DEBUGGING_HOST=0.0.0.0 PLUGIN_DEBUGGING_PORT=5003 EXPOSE_PLUGIN_DEBUGGING_HOST=localhost EXPOSE_PLUGIN_DEBUGGING_PORT=5003 # If this key is changed, DIFY_INNER_API_KEY in plugin_daemon service must also be updated or agent node will fail. PLUGIN_DIFY_INNER_API_KEY=QaHbTe77CtuXmsfyhR7+vRjI/+XbV1AaFy691iy+kGDv2Jvy0/eAh8Y1 PLUGIN_DIFY_INNER_API_URL=http://api:5001 ENDPOINT_URL_TEMPLATE=http://localhost/e/{hook_id} MARKETPLACE_ENABLED=true MARKETPLACE_API_URL=https://marketplace.dify.ai FORCE_VERIFYING_SIGNATURE=true PLUGIN_STDIO_BUFFER_SIZE=1024 PLUGIN_STDIO_MAX_BUFFER_SIZE=5242880 PLUGIN_PYTHON_ENV_INIT_TIMEOUT=120 PLUGIN_MAX_EXECUTION_TIMEOUT=600 # PIP_MIRROR_URL=https://pypi.tuna.tsinghua.edu.cn/simple PIP_MIRROR_URL= # https://github.com/langgenius/dify-plugin-daemon/blob/main/.env.example # Plugin storage type, local aws_s3 tencent_cos azure_blob aliyun_oss volcengine_tos PLUGIN_STORAGE_TYPE=local PLUGIN_STORAGE_LOCAL_ROOT=/app/storage PLUGIN_WORKING_PATH=/app/storage/cwd PLUGIN_INSTALLED_PATH=plugin PLUGIN_PACKAGE_CACHE_PATH=plugin_packages PLUGIN_MEDIA_CACHE_PATH=assets # Plugin oss bucket PLUGIN_STORAGE_OSS_BUCKET= # Plugin oss s3 credentials PLUGIN_S3_USE_AWS=false PLUGIN_S3_USE_AWS_MANAGED_IAM=false PLUGIN_S3_ENDPOINT= PLUGIN_S3_USE_PATH_STYLE=false PLUGIN_AWS_ACCESS_KEY= PLUGIN_AWS_SECRET_KEY= PLUGIN_AWS_REGION= # Plugin oss azure blob PLUGIN_AZURE_BLOB_STORAGE_CONTAINER_NAME= PLUGIN_AZURE_BLOB_STORAGE_CONNECTION_STRING= # Plugin oss tencent cos PLUGIN_TENCENT_COS_SECRET_KEY= PLUGIN_TENCENT_COS_SECRET_ID= PLUGIN_TENCENT_COS_REGION= # Plugin oss aliyun oss PLUGIN_ALIYUN_OSS_REGION= PLUGIN_ALIYUN_OSS_ENDPOINT= PLUGIN_ALIYUN_OSS_ACCESS_KEY_ID= PLUGIN_ALIYUN_OSS_ACCESS_KEY_SECRET= PLUGIN_ALIYUN_OSS_AUTH_VERSION=v4 PLUGIN_ALIYUN_OSS_PATH= # Plugin oss volcengine tos PLUGIN_VOLCENGINE_TOS_ENDPOINT= PLUGIN_VOLCENGINE_TOS_ACCESS_KEY= PLUGIN_VOLCENGINE_TOS_SECRET_KEY= PLUGIN_VOLCENGINE_TOS_REGION= # ------------------------------ # OTLP Collector Configuration # ------------------------------ ENABLE_OTEL=false OTLP_TRACE_ENDPOINT= OTLP_METRIC_ENDPOINT= OTLP_BASE_ENDPOINT=http://localhost:4318 OTLP_API_KEY= OTEL_EXPORTER_OTLP_PROTOCOL= OTEL_EXPORTER_TYPE=otlp OTEL_SAMPLING_RATE=0.1 OTEL_BATCH_EXPORT_SCHEDULE_DELAY=5000 OTEL_MAX_QUEUE_SIZE=2048 OTEL_MAX_EXPORT_BATCH_SIZE=512 OTEL_METRIC_EXPORT_INTERVAL=60000 OTEL_BATCH_EXPORT_TIMEOUT=10000 OTEL_METRIC_EXPORT_TIMEOUT=30000 # Prevent Clickjacking ALLOW_EMBED=false # Dataset queue monitor configuration QUEUE_MONITOR_THRESHOLD=200 # You can configure multiple ones, separated by commas. eg: test1@dify.ai,test2@dify.ai QUEUE_MONITOR_ALERT_EMAILS= # Monitor interval in minutes, default is 30 minutes QUEUE_MONITOR_INTERVAL=30 # Swagger UI configuration SWAGGER_UI_ENABLED=true SWAGGER_UI_PATH=/swagger-ui.html # Celery schedule tasks configuration ENABLE_CLEAN_EMBEDDING_CACHE_TASK=false ENABLE_CLEAN_UNUSED_DATASETS_TASK=false ENABLE_CREATE_TIDB_SERVERLESS_TASK=false ENABLE_UPDATE_TIDB_SERVERLESS_STATUS_TASK=false ENABLE_CLEAN_MESSAGES=false ENABLE_MAIL_CLEAN_DOCUMENT_NOTIFY_TASK=false ENABLE_DATASETS_QUEUE_MONITOR=false ENABLE_CHECK_UPGRADABLE_PLUGIN_TASK=true </code></pre> <p>太长了,其实很多都可以安装好之后,我们慢慢配置的,这边先无视它……</p> <p>然后处于好奇,我们来看一下它的<code>docker-compose.yml</code>文件</p> <pre><code class="language-yaml"># ================================================================== # WARNING: This file is auto-generated by generate_docker_compose # Do not modify this file directly. Instead, update the .env.example # or docker-compose-template.yaml and regenerate this file. # ================================================================== x-shared-env: &amp;shared-api-worker-env CONSOLE_API_URL: ${CONSOLE_API_URL:-} CONSOLE_WEB_URL: ${CONSOLE_WEB_URL:-} SERVICE_API_URL: ${SERVICE_API_URL:-} APP_API_URL: ${APP_API_URL:-} APP_WEB_URL: ${APP_WEB_URL:-} FILES_URL: ${FILES_URL:-} INTERNAL_FILES_URL: ${INTERNAL_FILES_URL:-} LANG: ${LANG:-en_US.UTF-8} LC_ALL: ${LC_ALL:-en_US.UTF-8} PYTHONIOENCODING: ${PYTHONIOENCODING:-utf-8} LOG_LEVEL: ${LOG_LEVEL:-INFO} LOG_FILE: ${LOG_FILE:-/app/logs/server.log} LOG_FILE_MAX_SIZE: ${LOG_FILE_MAX_SIZE:-20} LOG_FILE_BACKUP_COUNT: ${LOG_FILE_BACKUP_COUNT:-5} LOG_DATEFORMAT: ${LOG_DATEFORMAT:-%Y-%m-%d %H:%M:%S} LOG_TZ: ${LOG_TZ:-UTC} DEBUG: ${DEBUG:-false} FLASK_DEBUG: ${FLASK_DEBUG:-false} ENABLE_REQUEST_LOGGING: ${ENABLE_REQUEST_LOGGING:-False} SECRET_KEY: ${SECRET_KEY:-sk-9f73s3ljTXVcMT3Blb3ljTqtsKiGHXVcMT3BlbkFJLK7U} INIT_PASSWORD: ${INIT_PASSWORD:-} DEPLOY_ENV: ${DEPLOY_ENV:-PRODUCTION} CHECK_UPDATE_URL: ${CHECK_UPDATE_URL:-https://updates.dify.ai} OPENAI_API_BASE: ${OPENAI_API_BASE:-https://api.openai.com/v1} MIGRATION_ENABLED: ${MIGRATION_ENABLED:-true} FILES_ACCESS_TIMEOUT: ${FILES_ACCESS_TIMEOUT:-300} ACCESS_TOKEN_EXPIRE_MINUTES: ${ACCESS_TOKEN_EXPIRE_MINUTES:-60} REFRESH_TOKEN_EXPIRE_DAYS: ${REFRESH_TOKEN_EXPIRE_DAYS:-30} APP_MAX_ACTIVE_REQUESTS: ${APP_MAX_ACTIVE_REQUESTS:-0} APP_MAX_EXECUTION_TIME: ${APP_MAX_EXECUTION_TIME:-1200} DIFY_BIND_ADDRESS: ${DIFY_BIND_ADDRESS:-0.0.0.0} DIFY_PORT: ${DIFY_PORT:-5001} SERVER_WORKER_AMOUNT: ${SERVER_WORKER_AMOUNT:-1} SERVER_WORKER_CLASS: ${SERVER_WORKER_CLASS:-gevent} SERVER_WORKER_CONNECTIONS: ${SERVER_WORKER_CONNECTIONS:-10} CELERY_WORKER_CLASS: ${CELERY_WORKER_CLASS:-} GUNICORN_TIMEOUT: ${GUNICORN_TIMEOUT:-360} CELERY_WORKER_AMOUNT: ${CELERY_WORKER_AMOUNT:-} CELERY_AUTO_SCALE: ${CELERY_AUTO_SCALE:-false} CELERY_MAX_WORKERS: ${CELERY_MAX_WORKERS:-} CELERY_MIN_WORKERS: ${CELERY_MIN_WORKERS:-} API_TOOL_DEFAULT_CONNECT_TIMEOUT: ${API_TOOL_DEFAULT_CONNECT_TIMEOUT:-10} API_TOOL_DEFAULT_READ_TIMEOUT: ${API_TOOL_DEFAULT_READ_TIMEOUT:-60} ENABLE_WEBSITE_JINAREADER: ${ENABLE_WEBSITE_JINAREADER:-true} ENABLE_WEBSITE_FIRECRAWL: ${ENABLE_WEBSITE_FIRECRAWL:-true} ENABLE_WEBSITE_WATERCRAWL: ${ENABLE_WEBSITE_WATERCRAWL:-true} DB_USERNAME: ${DB_USERNAME:-postgres} DB_PASSWORD: ${DB_PASSWORD:-difyai123456} DB_HOST: ${DB_HOST:-db} DB_PORT: ${DB_PORT:-5432} DB_DATABASE: ${DB_DATABASE:-dify} SQLALCHEMY_POOL_SIZE: ${SQLALCHEMY_POOL_SIZE:-30} SQLALCHEMY_MAX_OVERFLOW: ${SQLALCHEMY_MAX_OVERFLOW:-10} SQLALCHEMY_POOL_RECYCLE: ${SQLALCHEMY_POOL_RECYCLE:-3600} SQLALCHEMY_ECHO: ${SQLALCHEMY_ECHO:-false} SQLALCHEMY_POOL_PRE_PING: ${SQLALCHEMY_POOL_PRE_PING:-false} SQLALCHEMY_POOL_USE_LIFO: ${SQLALCHEMY_POOL_USE_LIFO:-false} POSTGRES_MAX_CONNECTIONS: ${POSTGRES_MAX_CONNECTIONS:-100} POSTGRES_SHARED_BUFFERS: ${POSTGRES_SHARED_BUFFERS:-128MB} POSTGRES_WORK_MEM: ${POSTGRES_WORK_MEM:-4MB} POSTGRES_MAINTENANCE_WORK_MEM: ${POSTGRES_MAINTENANCE_WORK_MEM:-64MB} POSTGRES_EFFECTIVE_CACHE_SIZE: ${POSTGRES_EFFECTIVE_CACHE_SIZE:-4096MB} REDIS_HOST: ${REDIS_HOST:-redis} REDIS_PORT: ${REDIS_PORT:-6379} REDIS_USERNAME: ${REDIS_USERNAME:-} REDIS_PASSWORD: ${REDIS_PASSWORD:-difyai123456} REDIS_USE_SSL: ${REDIS_USE_SSL:-false} REDIS_SSL_CERT_REQS: ${REDIS_SSL_CERT_REQS:-CERT_NONE} REDIS_SSL_CA_CERTS: ${REDIS_SSL_CA_CERTS:-} REDIS_SSL_CERTFILE: ${REDIS_SSL_CERTFILE:-} REDIS_SSL_KEYFILE: ${REDIS_SSL_KEYFILE:-} REDIS_DB: ${REDIS_DB:-0} REDIS_USE_SENTINEL: ${REDIS_USE_SENTINEL:-false} REDIS_SENTINELS: ${REDIS_SENTINELS:-} REDIS_SENTINEL_SERVICE_NAME: ${REDIS_SENTINEL_SERVICE_NAME:-} REDIS_SENTINEL_USERNAME: ${REDIS_SENTINEL_USERNAME:-} REDIS_SENTINEL_PASSWORD: ${REDIS_SENTINEL_PASSWORD:-} REDIS_SENTINEL_SOCKET_TIMEOUT: ${REDIS_SENTINEL_SOCKET_TIMEOUT:-0.1} REDIS_USE_CLUSTERS: ${REDIS_USE_CLUSTERS:-false} REDIS_CLUSTERS: ${REDIS_CLUSTERS:-} REDIS_CLUSTERS_PASSWORD: ${REDIS_CLUSTERS_PASSWORD:-} CELERY_BROKER_URL: ${CELERY_BROKER_URL:-redis://:difyai123456@redis:6379/1} CELERY_BACKEND: ${CELERY_BACKEND:-redis} BROKER_USE_SSL: ${BROKER_USE_SSL:-false} CELERY_USE_SENTINEL: ${CELERY_USE_SENTINEL:-false} CELERY_SENTINEL_MASTER_NAME: ${CELERY_SENTINEL_MASTER_NAME:-} CELERY_SENTINEL_PASSWORD: ${CELERY_SENTINEL_PASSWORD:-} CELERY_SENTINEL_SOCKET_TIMEOUT: ${CELERY_SENTINEL_SOCKET_TIMEOUT:-0.1} WEB_API_CORS_ALLOW_ORIGINS: ${WEB_API_CORS_ALLOW_ORIGINS:-*} CONSOLE_CORS_ALLOW_ORIGINS: ${CONSOLE_CORS_ALLOW_ORIGINS:-*} STORAGE_TYPE: ${STORAGE_TYPE:-opendal} OPENDAL_SCHEME: ${OPENDAL_SCHEME:-fs} OPENDAL_FS_ROOT: ${OPENDAL_FS_ROOT:-storage} CLICKZETTA_VOLUME_TYPE: ${CLICKZETTA_VOLUME_TYPE:-user} CLICKZETTA_VOLUME_NAME: ${CLICKZETTA_VOLUME_NAME:-} CLICKZETTA_VOLUME_TABLE_PREFIX: ${CLICKZETTA_VOLUME_TABLE_PREFIX:-dataset_} CLICKZETTA_VOLUME_DIFY_PREFIX: ${CLICKZETTA_VOLUME_DIFY_PREFIX:-dify_km} S3_ENDPOINT: ${S3_ENDPOINT:-} S3_REGION: ${S3_REGION:-us-east-1} S3_BUCKET_NAME: ${S3_BUCKET_NAME:-difyai} S3_ACCESS_KEY: ${S3_ACCESS_KEY:-} S3_SECRET_KEY: ${S3_SECRET_KEY:-} S3_USE_AWS_MANAGED_IAM: ${S3_USE_AWS_MANAGED_IAM:-false} AZURE_BLOB_ACCOUNT_NAME: ${AZURE_BLOB_ACCOUNT_NAME:-difyai} AZURE_BLOB_ACCOUNT_KEY: ${AZURE_BLOB_ACCOUNT_KEY:-difyai} AZURE_BLOB_CONTAINER_NAME: ${AZURE_BLOB_CONTAINER_NAME:-difyai-container} AZURE_BLOB_ACCOUNT_URL: ${AZURE_BLOB_ACCOUNT_URL:-https://&lt;your_account_name&gt;.blob.core.windows.net} GOOGLE_STORAGE_BUCKET_NAME: ${GOOGLE_STORAGE_BUCKET_NAME:-your-bucket-name} GOOGLE_STORAGE_SERVICE_ACCOUNT_JSON_BASE64: ${GOOGLE_STORAGE_SERVICE_ACCOUNT_JSON_BASE64:-} ALIYUN_OSS_BUCKET_NAME: ${ALIYUN_OSS_BUCKET_NAME:-your-bucket-name} ALIYUN_OSS_ACCESS_KEY: ${ALIYUN_OSS_ACCESS_KEY:-your-access-key} ALIYUN_OSS_SECRET_KEY: ${ALIYUN_OSS_SECRET_KEY:-your-secret-key} ALIYUN_OSS_ENDPOINT: ${ALIYUN_OSS_ENDPOINT:-https://oss-ap-southeast-1-internal.aliyuncs.com} ALIYUN_OSS_REGION: ${ALIYUN_OSS_REGION:-ap-southeast-1} ALIYUN_OSS_AUTH_VERSION: ${ALIYUN_OSS_AUTH_VERSION:-v4} ALIYUN_OSS_PATH: ${ALIYUN_OSS_PATH:-your-path} TENCENT_COS_BUCKET_NAME: ${TENCENT_COS_BUCKET_NAME:-your-bucket-name} TENCENT_COS_SECRET_KEY: ${TENCENT_COS_SECRET_KEY:-your-secret-key} TENCENT_COS_SECRET_ID: ${TENCENT_COS_SECRET_ID:-your-secret-id} TENCENT_COS_REGION: ${TENCENT_COS_REGION:-your-region} TENCENT_COS_SCHEME: ${TENCENT_COS_SCHEME:-your-scheme} OCI_ENDPOINT: ${OCI_ENDPOINT:-https://your-object-storage-namespace.compat.objectstorage.us-ashburn-1.oraclecloud.com} OCI_BUCKET_NAME: ${OCI_BUCKET_NAME:-your-bucket-name} OCI_ACCESS_KEY: ${OCI_ACCESS_KEY:-your-access-key} OCI_SECRET_KEY: ${OCI_SECRET_KEY:-your-secret-key} OCI_REGION: ${OCI_REGION:-us-ashburn-1} HUAWEI_OBS_BUCKET_NAME: ${HUAWEI_OBS_BUCKET_NAME:-your-bucket-name} HUAWEI_OBS_SECRET_KEY: ${HUAWEI_OBS_SECRET_KEY:-your-secret-key} HUAWEI_OBS_ACCESS_KEY: ${HUAWEI_OBS_ACCESS_KEY:-your-access-key} HUAWEI_OBS_SERVER: ${HUAWEI_OBS_SERVER:-your-server-url} VOLCENGINE_TOS_BUCKET_NAME: ${VOLCENGINE_TOS_BUCKET_NAME:-your-bucket-name} VOLCENGINE_TOS_SECRET_KEY: ${VOLCENGINE_TOS_SECRET_KEY:-your-secret-key} VOLCENGINE_TOS_ACCESS_KEY: ${VOLCENGINE_TOS_ACCESS_KEY:-your-access-key} VOLCENGINE_TOS_ENDPOINT: ${VOLCENGINE_TOS_ENDPOINT:-your-server-url} VOLCENGINE_TOS_REGION: ${VOLCENGINE_TOS_REGION:-your-region} BAIDU_OBS_BUCKET_NAME: ${BAIDU_OBS_BUCKET_NAME:-your-bucket-name} BAIDU_OBS_SECRET_KEY: ${BAIDU_OBS_SECRET_KEY:-your-secret-key} BAIDU_OBS_ACCESS_KEY: ${BAIDU_OBS_ACCESS_KEY:-your-access-key} BAIDU_OBS_ENDPOINT: ${BAIDU_OBS_ENDPOINT:-your-server-url} SUPABASE_BUCKET_NAME: ${SUPABASE_BUCKET_NAME:-your-bucket-name} SUPABASE_API_KEY: ${SUPABASE_API_KEY:-your-access-key} SUPABASE_URL: ${SUPABASE_URL:-your-server-url} VECTOR_STORE: ${VECTOR_STORE:-weaviate} VECTOR_INDEX_NAME_PREFIX: ${VECTOR_INDEX_NAME_PREFIX:-Vector_index} WEAVIATE_ENDPOINT: ${WEAVIATE_ENDPOINT:-http://weaviate:8080} WEAVIATE_API_KEY: ${WEAVIATE_API_KEY:-WVF5YThaHlkYwhGUSmCRgsX3tD5ngdN8pkih} QDRANT_URL: ${QDRANT_URL:-http://qdrant:6333} QDRANT_API_KEY: ${QDRANT_API_KEY:-difyai123456} QDRANT_CLIENT_TIMEOUT: ${QDRANT_CLIENT_TIMEOUT:-20} QDRANT_GRPC_ENABLED: ${QDRANT_GRPC_ENABLED:-false} QDRANT_GRPC_PORT: ${QDRANT_GRPC_PORT:-6334} QDRANT_REPLICATION_FACTOR: ${QDRANT_REPLICATION_FACTOR:-1} MILVUS_URI: ${MILVUS_URI:-http://host.docker.internal:19530} MILVUS_DATABASE: ${MILVUS_DATABASE:-} MILVUS_TOKEN: ${MILVUS_TOKEN:-} MILVUS_USER: ${MILVUS_USER:-} MILVUS_PASSWORD: ${MILVUS_PASSWORD:-} MILVUS_ENABLE_HYBRID_SEARCH: ${MILVUS_ENABLE_HYBRID_SEARCH:-False} MILVUS_ANALYZER_PARAMS: ${MILVUS_ANALYZER_PARAMS:-} MYSCALE_HOST: ${MYSCALE_HOST:-myscale} MYSCALE_PORT: ${MYSCALE_PORT:-8123} MYSCALE_USER: ${MYSCALE_USER:-default} MYSCALE_PASSWORD: ${MYSCALE_PASSWORD:-} MYSCALE_DATABASE: ${MYSCALE_DATABASE:-dify} MYSCALE_FTS_PARAMS: ${MYSCALE_FTS_PARAMS:-} COUCHBASE_CONNECTION_STRING: ${COUCHBASE_CONNECTION_STRING:-couchbase://couchbase-server} COUCHBASE_USER: ${COUCHBASE_USER:-Administrator} COUCHBASE_PASSWORD: ${COUCHBASE_PASSWORD:-password} COUCHBASE_BUCKET_NAME: ${COUCHBASE_BUCKET_NAME:-Embeddings} COUCHBASE_SCOPE_NAME: ${COUCHBASE_SCOPE_NAME:-_default} PGVECTOR_HOST: ${PGVECTOR_HOST:-pgvector} PGVECTOR_PORT: ${PGVECTOR_PORT:-5432} PGVECTOR_USER: ${PGVECTOR_USER:-postgres} PGVECTOR_PASSWORD: ${PGVECTOR_PASSWORD:-difyai123456} PGVECTOR_DATABASE: ${PGVECTOR_DATABASE:-dify} PGVECTOR_MIN_CONNECTION: ${PGVECTOR_MIN_CONNECTION:-1} PGVECTOR_MAX_CONNECTION: ${PGVECTOR_MAX_CONNECTION:-5} PGVECTOR_PG_BIGM: ${PGVECTOR_PG_BIGM:-false} PGVECTOR_PG_BIGM_VERSION: ${PGVECTOR_PG_BIGM_VERSION:-1.2-20240606} VASTBASE_HOST: ${VASTBASE_HOST:-vastbase} VASTBASE_PORT: ${VASTBASE_PORT:-5432} VASTBASE_USER: ${VASTBASE_USER:-dify} VASTBASE_PASSWORD: ${VASTBASE_PASSWORD:-Difyai123456} VASTBASE_DATABASE: ${VASTBASE_DATABASE:-dify} VASTBASE_MIN_CONNECTION: ${VASTBASE_MIN_CONNECTION:-1} VASTBASE_MAX_CONNECTION: ${VASTBASE_MAX_CONNECTION:-5} PGVECTO_RS_HOST: ${PGVECTO_RS_HOST:-pgvecto-rs} PGVECTO_RS_PORT: ${PGVECTO_RS_PORT:-5432} PGVECTO_RS_USER: ${PGVECTO_RS_USER:-postgres} PGVECTO_RS_PASSWORD: ${PGVECTO_RS_PASSWORD:-difyai123456} PGVECTO_RS_DATABASE: ${PGVECTO_RS_DATABASE:-dify} ANALYTICDB_KEY_ID: ${ANALYTICDB_KEY_ID:-your-ak} ANALYTICDB_KEY_SECRET: ${ANALYTICDB_KEY_SECRET:-your-sk} ANALYTICDB_REGION_ID: ${ANALYTICDB_REGION_ID:-cn-hangzhou} ANALYTICDB_INSTANCE_ID: ${ANALYTICDB_INSTANCE_ID:-gp-ab123456} ANALYTICDB_ACCOUNT: ${ANALYTICDB_ACCOUNT:-testaccount} ANALYTICDB_PASSWORD: ${ANALYTICDB_PASSWORD:-testpassword} ANALYTICDB_NAMESPACE: ${ANALYTICDB_NAMESPACE:-dify} ANALYTICDB_NAMESPACE_PASSWORD: ${ANALYTICDB_NAMESPACE_PASSWORD:-difypassword} ANALYTICDB_HOST: ${ANALYTICDB_HOST:-gp-test.aliyuncs.com} ANALYTICDB_PORT: ${ANALYTICDB_PORT:-5432} ANALYTICDB_MIN_CONNECTION: ${ANALYTICDB_MIN_CONNECTION:-1} ANALYTICDB_MAX_CONNECTION: ${ANALYTICDB_MAX_CONNECTION:-5} TIDB_VECTOR_HOST: ${TIDB_VECTOR_HOST:-tidb} TIDB_VECTOR_PORT: ${TIDB_VECTOR_PORT:-4000} TIDB_VECTOR_USER: ${TIDB_VECTOR_USER:-} TIDB_VECTOR_PASSWORD: ${TIDB_VECTOR_PASSWORD:-} TIDB_VECTOR_DATABASE: ${TIDB_VECTOR_DATABASE:-dify} MATRIXONE_HOST: ${MATRIXONE_HOST:-matrixone} MATRIXONE_PORT: ${MATRIXONE_PORT:-6001} MATRIXONE_USER: ${MATRIXONE_USER:-dump} MATRIXONE_PASSWORD: ${MATRIXONE_PASSWORD:-111} MATRIXONE_DATABASE: ${MATRIXONE_DATABASE:-dify} TIDB_ON_QDRANT_URL: ${TIDB_ON_QDRANT_URL:-http://127.0.0.1} TIDB_ON_QDRANT_API_KEY: ${TIDB_ON_QDRANT_API_KEY:-dify} TIDB_ON_QDRANT_CLIENT_TIMEOUT: ${TIDB_ON_QDRANT_CLIENT_TIMEOUT:-20} TIDB_ON_QDRANT_GRPC_ENABLED: ${TIDB_ON_QDRANT_GRPC_ENABLED:-false} TIDB_ON_QDRANT_GRPC_PORT: ${TIDB_ON_QDRANT_GRPC_PORT:-6334} TIDB_PUBLIC_KEY: ${TIDB_PUBLIC_KEY:-dify} TIDB_PRIVATE_KEY: ${TIDB_PRIVATE_KEY:-dify} TIDB_API_URL: ${TIDB_API_URL:-http://127.0.0.1} TIDB_IAM_API_URL: ${TIDB_IAM_API_URL:-http://127.0.0.1} TIDB_REGION: ${TIDB_REGION:-regions/aws-us-east-1} TIDB_PROJECT_ID: ${TIDB_PROJECT_ID:-dify} TIDB_SPEND_LIMIT: ${TIDB_SPEND_LIMIT:-100} CHROMA_HOST: ${CHROMA_HOST:-127.0.0.1} CHROMA_PORT: ${CHROMA_PORT:-8000} CHROMA_TENANT: ${CHROMA_TENANT:-default_tenant} CHROMA_DATABASE: ${CHROMA_DATABASE:-default_database} CHROMA_AUTH_PROVIDER: ${CHROMA_AUTH_PROVIDER:-chromadb.auth.token_authn.TokenAuthClientProvider} CHROMA_AUTH_CREDENTIALS: ${CHROMA_AUTH_CREDENTIALS:-} ORACLE_USER: ${ORACLE_USER:-dify} ORACLE_PASSWORD: ${ORACLE_PASSWORD:-dify} ORACLE_DSN: ${ORACLE_DSN:-oracle:1521/FREEPDB1} ORACLE_CONFIG_DIR: ${ORACLE_CONFIG_DIR:-/app/api/storage/wallet} ORACLE_WALLET_LOCATION: ${ORACLE_WALLET_LOCATION:-/app/api/storage/wallet} ORACLE_WALLET_PASSWORD: ${ORACLE_WALLET_PASSWORD:-dify} ORACLE_IS_AUTONOMOUS: ${ORACLE_IS_AUTONOMOUS:-false} RELYT_HOST: ${RELYT_HOST:-db} RELYT_PORT: ${RELYT_PORT:-5432} RELYT_USER: ${RELYT_USER:-postgres} RELYT_PASSWORD: ${RELYT_PASSWORD:-difyai123456} RELYT_DATABASE: ${RELYT_DATABASE:-postgres} OPENSEARCH_HOST: ${OPENSEARCH_HOST:-opensearch} OPENSEARCH_PORT: ${OPENSEARCH_PORT:-9200} OPENSEARCH_SECURE: ${OPENSEARCH_SECURE:-true} OPENSEARCH_VERIFY_CERTS: ${OPENSEARCH_VERIFY_CERTS:-true} OPENSEARCH_AUTH_METHOD: ${OPENSEARCH_AUTH_METHOD:-basic} OPENSEARCH_USER: ${OPENSEARCH_USER:-admin} OPENSEARCH_PASSWORD: ${OPENSEARCH_PASSWORD:-admin} OPENSEARCH_AWS_REGION: ${OPENSEARCH_AWS_REGION:-ap-southeast-1} OPENSEARCH_AWS_SERVICE: ${OPENSEARCH_AWS_SERVICE:-aoss} TENCENT_VECTOR_DB_URL: ${TENCENT_VECTOR_DB_URL:-http://127.0.0.1} TENCENT_VECTOR_DB_API_KEY: ${TENCENT_VECTOR_DB_API_KEY:-dify} TENCENT_VECTOR_DB_TIMEOUT: ${TENCENT_VECTOR_DB_TIMEOUT:-30} TENCENT_VECTOR_DB_USERNAME: ${TENCENT_VECTOR_DB_USERNAME:-dify} TENCENT_VECTOR_DB_DATABASE: ${TENCENT_VECTOR_DB_DATABASE:-dify} TENCENT_VECTOR_DB_SHARD: ${TENCENT_VECTOR_DB_SHARD:-1} TENCENT_VECTOR_DB_REPLICAS: ${TENCENT_VECTOR_DB_REPLICAS:-2} TENCENT_VECTOR_DB_ENABLE_HYBRID_SEARCH: ${TENCENT_VECTOR_DB_ENABLE_HYBRID_SEARCH:-false} ELASTICSEARCH_HOST: ${ELASTICSEARCH_HOST:-0.0.0.0} ELASTICSEARCH_PORT: ${ELASTICSEARCH_PORT:-9200} ELASTICSEARCH_USERNAME: ${ELASTICSEARCH_USERNAME:-elastic} ELASTICSEARCH_PASSWORD: ${ELASTICSEARCH_PASSWORD:-elastic} KIBANA_PORT: ${KIBANA_PORT:-5601} ELASTICSEARCH_USE_CLOUD: ${ELASTICSEARCH_USE_CLOUD:-false} ELASTICSEARCH_CLOUD_URL: ${ELASTICSEARCH_CLOUD_URL:-YOUR-ELASTICSEARCH_CLOUD_URL} ELASTICSEARCH_API_KEY: ${ELASTICSEARCH_API_KEY:-YOUR-ELASTICSEARCH_API_KEY} ELASTICSEARCH_VERIFY_CERTS: ${ELASTICSEARCH_VERIFY_CERTS:-False} ELASTICSEARCH_CA_CERTS: ${ELASTICSEARCH_CA_CERTS:-} ELASTICSEARCH_REQUEST_TIMEOUT: ${ELASTICSEARCH_REQUEST_TIMEOUT:-100000} ELASTICSEARCH_RETRY_ON_TIMEOUT: ${ELASTICSEARCH_RETRY_ON_TIMEOUT:-True} ELASTICSEARCH_MAX_RETRIES: ${ELASTICSEARCH_MAX_RETRIES:-10} BAIDU_VECTOR_DB_ENDPOINT: ${BAIDU_VECTOR_DB_ENDPOINT:-http://127.0.0.1:5287} BAIDU_VECTOR_DB_CONNECTION_TIMEOUT_MS: ${BAIDU_VECTOR_DB_CONNECTION_TIMEOUT_MS:-30000} BAIDU_VECTOR_DB_ACCOUNT: ${BAIDU_VECTOR_DB_ACCOUNT:-root} BAIDU_VECTOR_DB_API_KEY: ${BAIDU_VECTOR_DB_API_KEY:-dify} BAIDU_VECTOR_DB_DATABASE: ${BAIDU_VECTOR_DB_DATABASE:-dify} BAIDU_VECTOR_DB_SHARD: ${BAIDU_VECTOR_DB_SHARD:-1} BAIDU_VECTOR_DB_REPLICAS: ${BAIDU_VECTOR_DB_REPLICAS:-3} VIKINGDB_ACCESS_KEY: ${VIKINGDB_ACCESS_KEY:-your-ak} VIKINGDB_SECRET_KEY: ${VIKINGDB_SECRET_KEY:-your-sk} VIKINGDB_REGION: ${VIKINGDB_REGION:-cn-shanghai} VIKINGDB_HOST: ${VIKINGDB_HOST:-api-vikingdb.xxx.volces.com} VIKINGDB_SCHEMA: ${VIKINGDB_SCHEMA:-http} VIKINGDB_CONNECTION_TIMEOUT: ${VIKINGDB_CONNECTION_TIMEOUT:-30} VIKINGDB_SOCKET_TIMEOUT: ${VIKINGDB_SOCKET_TIMEOUT:-30} LINDORM_URL: ${LINDORM_URL:-http://lindorm:30070} LINDORM_USERNAME: ${LINDORM_USERNAME:-lindorm} LINDORM_PASSWORD: ${LINDORM_PASSWORD:-lindorm} LINDORM_QUERY_TIMEOUT: ${LINDORM_QUERY_TIMEOUT:-1} OCEANBASE_VECTOR_HOST: ${OCEANBASE_VECTOR_HOST:-oceanbase} OCEANBASE_VECTOR_PORT: ${OCEANBASE_VECTOR_PORT:-2881} OCEANBASE_VECTOR_USER: ${OCEANBASE_VECTOR_USER:-root@test} OCEANBASE_VECTOR_PASSWORD: ${OCEANBASE_VECTOR_PASSWORD:-difyai123456} OCEANBASE_VECTOR_DATABASE: ${OCEANBASE_VECTOR_DATABASE:-test} OCEANBASE_CLUSTER_NAME: ${OCEANBASE_CLUSTER_NAME:-difyai} OCEANBASE_MEMORY_LIMIT: ${OCEANBASE_MEMORY_LIMIT:-6G} OCEANBASE_ENABLE_HYBRID_SEARCH: ${OCEANBASE_ENABLE_HYBRID_SEARCH:-false} OPENGAUSS_HOST: ${OPENGAUSS_HOST:-opengauss} OPENGAUSS_PORT: ${OPENGAUSS_PORT:-6600} OPENGAUSS_USER: ${OPENGAUSS_USER:-postgres} OPENGAUSS_PASSWORD: ${OPENGAUSS_PASSWORD:-Dify@123} OPENGAUSS_DATABASE: ${OPENGAUSS_DATABASE:-dify} OPENGAUSS_MIN_CONNECTION: ${OPENGAUSS_MIN_CONNECTION:-1} OPENGAUSS_MAX_CONNECTION: ${OPENGAUSS_MAX_CONNECTION:-5} OPENGAUSS_ENABLE_PQ: ${OPENGAUSS_ENABLE_PQ:-false} HUAWEI_CLOUD_HOSTS: ${HUAWEI_CLOUD_HOSTS:-https://127.0.0.1:9200} HUAWEI_CLOUD_USER: ${HUAWEI_CLOUD_USER:-admin} HUAWEI_CLOUD_PASSWORD: ${HUAWEI_CLOUD_PASSWORD:-admin} UPSTASH_VECTOR_URL: ${UPSTASH_VECTOR_URL:-https://xxx-vector.upstash.io} UPSTASH_VECTOR_TOKEN: ${UPSTASH_VECTOR_TOKEN:-dify} TABLESTORE_ENDPOINT: ${TABLESTORE_ENDPOINT:-https://instance-name.cn-hangzhou.ots.aliyuncs.com} TABLESTORE_INSTANCE_NAME: ${TABLESTORE_INSTANCE_NAME:-instance-name} TABLESTORE_ACCESS_KEY_ID: ${TABLESTORE_ACCESS_KEY_ID:-xxx} TABLESTORE_ACCESS_KEY_SECRET: ${TABLESTORE_ACCESS_KEY_SECRET:-xxx} TABLESTORE_NORMALIZE_FULLTEXT_BM25_SCORE: ${TABLESTORE_NORMALIZE_FULLTEXT_BM25_SCORE:-false} CLICKZETTA_USERNAME: ${CLICKZETTA_USERNAME:-} CLICKZETTA_PASSWORD: ${CLICKZETTA_PASSWORD:-} CLICKZETTA_INSTANCE: ${CLICKZETTA_INSTANCE:-} CLICKZETTA_SERVICE: ${CLICKZETTA_SERVICE:-api.clickzetta.com} CLICKZETTA_WORKSPACE: ${CLICKZETTA_WORKSPACE:-quick_start} CLICKZETTA_VCLUSTER: ${CLICKZETTA_VCLUSTER:-default_ap} CLICKZETTA_SCHEMA: ${CLICKZETTA_SCHEMA:-dify} CLICKZETTA_BATCH_SIZE: ${CLICKZETTA_BATCH_SIZE:-100} CLICKZETTA_ENABLE_INVERTED_INDEX: ${CLICKZETTA_ENABLE_INVERTED_INDEX:-true} CLICKZETTA_ANALYZER_TYPE: ${CLICKZETTA_ANALYZER_TYPE:-chinese} CLICKZETTA_ANALYZER_MODE: ${CLICKZETTA_ANALYZER_MODE:-smart} CLICKZETTA_VECTOR_DISTANCE_FUNCTION: ${CLICKZETTA_VECTOR_DISTANCE_FUNCTION:-cosine_distance} UPLOAD_FILE_SIZE_LIMIT: ${UPLOAD_FILE_SIZE_LIMIT:-15} UPLOAD_FILE_BATCH_LIMIT: ${UPLOAD_FILE_BATCH_LIMIT:-5} ETL_TYPE: ${ETL_TYPE:-dify} UNSTRUCTURED_API_URL: ${UNSTRUCTURED_API_URL:-} UNSTRUCTURED_API_KEY: ${UNSTRUCTURED_API_KEY:-} SCARF_NO_ANALYTICS: ${SCARF_NO_ANALYTICS:-true} PROMPT_GENERATION_MAX_TOKENS: ${PROMPT_GENERATION_MAX_TOKENS:-512} CODE_GENERATION_MAX_TOKENS: ${CODE_GENERATION_MAX_TOKENS:-1024} PLUGIN_BASED_TOKEN_COUNTING_ENABLED: ${PLUGIN_BASED_TOKEN_COUNTING_ENABLED:-false} MULTIMODAL_SEND_FORMAT: ${MULTIMODAL_SEND_FORMAT:-base64} UPLOAD_IMAGE_FILE_SIZE_LIMIT: ${UPLOAD_IMAGE_FILE_SIZE_LIMIT:-10} UPLOAD_VIDEO_FILE_SIZE_LIMIT: ${UPLOAD_VIDEO_FILE_SIZE_LIMIT:-100} UPLOAD_AUDIO_FILE_SIZE_LIMIT: ${UPLOAD_AUDIO_FILE_SIZE_LIMIT:-50} SENTRY_DSN: ${SENTRY_DSN:-} API_SENTRY_DSN: ${API_SENTRY_DSN:-} API_SENTRY_TRACES_SAMPLE_RATE: ${API_SENTRY_TRACES_SAMPLE_RATE:-1.0} API_SENTRY_PROFILES_SAMPLE_RATE: ${API_SENTRY_PROFILES_SAMPLE_RATE:-1.0} WEB_SENTRY_DSN: ${WEB_SENTRY_DSN:-} NOTION_INTEGRATION_TYPE: ${NOTION_INTEGRATION_TYPE:-public} NOTION_CLIENT_SECRET: ${NOTION_CLIENT_SECRET:-} NOTION_CLIENT_ID: ${NOTION_CLIENT_ID:-} NOTION_INTERNAL_SECRET: ${NOTION_INTERNAL_SECRET:-} MAIL_TYPE: ${MAIL_TYPE:-resend} 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} SMTP_SERVER: ${SMTP_SERVER:-} SMTP_PORT: ${SMTP_PORT:-465} SMTP_USERNAME: ${SMTP_USERNAME:-} SMTP_PASSWORD: ${SMTP_PASSWORD:-} SMTP_USE_TLS: ${SMTP_USE_TLS:-true} SMTP_OPPORTUNISTIC_TLS: ${SMTP_OPPORTUNISTIC_TLS:-false} SENDGRID_API_KEY: ${SENDGRID_API_KEY:-} INDEXING_MAX_SEGMENTATION_TOKENS_LENGTH: ${INDEXING_MAX_SEGMENTATION_TOKENS_LENGTH:-4000} INVITE_EXPIRY_HOURS: ${INVITE_EXPIRY_HOURS:-72} RESET_PASSWORD_TOKEN_EXPIRY_MINUTES: ${RESET_PASSWORD_TOKEN_EXPIRY_MINUTES:-5} CHANGE_EMAIL_TOKEN_EXPIRY_MINUTES: ${CHANGE_EMAIL_TOKEN_EXPIRY_MINUTES:-5} OWNER_TRANSFER_TOKEN_EXPIRY_MINUTES: ${OWNER_TRANSFER_TOKEN_EXPIRY_MINUTES:-5} CODE_EXECUTION_ENDPOINT: ${CODE_EXECUTION_ENDPOINT:-http://sandbox:8194} CODE_EXECUTION_API_KEY: ${CODE_EXECUTION_API_KEY:-dify-sandbox} CODE_MAX_NUMBER: ${CODE_MAX_NUMBER:-9223372036854775807} CODE_MIN_NUMBER: ${CODE_MIN_NUMBER:--9223372036854775808} CODE_MAX_DEPTH: ${CODE_MAX_DEPTH:-5} CODE_MAX_PRECISION: ${CODE_MAX_PRECISION:-20} CODE_MAX_STRING_LENGTH: ${CODE_MAX_STRING_LENGTH:-80000} CODE_MAX_STRING_ARRAY_LENGTH: ${CODE_MAX_STRING_ARRAY_LENGTH:-30} CODE_MAX_OBJECT_ARRAY_LENGTH: ${CODE_MAX_OBJECT_ARRAY_LENGTH:-30} CODE_MAX_NUMBER_ARRAY_LENGTH: ${CODE_MAX_NUMBER_ARRAY_LENGTH:-1000} CODE_EXECUTION_CONNECT_TIMEOUT: ${CODE_EXECUTION_CONNECT_TIMEOUT:-10} CODE_EXECUTION_READ_TIMEOUT: ${CODE_EXECUTION_READ_TIMEOUT:-60} CODE_EXECUTION_WRITE_TIMEOUT: ${CODE_EXECUTION_WRITE_TIMEOUT:-10} TEMPLATE_TRANSFORM_MAX_LENGTH: ${TEMPLATE_TRANSFORM_MAX_LENGTH:-80000} WORKFLOW_MAX_EXECUTION_STEPS: ${WORKFLOW_MAX_EXECUTION_STEPS:-500} WORKFLOW_MAX_EXECUTION_TIME: ${WORKFLOW_MAX_EXECUTION_TIME:-1200} WORKFLOW_CALL_MAX_DEPTH: ${WORKFLOW_CALL_MAX_DEPTH:-5} MAX_VARIABLE_SIZE: ${MAX_VARIABLE_SIZE:-204800} WORKFLOW_PARALLEL_DEPTH_LIMIT: ${WORKFLOW_PARALLEL_DEPTH_LIMIT:-3} WORKFLOW_FILE_UPLOAD_LIMIT: ${WORKFLOW_FILE_UPLOAD_LIMIT:-10} WORKFLOW_NODE_EXECUTION_STORAGE: ${WORKFLOW_NODE_EXECUTION_STORAGE:-rdbms} CORE_WORKFLOW_EXECUTION_REPOSITORY: ${CORE_WORKFLOW_EXECUTION_REPOSITORY:-core.repositories.sqlalchemy_workflow_execution_repository.SQLAlchemyWorkflowExecutionRepository} CORE_WORKFLOW_NODE_EXECUTION_REPOSITORY: ${CORE_WORKFLOW_NODE_EXECUTION_REPOSITORY:-core.repositories.sqlalchemy_workflow_node_execution_repository.SQLAlchemyWorkflowNodeExecutionRepository} API_WORKFLOW_RUN_REPOSITORY: ${API_WORKFLOW_RUN_REPOSITORY:-repositories.sqlalchemy_api_workflow_run_repository.DifyAPISQLAlchemyWorkflowRunRepository} API_WORKFLOW_NODE_EXECUTION_REPOSITORY: ${API_WORKFLOW_NODE_EXECUTION_REPOSITORY:-repositories.sqlalchemy_api_workflow_node_execution_repository.DifyAPISQLAlchemyWorkflowNodeExecutionRepository} WORKFLOW_LOG_CLEANUP_ENABLED: ${WORKFLOW_LOG_CLEANUP_ENABLED:-false} WORKFLOW_LOG_RETENTION_DAYS: ${WORKFLOW_LOG_RETENTION_DAYS:-30} WORKFLOW_LOG_CLEANUP_BATCH_SIZE: ${WORKFLOW_LOG_CLEANUP_BATCH_SIZE:-100} HTTP_REQUEST_NODE_MAX_BINARY_SIZE: ${HTTP_REQUEST_NODE_MAX_BINARY_SIZE:-10485760} HTTP_REQUEST_NODE_MAX_TEXT_SIZE: ${HTTP_REQUEST_NODE_MAX_TEXT_SIZE:-1048576} HTTP_REQUEST_NODE_SSL_VERIFY: ${HTTP_REQUEST_NODE_SSL_VERIFY:-True} RESPECT_XFORWARD_HEADERS_ENABLED: ${RESPECT_XFORWARD_HEADERS_ENABLED:-false} SSRF_PROXY_HTTP_URL: ${SSRF_PROXY_HTTP_URL:-http://ssrf_proxy:3128} SSRF_PROXY_HTTPS_URL: ${SSRF_PROXY_HTTPS_URL:-http://ssrf_proxy:3128} LOOP_NODE_MAX_COUNT: ${LOOP_NODE_MAX_COUNT:-100} MAX_TOOLS_NUM: ${MAX_TOOLS_NUM:-10} MAX_PARALLEL_LIMIT: ${MAX_PARALLEL_LIMIT:-10} MAX_ITERATIONS_NUM: ${MAX_ITERATIONS_NUM:-99} TEXT_GENERATION_TIMEOUT_MS: ${TEXT_GENERATION_TIMEOUT_MS:-60000} ALLOW_UNSAFE_DATA_SCHEME: ${ALLOW_UNSAFE_DATA_SCHEME:-false} MAX_TREE_DEPTH: ${MAX_TREE_DEPTH:-50} POSTGRES_USER: ${POSTGRES_USER:-${DB_USERNAME}} POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:-${DB_PASSWORD}} POSTGRES_DB: ${POSTGRES_DB:-${DB_DATABASE}} PGDATA: ${PGDATA:-/var/lib/postgresql/data/pgdata} SANDBOX_API_KEY: ${SANDBOX_API_KEY:-dify-sandbox} SANDBOX_GIN_MODE: ${SANDBOX_GIN_MODE:-release} SANDBOX_WORKER_TIMEOUT: ${SANDBOX_WORKER_TIMEOUT:-15} SANDBOX_ENABLE_NETWORK: ${SANDBOX_ENABLE_NETWORK:-true} SANDBOX_HTTP_PROXY: ${SANDBOX_HTTP_PROXY:-http://ssrf_proxy:3128} SANDBOX_HTTPS_PROXY: ${SANDBOX_HTTPS_PROXY:-http://ssrf_proxy:3128} SANDBOX_PORT: ${SANDBOX_PORT:-8194} WEAVIATE_PERSISTENCE_DATA_PATH: ${WEAVIATE_PERSISTENCE_DATA_PATH:-/var/lib/weaviate} WEAVIATE_QUERY_DEFAULTS_LIMIT: ${WEAVIATE_QUERY_DEFAULTS_LIMIT:-25} WEAVIATE_AUTHENTICATION_ANONYMOUS_ACCESS_ENABLED: ${WEAVIATE_AUTHENTICATION_ANONYMOUS_ACCESS_ENABLED:-true} WEAVIATE_DEFAULT_VECTORIZER_MODULE: ${WEAVIATE_DEFAULT_VECTORIZER_MODULE:-none} WEAVIATE_CLUSTER_HOSTNAME: ${WEAVIATE_CLUSTER_HOSTNAME:-node1} WEAVIATE_AUTHENTICATION_APIKEY_ENABLED: ${WEAVIATE_AUTHENTICATION_APIKEY_ENABLED:-true} WEAVIATE_AUTHENTICATION_APIKEY_ALLOWED_KEYS: ${WEAVIATE_AUTHENTICATION_APIKEY_ALLOWED_KEYS:-WVF5YThaHlkYwhGUSmCRgsX3tD5ngdN8pkih} WEAVIATE_AUTHENTICATION_APIKEY_USERS: ${WEAVIATE_AUTHENTICATION_APIKEY_USERS:-hello@dify.ai} WEAVIATE_AUTHORIZATION_ADMINLIST_ENABLED: ${WEAVIATE_AUTHORIZATION_ADMINLIST_ENABLED:-true} WEAVIATE_AUTHORIZATION_ADMINLIST_USERS: ${WEAVIATE_AUTHORIZATION_ADMINLIST_USERS:-hello@dify.ai} CHROMA_SERVER_AUTHN_CREDENTIALS: ${CHROMA_SERVER_AUTHN_CREDENTIALS:-difyai123456} CHROMA_SERVER_AUTHN_PROVIDER: ${CHROMA_SERVER_AUTHN_PROVIDER:-chromadb.auth.token_authn.TokenAuthenticationServerProvider} CHROMA_IS_PERSISTENT: ${CHROMA_IS_PERSISTENT:-TRUE} ORACLE_PWD: ${ORACLE_PWD:-Dify123456} ORACLE_CHARACTERSET: ${ORACLE_CHARACTERSET:-AL32UTF8} ETCD_AUTO_COMPACTION_MODE: ${ETCD_AUTO_COMPACTION_MODE:-revision} ETCD_AUTO_COMPACTION_RETENTION: ${ETCD_AUTO_COMPACTION_RETENTION:-1000} ETCD_QUOTA_BACKEND_BYTES: ${ETCD_QUOTA_BACKEND_BYTES:-4294967296} ETCD_SNAPSHOT_COUNT: ${ETCD_SNAPSHOT_COUNT:-50000} MINIO_ACCESS_KEY: ${MINIO_ACCESS_KEY:-minioadmin} MINIO_SECRET_KEY: ${MINIO_SECRET_KEY:-minioadmin} ETCD_ENDPOINTS: ${ETCD_ENDPOINTS:-etcd:2379} MINIO_ADDRESS: ${MINIO_ADDRESS:-minio:9000} MILVUS_AUTHORIZATION_ENABLED: ${MILVUS_AUTHORIZATION_ENABLED:-true} PGVECTOR_PGUSER: ${PGVECTOR_PGUSER:-postgres} PGVECTOR_POSTGRES_PASSWORD: ${PGVECTOR_POSTGRES_PASSWORD:-difyai123456} PGVECTOR_POSTGRES_DB: ${PGVECTOR_POSTGRES_DB:-dify} PGVECTOR_PGDATA: ${PGVECTOR_PGDATA:-/var/lib/postgresql/data/pgdata} OPENSEARCH_DISCOVERY_TYPE: ${OPENSEARCH_DISCOVERY_TYPE:-single-node} OPENSEARCH_BOOTSTRAP_MEMORY_LOCK: ${OPENSEARCH_BOOTSTRAP_MEMORY_LOCK:-true} OPENSEARCH_JAVA_OPTS_MIN: ${OPENSEARCH_JAVA_OPTS_MIN:-512m} OPENSEARCH_JAVA_OPTS_MAX: ${OPENSEARCH_JAVA_OPTS_MAX:-1024m} OPENSEARCH_INITIAL_ADMIN_PASSWORD: ${OPENSEARCH_INITIAL_ADMIN_PASSWORD:-Qazwsxedc!@#123} OPENSEARCH_MEMLOCK_SOFT: ${OPENSEARCH_MEMLOCK_SOFT:--1} OPENSEARCH_MEMLOCK_HARD: ${OPENSEARCH_MEMLOCK_HARD:--1} OPENSEARCH_NOFILE_SOFT: ${OPENSEARCH_NOFILE_SOFT:-65536} OPENSEARCH_NOFILE_HARD: ${OPENSEARCH_NOFILE_HARD:-65536} NGINX_SERVER_NAME: ${NGINX_SERVER_NAME:-_} NGINX_HTTPS_ENABLED: ${NGINX_HTTPS_ENABLED:-false} NGINX_PORT: ${NGINX_PORT:-80} NGINX_SSL_PORT: ${NGINX_SSL_PORT:-443} NGINX_SSL_CERT_FILENAME: ${NGINX_SSL_CERT_FILENAME:-dify.crt} NGINX_SSL_CERT_KEY_FILENAME: ${NGINX_SSL_CERT_KEY_FILENAME:-dify.key} NGINX_SSL_PROTOCOLS: ${NGINX_SSL_PROTOCOLS:-TLSv1.1 TLSv1.2 TLSv1.3} NGINX_WORKER_PROCESSES: ${NGINX_WORKER_PROCESSES:-auto} NGINX_CLIENT_MAX_BODY_SIZE: ${NGINX_CLIENT_MAX_BODY_SIZE:-100M} NGINX_KEEPALIVE_TIMEOUT: ${NGINX_KEEPALIVE_TIMEOUT:-65} 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_OPTIONS: ${CERTBOT_OPTIONS:-} SSRF_HTTP_PORT: ${SSRF_HTTP_PORT:-3128} SSRF_COREDUMP_DIR: ${SSRF_COREDUMP_DIR:-/var/spool/squid} SSRF_REVERSE_PROXY_PORT: ${SSRF_REVERSE_PROXY_PORT:-8194} SSRF_SANDBOX_HOST: ${SSRF_SANDBOX_HOST:-sandbox} SSRF_DEFAULT_TIME_OUT: ${SSRF_DEFAULT_TIME_OUT:-5} SSRF_DEFAULT_CONNECT_TIME_OUT: ${SSRF_DEFAULT_CONNECT_TIME_OUT:-5} SSRF_DEFAULT_READ_TIME_OUT: ${SSRF_DEFAULT_READ_TIME_OUT:-5} SSRF_DEFAULT_WRITE_TIME_OUT: ${SSRF_DEFAULT_WRITE_TIME_OUT:-5} EXPOSE_NGINX_PORT: ${EXPOSE_NGINX_PORT:-80} EXPOSE_NGINX_SSL_PORT: ${EXPOSE_NGINX_SSL_PORT:-443} POSITION_TOOL_PINS: ${POSITION_TOOL_PINS:-} POSITION_TOOL_INCLUDES: ${POSITION_TOOL_INCLUDES:-} POSITION_TOOL_EXCLUDES: ${POSITION_TOOL_EXCLUDES:-} POSITION_PROVIDER_PINS: ${POSITION_PROVIDER_PINS:-} POSITION_PROVIDER_INCLUDES: ${POSITION_PROVIDER_INCLUDES:-} POSITION_PROVIDER_EXCLUDES: ${POSITION_PROVIDER_EXCLUDES:-} CSP_WHITELIST: ${CSP_WHITELIST:-} CREATE_TIDB_SERVICE_JOB_ENABLED: ${CREATE_TIDB_SERVICE_JOB_ENABLED:-false} MAX_SUBMIT_COUNT: ${MAX_SUBMIT_COUNT:-100} TOP_K_MAX_VALUE: ${TOP_K_MAX_VALUE:-10} DB_PLUGIN_DATABASE: ${DB_PLUGIN_DATABASE:-dify_plugin} EXPOSE_PLUGIN_DAEMON_PORT: ${EXPOSE_PLUGIN_DAEMON_PORT:-5002} PLUGIN_DAEMON_PORT: ${PLUGIN_DAEMON_PORT:-5002} PLUGIN_DAEMON_KEY: ${PLUGIN_DAEMON_KEY:-lYkiYYT6owG+71oLerGzA7GXCgOT++6ovaezWAjpCjf+Sjc3ZtU+qUEi} PLUGIN_DAEMON_URL: ${PLUGIN_DAEMON_URL:-http://plugin_daemon:5002} PLUGIN_MAX_PACKAGE_SIZE: ${PLUGIN_MAX_PACKAGE_SIZE:-52428800} PLUGIN_PPROF_ENABLED: ${PLUGIN_PPROF_ENABLED:-false} PLUGIN_DEBUGGING_HOST: ${PLUGIN_DEBUGGING_HOST:-0.0.0.0} PLUGIN_DEBUGGING_PORT: ${PLUGIN_DEBUGGING_PORT:-5003} EXPOSE_PLUGIN_DEBUGGING_HOST: ${EXPOSE_PLUGIN_DEBUGGING_HOST:-localhost} EXPOSE_PLUGIN_DEBUGGING_PORT: ${EXPOSE_PLUGIN_DEBUGGING_PORT:-5003} PLUGIN_DIFY_INNER_API_KEY: ${PLUGIN_DIFY_INNER_API_KEY:-QaHbTe77CtuXmsfyhR7+vRjI/+XbV1AaFy691iy+kGDv2Jvy0/eAh8Y1} PLUGIN_DIFY_INNER_API_URL: ${PLUGIN_DIFY_INNER_API_URL:-http://api:5001} ENDPOINT_URL_TEMPLATE: ${ENDPOINT_URL_TEMPLATE:-http://localhost/e/{hook_id}} MARKETPLACE_ENABLED: ${MARKETPLACE_ENABLED:-true} MARKETPLACE_API_URL: ${MARKETPLACE_API_URL:-https://marketplace.dify.ai} FORCE_VERIFYING_SIGNATURE: ${FORCE_VERIFYING_SIGNATURE:-true} PLUGIN_STDIO_BUFFER_SIZE: ${PLUGIN_STDIO_BUFFER_SIZE:-1024} PLUGIN_STDIO_MAX_BUFFER_SIZE: ${PLUGIN_STDIO_MAX_BUFFER_SIZE:-5242880} PLUGIN_PYTHON_ENV_INIT_TIMEOUT: ${PLUGIN_PYTHON_ENV_INIT_TIMEOUT:-120} PLUGIN_MAX_EXECUTION_TIMEOUT: ${PLUGIN_MAX_EXECUTION_TIMEOUT:-600} PIP_MIRROR_URL: ${PIP_MIRROR_URL:-} PLUGIN_STORAGE_TYPE: ${PLUGIN_STORAGE_TYPE:-local} PLUGIN_STORAGE_LOCAL_ROOT: ${PLUGIN_STORAGE_LOCAL_ROOT:-/app/storage} PLUGIN_WORKING_PATH: ${PLUGIN_WORKING_PATH:-/app/storage/cwd} PLUGIN_INSTALLED_PATH: ${PLUGIN_INSTALLED_PATH:-plugin} PLUGIN_PACKAGE_CACHE_PATH: ${PLUGIN_PACKAGE_CACHE_PATH:-plugin_packages} PLUGIN_MEDIA_CACHE_PATH: ${PLUGIN_MEDIA_CACHE_PATH:-assets} PLUGIN_STORAGE_OSS_BUCKET: ${PLUGIN_STORAGE_OSS_BUCKET:-} PLUGIN_S3_USE_AWS: ${PLUGIN_S3_USE_AWS:-false} PLUGIN_S3_USE_AWS_MANAGED_IAM: ${PLUGIN_S3_USE_AWS_MANAGED_IAM:-false} PLUGIN_S3_ENDPOINT: ${PLUGIN_S3_ENDPOINT:-} PLUGIN_S3_USE_PATH_STYLE: ${PLUGIN_S3_USE_PATH_STYLE:-false} PLUGIN_AWS_ACCESS_KEY: ${PLUGIN_AWS_ACCESS_KEY:-} PLUGIN_AWS_SECRET_KEY: ${PLUGIN_AWS_SECRET_KEY:-} PLUGIN_AWS_REGION: ${PLUGIN_AWS_REGION:-} PLUGIN_AZURE_BLOB_STORAGE_CONTAINER_NAME: ${PLUGIN_AZURE_BLOB_STORAGE_CONTAINER_NAME:-} PLUGIN_AZURE_BLOB_STORAGE_CONNECTION_STRING: ${PLUGIN_AZURE_BLOB_STORAGE_CONNECTION_STRING:-} PLUGIN_TENCENT_COS_SECRET_KEY: ${PLUGIN_TENCENT_COS_SECRET_KEY:-} PLUGIN_TENCENT_COS_SECRET_ID: ${PLUGIN_TENCENT_COS_SECRET_ID:-} PLUGIN_TENCENT_COS_REGION: ${PLUGIN_TENCENT_COS_REGION:-} PLUGIN_ALIYUN_OSS_REGION: ${PLUGIN_ALIYUN_OSS_REGION:-} PLUGIN_ALIYUN_OSS_ENDPOINT: ${PLUGIN_ALIYUN_OSS_ENDPOINT:-} PLUGIN_ALIYUN_OSS_ACCESS_KEY_ID: ${PLUGIN_ALIYUN_OSS_ACCESS_KEY_ID:-} PLUGIN_ALIYUN_OSS_ACCESS_KEY_SECRET: ${PLUGIN_ALIYUN_OSS_ACCESS_KEY_SECRET:-} PLUGIN_ALIYUN_OSS_AUTH_VERSION: ${PLUGIN_ALIYUN_OSS_AUTH_VERSION:-v4} PLUGIN_ALIYUN_OSS_PATH: ${PLUGIN_ALIYUN_OSS_PATH:-} PLUGIN_VOLCENGINE_TOS_ENDPOINT: ${PLUGIN_VOLCENGINE_TOS_ENDPOINT:-} PLUGIN_VOLCENGINE_TOS_ACCESS_KEY: ${PLUGIN_VOLCENGINE_TOS_ACCESS_KEY:-} PLUGIN_VOLCENGINE_TOS_SECRET_KEY: ${PLUGIN_VOLCENGINE_TOS_SECRET_KEY:-} PLUGIN_VOLCENGINE_TOS_REGION: ${PLUGIN_VOLCENGINE_TOS_REGION:-} ENABLE_OTEL: ${ENABLE_OTEL:-false} OTLP_TRACE_ENDPOINT: ${OTLP_TRACE_ENDPOINT:-} OTLP_METRIC_ENDPOINT: ${OTLP_METRIC_ENDPOINT:-} OTLP_BASE_ENDPOINT: ${OTLP_BASE_ENDPOINT:-http://localhost:4318} OTLP_API_KEY: ${OTLP_API_KEY:-} OTEL_EXPORTER_OTLP_PROTOCOL: ${OTEL_EXPORTER_OTLP_PROTOCOL:-} OTEL_EXPORTER_TYPE: ${OTEL_EXPORTER_TYPE:-otlp} OTEL_SAMPLING_RATE: ${OTEL_SAMPLING_RATE:-0.1} OTEL_BATCH_EXPORT_SCHEDULE_DELAY: ${OTEL_BATCH_EXPORT_SCHEDULE_DELAY:-5000} OTEL_MAX_QUEUE_SIZE: ${OTEL_MAX_QUEUE_SIZE:-2048} OTEL_MAX_EXPORT_BATCH_SIZE: ${OTEL_MAX_EXPORT_BATCH_SIZE:-512} OTEL_METRIC_EXPORT_INTERVAL: ${OTEL_METRIC_EXPORT_INTERVAL:-60000} OTEL_BATCH_EXPORT_TIMEOUT: ${OTEL_BATCH_EXPORT_TIMEOUT:-10000} OTEL_METRIC_EXPORT_TIMEOUT: ${OTEL_METRIC_EXPORT_TIMEOUT:-30000} ALLOW_EMBED: ${ALLOW_EMBED:-false} QUEUE_MONITOR_THRESHOLD: ${QUEUE_MONITOR_THRESHOLD:-200} QUEUE_MONITOR_ALERT_EMAILS: ${QUEUE_MONITOR_ALERT_EMAILS:-} QUEUE_MONITOR_INTERVAL: ${QUEUE_MONITOR_INTERVAL:-30} SWAGGER_UI_ENABLED: ${SWAGGER_UI_ENABLED:-true} SWAGGER_UI_PATH: ${SWAGGER_UI_PATH:-/swagger-ui.html} ENABLE_CLEAN_EMBEDDING_CACHE_TASK: ${ENABLE_CLEAN_EMBEDDING_CACHE_TASK:-false} ENABLE_CLEAN_UNUSED_DATASETS_TASK: ${ENABLE_CLEAN_UNUSED_DATASETS_TASK:-false} ENABLE_CREATE_TIDB_SERVERLESS_TASK: ${ENABLE_CREATE_TIDB_SERVERLESS_TASK:-false} ENABLE_UPDATE_TIDB_SERVERLESS_STATUS_TASK: ${ENABLE_UPDATE_TIDB_SERVERLESS_STATUS_TASK:-false} ENABLE_CLEAN_MESSAGES: ${ENABLE_CLEAN_MESSAGES:-false} ENABLE_MAIL_CLEAN_DOCUMENT_NOTIFY_TASK: ${ENABLE_MAIL_CLEAN_DOCUMENT_NOTIFY_TASK:-false} ENABLE_DATASETS_QUEUE_MONITOR: ${ENABLE_DATASETS_QUEUE_MONITOR:-false} ENABLE_CHECK_UPGRADABLE_PLUGIN_TASK: ${ENABLE_CHECK_UPGRADABLE_PLUGIN_TASK:-true} services: # API service api: image: langgenius/dify-api:1.8.0 restart: always environment: # Use the shared environment variables. &lt;&lt;: *shared-api-worker-env # Startup mode, 'api' starts the API server. MODE: api SENTRY_DSN: ${API_SENTRY_DSN:-} SENTRY_TRACES_SAMPLE_RATE: ${API_SENTRY_TRACES_SAMPLE_RATE:-1.0} SENTRY_PROFILES_SAMPLE_RATE: ${API_SENTRY_PROFILES_SAMPLE_RATE:-1.0} PLUGIN_REMOTE_INSTALL_HOST: ${EXPOSE_PLUGIN_DEBUGGING_HOST:-localhost} PLUGIN_REMOTE_INSTALL_PORT: ${EXPOSE_PLUGIN_DEBUGGING_PORT:-5003} PLUGIN_MAX_PACKAGE_SIZE: ${PLUGIN_MAX_PACKAGE_SIZE:-52428800} INNER_API_KEY_FOR_PLUGIN: ${PLUGIN_DIFY_INNER_API_KEY:-QaHbTe77CtuXmsfyhR7+vRjI/+XbV1AaFy691iy+kGDv2Jvy0/eAh8Y1} depends_on: db: condition: service_healthy redis: condition: service_started volumes: # Mount the storage directory to the container, for storing user files. - ./volumes/app/storage:/app/api/storage networks: - ssrf_proxy_network - default # worker service # The Celery worker for processing the queue. worker: image: langgenius/dify-api:1.8.0 restart: always environment: # Use the shared environment variables. &lt;&lt;: *shared-api-worker-env # Startup mode, 'worker' starts the Celery worker for processing the queue. MODE: worker SENTRY_DSN: ${API_SENTRY_DSN:-} SENTRY_TRACES_SAMPLE_RATE: ${API_SENTRY_TRACES_SAMPLE_RATE:-1.0} SENTRY_PROFILES_SAMPLE_RATE: ${API_SENTRY_PROFILES_SAMPLE_RATE:-1.0} PLUGIN_MAX_PACKAGE_SIZE: ${PLUGIN_MAX_PACKAGE_SIZE:-52428800} INNER_API_KEY_FOR_PLUGIN: ${PLUGIN_DIFY_INNER_API_KEY:-QaHbTe77CtuXmsfyhR7+vRjI/+XbV1AaFy691iy+kGDv2Jvy0/eAh8Y1} depends_on: db: condition: service_healthy redis: condition: service_started volumes: # Mount the storage directory to the container, for storing user files. - ./volumes/app/storage:/app/api/storage networks: - ssrf_proxy_network - default # worker_beat service # Celery beat for scheduling periodic tasks. worker_beat: image: langgenius/dify-api:1.8.0 restart: always environment: # Use the shared environment variables. &lt;&lt;: *shared-api-worker-env # Startup mode, 'worker_beat' starts the Celery beat for scheduling periodic tasks. MODE: beat depends_on: db: condition: service_healthy redis: condition: service_started networks: - ssrf_proxy_network - default # Frontend web application. web: image: langgenius/dify-web:1.8.0 restart: always environment: CONSOLE_API_URL: ${CONSOLE_API_URL:-} APP_API_URL: ${APP_API_URL:-} SENTRY_DSN: ${WEB_SENTRY_DSN:-} NEXT_TELEMETRY_DISABLED: ${NEXT_TELEMETRY_DISABLED:-0} TEXT_GENERATION_TIMEOUT_MS: ${TEXT_GENERATION_TIMEOUT_MS:-60000} CSP_WHITELIST: ${CSP_WHITELIST:-} ALLOW_EMBED: ${ALLOW_EMBED:-false} 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:-} PM2_INSTANCES: ${PM2_INSTANCES:-2} LOOP_NODE_MAX_COUNT: ${LOOP_NODE_MAX_COUNT:-100} MAX_TOOLS_NUM: ${MAX_TOOLS_NUM:-10} MAX_PARALLEL_LIMIT: ${MAX_PARALLEL_LIMIT:-10} MAX_ITERATIONS_NUM: ${MAX_ITERATIONS_NUM:-99} MAX_TREE_DEPTH: ${MAX_TREE_DEPTH:-50} ENABLE_WEBSITE_JINAREADER: ${ENABLE_WEBSITE_JINAREADER:-true} ENABLE_WEBSITE_FIRECRAWL: ${ENABLE_WEBSITE_FIRECRAWL:-true} ENABLE_WEBSITE_WATERCRAWL: ${ENABLE_WEBSITE_WATERCRAWL:-true} # The postgres database. db: image: postgres:15-alpine restart: always environment: POSTGRES_USER: ${POSTGRES_USER:-postgres} POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:-difyai123456} POSTGRES_DB: ${POSTGRES_DB:-dify} PGDATA: ${PGDATA:-/var/lib/postgresql/data/pgdata} command: &gt; postgres -c 'max_connections=${POSTGRES_MAX_CONNECTIONS:-100}' -c 'shared_buffers=${POSTGRES_SHARED_BUFFERS:-128MB}' -c 'work_mem=${POSTGRES_WORK_MEM:-4MB}' -c 'maintenance_work_mem=${POSTGRES_MAINTENANCE_WORK_MEM:-64MB}' -c 'effective_cache_size=${POSTGRES_EFFECTIVE_CACHE_SIZE:-4096MB}' volumes: - ./volumes/db/data:/var/lib/postgresql/data healthcheck: test: [ 'CMD', 'pg_isready', '-h', 'db', '-U', '${PGUSER:-postgres}', '-d', '${POSTGRES_DB:-dify}' ] interval: 1s timeout: 3s retries: 60 # The redis cache. redis: image: redis:6-alpine restart: always environment: REDISCLI_AUTH: ${REDIS_PASSWORD:-difyai123456} volumes: # Mount the redis data directory to the container. - ./volumes/redis/data:/data # Set the redis password when startup redis server. command: redis-server --requirepass ${REDIS_PASSWORD:-difyai123456} healthcheck: test: [ 'CMD', 'redis-cli', 'ping' ] # The DifySandbox sandbox: image: langgenius/dify-sandbox:0.2.12 restart: always environment: # The DifySandbox configurations # Make sure you are changing this key for your deployment with a strong key. # You can generate a strong key using `openssl rand -base64 42`. API_KEY: ${SANDBOX_API_KEY:-dify-sandbox} GIN_MODE: ${SANDBOX_GIN_MODE:-release} WORKER_TIMEOUT: ${SANDBOX_WORKER_TIMEOUT:-15} ENABLE_NETWORK: ${SANDBOX_ENABLE_NETWORK:-true} HTTP_PROXY: ${SANDBOX_HTTP_PROXY:-http://ssrf_proxy:3128} HTTPS_PROXY: ${SANDBOX_HTTPS_PROXY:-http://ssrf_proxy:3128} SANDBOX_PORT: ${SANDBOX_PORT:-8194} PIP_MIRROR_URL: ${PIP_MIRROR_URL:-} volumes: - ./volumes/sandbox/dependencies:/dependencies - ./volumes/sandbox/conf:/conf healthcheck: test: [ 'CMD', 'curl', '-f', 'http://localhost:8194/health' ] networks: - ssrf_proxy_network # plugin daemon plugin_daemon: image: langgenius/dify-plugin-daemon:0.2.0-local restart: always environment: # Use the shared environment variables. &lt;&lt;: *shared-api-worker-env DB_DATABASE: ${DB_PLUGIN_DATABASE:-dify_plugin} SERVER_PORT: ${PLUGIN_DAEMON_PORT:-5002} SERVER_KEY: ${PLUGIN_DAEMON_KEY:-lYkiYYT6owG+71oLerGzA7GXCgOT++6ovaezWAjpCjf+Sjc3ZtU+qUEi} MAX_PLUGIN_PACKAGE_SIZE: ${PLUGIN_MAX_PACKAGE_SIZE:-52428800} PPROF_ENABLED: ${PLUGIN_PPROF_ENABLED:-false} DIFY_INNER_API_URL: ${PLUGIN_DIFY_INNER_API_URL:-http://api:5001} DIFY_INNER_API_KEY: ${PLUGIN_DIFY_INNER_API_KEY:-QaHbTe77CtuXmsfyhR7+vRjI/+XbV1AaFy691iy+kGDv2Jvy0/eAh8Y1} PLUGIN_REMOTE_INSTALLING_HOST: ${PLUGIN_DEBUGGING_HOST:-0.0.0.0} PLUGIN_REMOTE_INSTALLING_PORT: ${PLUGIN_DEBUGGING_PORT:-5003} PLUGIN_WORKING_PATH: ${PLUGIN_WORKING_PATH:-/app/storage/cwd} FORCE_VERIFYING_SIGNATURE: ${FORCE_VERIFYING_SIGNATURE:-true} PYTHON_ENV_INIT_TIMEOUT: ${PLUGIN_PYTHON_ENV_INIT_TIMEOUT:-120} PLUGIN_MAX_EXECUTION_TIMEOUT: ${PLUGIN_MAX_EXECUTION_TIMEOUT:-600} PLUGIN_STDIO_BUFFER_SIZE: ${PLUGIN_STDIO_BUFFER_SIZE:-1024} PLUGIN_STDIO_MAX_BUFFER_SIZE: ${PLUGIN_STDIO_MAX_BUFFER_SIZE:-5242880} PIP_MIRROR_URL: ${PIP_MIRROR_URL:-} PLUGIN_STORAGE_TYPE: ${PLUGIN_STORAGE_TYPE:-local} PLUGIN_STORAGE_LOCAL_ROOT: ${PLUGIN_STORAGE_LOCAL_ROOT:-/app/storage} PLUGIN_INSTALLED_PATH: ${PLUGIN_INSTALLED_PATH:-plugin} PLUGIN_PACKAGE_CACHE_PATH: ${PLUGIN_PACKAGE_CACHE_PATH:-plugin_packages} PLUGIN_MEDIA_CACHE_PATH: ${PLUGIN_MEDIA_CACHE_PATH:-assets} PLUGIN_STORAGE_OSS_BUCKET: ${PLUGIN_STORAGE_OSS_BUCKET:-} S3_USE_AWS_MANAGED_IAM: ${PLUGIN_S3_USE_AWS_MANAGED_IAM:-false} S3_USE_AWS: ${PLUGIN_S3_USE_AWS:-false} S3_ENDPOINT: ${PLUGIN_S3_ENDPOINT:-} S3_USE_PATH_STYLE: ${PLUGIN_S3_USE_PATH_STYLE:-false} AWS_ACCESS_KEY: ${PLUGIN_AWS_ACCESS_KEY:-} AWS_SECRET_KEY: ${PLUGIN_AWS_SECRET_KEY:-} AWS_REGION: ${PLUGIN_AWS_REGION:-} AZURE_BLOB_STORAGE_CONNECTION_STRING: ${PLUGIN_AZURE_BLOB_STORAGE_CONNECTION_STRING:-} AZURE_BLOB_STORAGE_CONTAINER_NAME: ${PLUGIN_AZURE_BLOB_STORAGE_CONTAINER_NAME:-} TENCENT_COS_SECRET_KEY: ${PLUGIN_TENCENT_COS_SECRET_KEY:-} TENCENT_COS_SECRET_ID: ${PLUGIN_TENCENT_COS_SECRET_ID:-} TENCENT_COS_REGION: ${PLUGIN_TENCENT_COS_REGION:-} ALIYUN_OSS_REGION: ${PLUGIN_ALIYUN_OSS_REGION:-} ALIYUN_OSS_ENDPOINT: ${PLUGIN_ALIYUN_OSS_ENDPOINT:-} ALIYUN_OSS_ACCESS_KEY_ID: ${PLUGIN_ALIYUN_OSS_ACCESS_KEY_ID:-} ALIYUN_OSS_ACCESS_KEY_SECRET: ${PLUGIN_ALIYUN_OSS_ACCESS_KEY_SECRET:-} ALIYUN_OSS_AUTH_VERSION: ${PLUGIN_ALIYUN_OSS_AUTH_VERSION:-v4} ALIYUN_OSS_PATH: ${PLUGIN_ALIYUN_OSS_PATH:-} VOLCENGINE_TOS_ENDPOINT: ${PLUGIN_VOLCENGINE_TOS_ENDPOINT:-} VOLCENGINE_TOS_ACCESS_KEY: ${PLUGIN_VOLCENGINE_TOS_ACCESS_KEY:-} VOLCENGINE_TOS_SECRET_KEY: ${PLUGIN_VOLCENGINE_TOS_SECRET_KEY:-} VOLCENGINE_TOS_REGION: ${PLUGIN_VOLCENGINE_TOS_REGION:-} ports: - "${EXPOSE_PLUGIN_DEBUGGING_PORT:-5003}:${PLUGIN_DEBUGGING_PORT:-5003}" volumes: - ./volumes/plugin_daemon:/app/storage depends_on: db: condition: service_healthy # ssrf_proxy server # for more information, please refer to # https://docs.dify.ai/learn-more/faq/install-faq#18-why-is-ssrf-proxy-needed%3F ssrf_proxy: image: ubuntu/squid:latest restart: always volumes: - ./ssrf_proxy/squid.conf.template:/etc/squid/squid.conf.template - ./ssrf_proxy/docker-entrypoint.sh:/docker-entrypoint-mount.sh entrypoint: [ 'sh', '-c', "cp /docker-entrypoint-mount.sh /docker-entrypoint.sh &amp;&amp; sed -i 's/\r$$//' /docker-entrypoint.sh &amp;&amp; chmod +x /docker-entrypoint.sh &amp;&amp; /docker-entrypoint.sh" ] environment: # pls clearly modify the squid env vars to fit your network environment. HTTP_PORT: ${SSRF_HTTP_PORT:-3128} COREDUMP_DIR: ${SSRF_COREDUMP_DIR:-/var/spool/squid} REVERSE_PROXY_PORT: ${SSRF_REVERSE_PROXY_PORT:-8194} SANDBOX_HOST: ${SSRF_SANDBOX_HOST:-sandbox} SANDBOX_PORT: ${SANDBOX_PORT:-8194} networks: - ssrf_proxy_network - default # Certbot service # use `docker-compose --profile certbot up` to start the certbot service. certbot: image: certbot/certbot profiles: - certbot volumes: - ./volumes/certbot/conf:/etc/letsencrypt - ./volumes/certbot/www:/var/www/html - ./volumes/certbot/logs:/var/log/letsencrypt - ./volumes/certbot/conf/live:/etc/letsencrypt/live - ./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_OPTIONS=${CERTBOT_OPTIONS:-} entrypoint: [ '/docker-entrypoint.sh' ] command: [ 'tail', '-f', '/dev/null' ] # The nginx reverse proxy. # used for reverse proxying the API service and Web service. nginx: image: nginx:latest restart: always volumes: - ./nginx/nginx.conf.template:/etc/nginx/nginx.conf.template - ./nginx/proxy.conf.template:/etc/nginx/proxy.conf.template - ./nginx/https.conf.template:/etc/nginx/https.conf.template - ./nginx/conf.d:/etc/nginx/conf.d - ./nginx/docker-entrypoint.sh:/docker-entrypoint-mount.sh - ./nginx/ssl:/etc/ssl # cert dir (legacy) - ./volumes/certbot/conf/live:/etc/letsencrypt/live # cert dir (with certbot container) - ./volumes/certbot/conf:/etc/letsencrypt - ./volumes/certbot/www:/var/www/html entrypoint: [ 'sh', '-c', "cp /docker-entrypoint-mount.sh /docker-entrypoint.sh &amp;&amp; sed -i 's/\r$$//' /docker-entrypoint.sh &amp;&amp; chmod +x /docker-entrypoint.sh &amp;&amp; /docker-entrypoint.sh" ] environment: NGINX_SERVER_NAME: ${NGINX_SERVER_NAME:-_} NGINX_HTTPS_ENABLED: ${NGINX_HTTPS_ENABLED:-false} NGINX_SSL_PORT: ${NGINX_SSL_PORT:-443} NGINX_PORT: ${NGINX_PORT:-80} # You're required to add your own SSL certificates/keys to the `./nginx/ssl` directory # and modify the env vars below in .env if HTTPS_ENABLED is true. NGINX_SSL_CERT_FILENAME: ${NGINX_SSL_CERT_FILENAME:-dify.crt} NGINX_SSL_CERT_KEY_FILENAME: ${NGINX_SSL_CERT_KEY_FILENAME:-dify.key} NGINX_SSL_PROTOCOLS: ${NGINX_SSL_PROTOCOLS:-TLSv1.1 TLSv1.2 TLSv1.3} NGINX_WORKER_PROCESSES: ${NGINX_WORKER_PROCESSES:-auto} NGINX_CLIENT_MAX_BODY_SIZE: ${NGINX_CLIENT_MAX_BODY_SIZE:-100M} NGINX_KEEPALIVE_TIMEOUT: ${NGINX_KEEPALIVE_TIMEOUT:-65} 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_DOMAIN: ${CERTBOT_DOMAIN:-} depends_on: - api - web ports: - '${EXPOSE_NGINX_PORT:-80}:${NGINX_PORT:-80}' - '${EXPOSE_NGINX_SSL_PORT:-443}:${NGINX_SSL_PORT:-443}' # The Weaviate vector store. weaviate: image: semitechnologies/weaviate:1.19.0 profiles: - '' - weaviate restart: always volumes: # Mount the Weaviate data directory to the con tainer. - ./volumes/weaviate:/var/lib/weaviate environment: # The Weaviate configurations # You can refer to the [Weaviate](https://weaviate.io/developers/weaviate/config-refs/env-vars) documentation for more information. PERSISTENCE_DATA_PATH: ${WEAVIATE_PERSISTENCE_DATA_PATH:-/var/lib/weaviate} QUERY_DEFAULTS_LIMIT: ${WEAVIATE_QUERY_DEFAULTS_LIMIT:-25} AUTHENTICATION_ANONYMOUS_ACCESS_ENABLED: ${WEAVIATE_AUTHENTICATION_ANONYMOUS_ACCESS_ENABLED:-false} DEFAULT_VECTORIZER_MODULE: ${WEAVIATE_DEFAULT_VECTORIZER_MODULE:-none} CLUSTER_HOSTNAME: ${WEAVIATE_CLUSTER_HOSTNAME:-node1} AUTHENTICATION_APIKEY_ENABLED: ${WEAVIATE_AUTHENTICATION_APIKEY_ENABLED:-true} AUTHENTICATION_APIKEY_ALLOWED_KEYS: ${WEAVIATE_AUTHENTICATION_APIKEY_ALLOWED_KEYS:-WVF5YThaHlkYwhGUSmCRgsX3tD5ngdN8pkih} AUTHENTICATION_APIKEY_USERS: ${WEAVIATE_AUTHENTICATION_APIKEY_USERS:-hello@dify.ai} AUTHORIZATION_ADMINLIST_ENABLED: ${WEAVIATE_AUTHORIZATION_ADMINLIST_ENABLED:-true} AUTHORIZATION_ADMINLIST_USERS: ${WEAVIATE_AUTHORIZATION_ADMINLIST_USERS:-hello@dify.ai} # Qdrant vector store. # (if used, you need to set VECTOR_STORE to qdrant in the api &amp; worker service.) qdrant: image: langgenius/qdrant:v1.7.3 profiles: - qdrant restart: always volumes: - ./volumes/qdrant:/qdrant/storage environment: QDRANT_API_KEY: ${QDRANT_API_KEY:-difyai123456} # The Couchbase vector store. couchbase-server: build: ./couchbase-server profiles: - couchbase restart: always environment: - CLUSTER_NAME=dify_search - COUCHBASE_ADMINISTRATOR_USERNAME=${COUCHBASE_USER:-Administrator} - COUCHBASE_ADMINISTRATOR_PASSWORD=${COUCHBASE_PASSWORD:-password} - COUCHBASE_BUCKET=${COUCHBASE_BUCKET_NAME:-Embeddings} - COUCHBASE_BUCKET_RAMSIZE=512 - COUCHBASE_RAM_SIZE=2048 - COUCHBASE_EVENTING_RAM_SIZE=512 - COUCHBASE_INDEX_RAM_SIZE=512 - COUCHBASE_FTS_RAM_SIZE=1024 hostname: couchbase-server container_name: couchbase-server working_dir: /opt/couchbase stdin_open: true tty: true entrypoint: [ "" ] command: sh -c "/opt/couchbase/init/init-cbserver.sh" volumes: - ./volumes/couchbase/data:/opt/couchbase/var/lib/couchbase/data healthcheck: # ensure bucket was created before proceeding test: [ "CMD-SHELL", "curl -s -f -u Administrator:password http://localhost:8091/pools/default/buckets | grep -q '\\[{' || exit 1" ] interval: 10s retries: 10 start_period: 30s timeout: 10s # The pgvector vector database. pgvector: image: pgvector/pgvector:pg16 profiles: - pgvector restart: always environment: PGUSER: ${PGVECTOR_PGUSER:-postgres} # The password for the default postgres user. POSTGRES_PASSWORD: ${PGVECTOR_POSTGRES_PASSWORD:-difyai123456} # The name of the default postgres database. POSTGRES_DB: ${PGVECTOR_POSTGRES_DB:-dify} # postgres data directory PGDATA: ${PGVECTOR_PGDATA:-/var/lib/postgresql/data/pgdata} # pg_bigm module for full text search PG_BIGM: ${PGVECTOR_PG_BIGM:-false} PG_BIGM_VERSION: ${PGVECTOR_PG_BIGM_VERSION:-1.2-20240606} volumes: - ./volumes/pgvector/data:/var/lib/postgresql/data - ./pgvector/docker-entrypoint.sh:/docker-entrypoint.sh entrypoint: [ '/docker-entrypoint.sh' ] healthcheck: test: [ 'CMD', 'pg_isready' ] interval: 1s timeout: 3s retries: 30 # get image from https://www.vastdata.com.cn/ vastbase: image: vastdata/vastbase-vector profiles: - vastbase restart: always environment: - VB_DBCOMPATIBILITY=PG - VB_DB=dify - VB_USERNAME=dify - VB_PASSWORD=Difyai123456 ports: - '5434:5432' volumes: - ./vastbase/lic:/home/vastbase/vastbase/lic - ./vastbase/data:/home/vastbase/data - ./vastbase/backup:/home/vastbase/backup - ./vastbase/backup_log:/home/vastbase/backup_log healthcheck: test: [ 'CMD', 'pg_isready' ] interval: 1s timeout: 3s retries: 30 # pgvecto-rs vector store pgvecto-rs: image: tensorchord/pgvecto-rs:pg16-v0.3.0 profiles: - pgvecto-rs restart: always environment: PGUSER: ${PGVECTOR_PGUSER:-postgres} # The password for the default postgres user. POSTGRES_PASSWORD: ${PGVECTOR_POSTGRES_PASSWORD:-difyai123456} # The name of the default postgres database. POSTGRES_DB: ${PGVECTOR_POSTGRES_DB:-dify} # postgres data directory PGDATA: ${PGVECTOR_PGDATA:-/var/lib/postgresql/data/pgdata} volumes: - ./volumes/pgvecto_rs/data:/var/lib/postgresql/data healthcheck: test: [ 'CMD', 'pg_isready' ] interval: 1s timeout: 3s retries: 30 # Chroma vector database chroma: image: ghcr.io/chroma-core/chroma:0.5.20 profiles: - chroma restart: always volumes: - ./volumes/chroma:/chroma/chroma environment: CHROMA_SERVER_AUTHN_CREDENTIALS: ${CHROMA_SERVER_AUTHN_CREDENTIALS:-difyai123456} CHROMA_SERVER_AUTHN_PROVIDER: ${CHROMA_SERVER_AUTHN_PROVIDER:-chromadb.auth.token_authn.TokenAuthenticationServerProvider} IS_PERSISTENT: ${CHROMA_IS_PERSISTENT:-TRUE} # OceanBase vector database oceanbase: image: oceanbase/oceanbase-ce:4.3.5-lts container_name: oceanbase profiles: - oceanbase restart: always volumes: - ./volumes/oceanbase/data:/root/ob - ./volumes/oceanbase/conf:/root/.obd/cluster - ./volumes/oceanbase/init.d:/root/boot/init.d environment: OB_MEMORY_LIMIT: ${OCEANBASE_MEMORY_LIMIT:-6G} OB_SYS_PASSWORD: ${OCEANBASE_VECTOR_PASSWORD:-difyai123456} OB_TENANT_PASSWORD: ${OCEANBASE_VECTOR_PASSWORD:-difyai123456} OB_CLUSTER_NAME: ${OCEANBASE_CLUSTER_NAME:-difyai} OB_SERVER_IP: 127.0.0.1 MODE: mini ports: - "${OCEANBASE_VECTOR_PORT:-2881}:2881" healthcheck: test: [ 'CMD-SHELL', 'obclient -h127.0.0.1 -P2881 -uroot@test -p$${OB_TENANT_PASSWORD} -e "SELECT 1;"' ] interval: 10s retries: 30 start_period: 30s timeout: 10s # Oracle vector database oracle: image: container-registry.oracle.com/database/free:latest profiles: - oracle restart: always volumes: - source: oradata type: volume target: /opt/oracle/oradata - ./startupscripts:/opt/oracle/scripts/startup environment: ORACLE_PWD: ${ORACLE_PWD:-Dify123456} ORACLE_CHARACTERSET: ${ORACLE_CHARACTERSET:-AL32UTF8} # Milvus vector database services etcd: container_name: milvus-etcd image: quay.io/coreos/etcd:v3.5.5 profiles: - milvus environment: ETCD_AUTO_COMPACTION_MODE: ${ETCD_AUTO_COMPACTION_MODE:-revision} ETCD_AUTO_COMPACTION_RETENTION: ${ETCD_AUTO_COMPACTION_RETENTION:-1000} ETCD_QUOTA_BACKEND_BYTES: ${ETCD_QUOTA_BACKEND_BYTES:-4294967296} ETCD_SNAPSHOT_COUNT: ${ETCD_SNAPSHOT_COUNT:-50000} volumes: - ./volumes/milvus/etcd:/etcd command: etcd -advertise-client-urls=http://127.0.0.1:2379 -listen-client-urls http://0.0.0.0:2379 --data-dir /etcd healthcheck: test: [ 'CMD', 'etcdctl', 'endpoint', 'health' ] interval: 30s timeout: 20s retries: 3 networks: - milvus minio: container_name: milvus-minio image: minio/minio:RELEASE.2023-03-20T20-16-18Z profiles: - milvus environment: MINIO_ACCESS_KEY: ${MINIO_ACCESS_KEY:-minioadmin} MINIO_SECRET_KEY: ${MINIO_SECRET_KEY:-minioadmin} volumes: - ./volumes/milvus/minio:/minio_data command: minio server /minio_data --console-address ":9001" healthcheck: test: [ 'CMD', 'curl', '-f', 'http://localhost:9000/minio/health/live' ] interval: 30s timeout: 20s retries: 3 networks: - milvus milvus-standalone: container_name: milvus-standalone image: milvusdb/milvus:v2.5.15 profiles: - milvus command: [ 'milvus', 'run', 'standalone' ] environment: ETCD_ENDPOINTS: ${ETCD_ENDPOINTS:-etcd:2379} MINIO_ADDRESS: ${MINIO_ADDRESS:-minio:9000} common.security.authorizationEnabled: ${MILVUS_AUTHORIZATION_ENABLED:-true} volumes: - ./volumes/milvus/milvus:/var/lib/milvus healthcheck: test: [ 'CMD', 'curl', '-f', 'http://localhost:9091/healthz' ] interval: 30s start_period: 90s timeout: 20s retries: 3 depends_on: - etcd - minio ports: - 19530:19530 - 9091:9091 networks: - milvus # Opensearch vector database opensearch: container_name: opensearch image: opensearchproject/opensearch:latest profiles: - opensearch environment: discovery.type: ${OPENSEARCH_DISCOVERY_TYPE:-single-node} bootstrap.memory_lock: ${OPENSEARCH_BOOTSTRAP_MEMORY_LOCK:-true} OPENSEARCH_JAVA_OPTS: -Xms${OPENSEARCH_JAVA_OPTS_MIN:-512m} -Xmx${OPENSEARCH_JAVA_OPTS_MAX:-1024m} OPENSEARCH_INITIAL_ADMIN_PASSWORD: ${OPENSEARCH_INITIAL_ADMIN_PASSWORD:-Qazwsxedc!@#123} ulimits: memlock: soft: ${OPENSEARCH_MEMLOCK_SOFT:--1} hard: ${OPENSEARCH_MEMLOCK_HARD:--1} nofile: soft: ${OPENSEARCH_NOFILE_SOFT:-65536} hard: ${OPENSEARCH_NOFILE_HARD:-65536} volumes: - ./volumes/opensearch/data:/usr/share/opensearch/data networks: - opensearch-net opensearch-dashboards: container_name: opensearch-dashboards image: opensearchproject/opensearch-dashboards:latest profiles: - opensearch environment: OPENSEARCH_HOSTS: '["https://opensearch:9200"]' volumes: - ./volumes/opensearch/opensearch_dashboards.yml:/usr/share/opensearch-dashboards/config/opensearch_dashboards.yml networks: - opensearch-net depends_on: - opensearch # opengauss vector database. opengauss: image: opengauss/opengauss:7.0.0-RC1 profiles: - opengauss privileged: true restart: always environment: GS_USERNAME: ${OPENGAUSS_USER:-postgres} GS_PASSWORD: ${OPENGAUSS_PASSWORD:-Dify@123} GS_PORT: ${OPENGAUSS_PORT:-6600} GS_DB: ${OPENGAUSS_DATABASE:-dify} volumes: - ./volumes/opengauss/data:/var/lib/opengauss/data healthcheck: test: [ "CMD-SHELL", "netstat -lntp | grep tcp6 &gt; /dev/null 2&gt;&amp;1" ] interval: 10s timeout: 10s retries: 10 ports: - ${OPENGAUSS_PORT:-6600}:${OPENGAUSS_PORT:-6600} # MyScale vector database myscale: container_name: myscale image: myscale/myscaledb:1.6.4 profiles: - myscale restart: always tty: true volumes: - ./volumes/myscale/data:/var/lib/clickhouse - ./volumes/myscale/log:/var/log/clickhouse-server - ./volumes/myscale/config/users.d/custom_users_config.xml:/etc/clickhouse-server/users.d/custom_users_config.xml ports: - ${MYSCALE_PORT:-8123}:${MYSCALE_PORT:-8123} # Matrixone vector store. matrixone: hostname: matrixone image: matrixorigin/matrixone:2.1.1 profiles: - matrixone restart: always volumes: - ./volumes/matrixone/data:/mo-data ports: - ${MATRIXONE_PORT:-6001}:${MATRIXONE_PORT:-6001} # https://www.elastic.co/guide/en/elasticsearch/reference/current/settings.html # https://www.elastic.co/guide/en/elasticsearch/reference/current/docker.html#docker-prod-prerequisites elasticsearch: image: docker.elastic.co/elasticsearch/elasticsearch:8.14.3 container_name: elasticsearch profiles: - elasticsearch - elasticsearch-ja restart: always volumes: - ./elasticsearch/docker-entrypoint.sh:/docker-entrypoint-mount.sh - dify_es01_data:/usr/share/elasticsearch/data environment: ELASTIC_PASSWORD: ${ELASTICSEARCH_PASSWORD:-elastic} VECTOR_STORE: ${VECTOR_STORE:-} cluster.name: dify-es-cluster node.name: dify-es0 discovery.type: single-node xpack.license.self_generated.type: basic xpack.security.enabled: 'true' xpack.security.enrollment.enabled: 'false' xpack.security.http.ssl.enabled: 'false' ports: - ${ELASTICSEARCH_PORT:-9200}:9200 deploy: resources: limits: memory: 2g entrypoint: [ 'sh', '-c', "sh /docker-entrypoint-mount.sh" ] healthcheck: test: [ 'CMD', 'curl', '-s', 'http://localhost:9200/_cluster/health?pretty' ] interval: 30s timeout: 10s retries: 50 # https://www.elastic.co/guide/en/kibana/current/docker.html # https://www.elastic.co/guide/en/kibana/current/settings.html kibana: image: docker.elastic.co/kibana/kibana:8.14.3 container_name: kibana profiles: - elasticsearch depends_on: - elasticsearch restart: always environment: XPACK_ENCRYPTEDSAVEDOBJECTS_ENCRYPTIONKEY: d1a66dfd-c4d3-4a0a-8290-2abcb83ab3aa NO_PROXY: localhost,127.0.0.1,elasticsearch,kibana XPACK_SECURITY_ENABLED: 'true' XPACK_SECURITY_ENROLLMENT_ENABLED: 'false' XPACK_SECURITY_HTTP_SSL_ENABLED: 'false' XPACK_FLEET_ISAIRGAPPED: 'true' I18N_LOCALE: zh-CN SERVER_PORT: '5601' ELASTICSEARCH_HOSTS: http://elasticsearch:9200 ports: - ${KIBANA_PORT:-5601}:5601 healthcheck: test: [ 'CMD-SHELL', 'curl -s http://localhost:5601 &gt;/dev/null || exit 1' ] interval: 30s timeout: 10s retries: 3 # unstructured . # (if used, you need to set ETL_TYPE to Unstructured in the api &amp; worker service.) unstructured: image: downloads.unstructured.io/unstructured-io/unstructured-api:latest profiles: - unstructured restart: always volumes: - ./volumes/unstructured:/app/data networks: # create a network between sandbox, api and ssrf_proxy, and can not access outside. ssrf_proxy_network: driver: bridge internal: true milvus: driver: bridge opensearch-net: driver: bridge internal: true volumes: oradata: dify_es01_data: </code></pre> <p>其实一个一个分析,它的结构和我们之前部署的docker项目是类似的,只不过一次性起了很多个容器。</p> <p>我们这边可以先不管它,直接暴力拉起</p> <pre><code class="language-bash">docker compose up -d </code></pre> <p>我这边在公司,本来以为很快能拉好镜像,结果网非常的慢,我等不及就去吃中饭了,等到吃饭回来发现已经拉好了。</p> <p><img src="https://blog.laoda.de/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fimg.laoda.de%2Fi%2F2025%2F08%2F28%2F160453-1.webp&amp;size=m" alt="img"></p> <p>在这期间,如果决定要本地使用,不调用云端API的话,我们可以先把Ollama和本地的模型先下载起来。</p> <h2 id="安装Ollama-可选-">安装Ollama(可选)</h2> <p><img src="https://blog.laoda.de/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fimg.laoda.de%2Fi%2F2025%2F08%2F28%2F160447-1.webp&amp;size=m" alt="img"></p> <p>其实这个也可以用docker来装,但是既然给了安装版,我们就直接到官网下载即可。</p> <p>什么是Ollama?</p> <img src="https://blog.laoda.de/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fimg.laoda.de%2Fi%2F2024%2F05%2F21%2F110652-1.webp&amp;size=m" alt="62dc7ef085dd48a06b4bcce9782a12c5.png" style="zoom:33%;"> <p>Ollama 是一个便于本地部署和运行大型语言模型(Large Language Models, LLMs)的工具。使用通俗的语言来说,如果你想在自己的电脑上运行如 GPT这样的大型人工智能模型,而不是通过互联网连接到它们,那么 Ollama 是一个实现这一目标的工具。</p> <p>Ollama支持非常多的开源模型,比如:</p> <p><img src="https://blog.laoda.de/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fimg.laoda.de%2Fi%2F2025%2F08%2F28%2F160440-1.webp&amp;size=m" alt="img"></p> <p>更多支持的模型可以看这边:https://ollama.com/library</p> <p>当然它还支持自定义模型,这边就不深入了,有兴趣的可以研究:https://github.com/ollama/ollama</p> <p>装好之后,命令行运行:</p> <pre><code class="language-bash">ollama -v </code></pre> <p>查看到版本号说明安装好了。</p> <p>接下来,比如我们下载一个llama3 8B的模型:</p> <pre><code class="language-bash">ollama run llama3 </code></pre> <p>这边可能需要比较长的时间,取决于你的网速。</p> <p>下载好了就可以进行交互了:</p> <p><img src="https://blog.laoda.de/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fimg.laoda.de%2Fi%2F2025%2F08%2F28%2F160436-1.webp&amp;size=m" alt="b8179cebba4cd58465fd233020035fc9.png"></p> <p><code>/?</code> 可以查看帮助:</p> <p><img src="https://blog.laoda.de/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fimg.laoda.de%2Fi%2F2025%2F08%2F28%2F160434-1.webp&amp;size=m" alt="576b3e8c12d6561d8c9059e8b8504d14.png"></p> <p><img src="https://blog.laoda.de/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fimg.laoda.de%2Fi%2F2025%2F08%2F28%2F160430-1.webp&amp;size=m" alt="7a9e6a8e285b4562c6a39c83a0c468b5.png"></p> <p><code>/bye</code> 可以退出</p> <img src="https://blog.laoda.de/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fimg.laoda.de%2Fi%2F2025%2F08%2F28%2F160428-1.webp&amp;size=m" alt="f6b660f17b96db1219cff57b888ec428.png" style="zoom:50%;"> <p>其实现在已经搞定了,你可以让它帮你干活了,但是每次都要用命令行,有些人觉得不方便,而且也不能给别人用,下面我们就给它搞一个图形化界面,扩展一下功能,也能让局域网里的其他小伙伴也用上你的这个模型(注意多人同时访问会导致机器负载飙升,具体取决于你机器的性能和模型参数的大小)</p> <h2 id="配置Dify">配置Dify</h2> <p>等所有容器都拉取好了,我们就可以通过在浏览器地址栏输入:<code>127.0.0.1</code>来访问Dify了。</p> <p>第一次登录会让你注册一个管理员账号,然后我们就可以登录了,</p> <p><img src="https://blog.laoda.de/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fimg.laoda.de%2Fi%2F2025%2F08%2F28%2F160422-1.webp&amp;size=m" alt="img"></p> <p>后台是这个样子的,</p> <p><img src="https://blog.laoda.de/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fimg.laoda.de%2Fi%2F2025%2F08%2F28%2F160420-1.webp&amp;size=m" alt="img"></p> <p>我们先来到设置,配置一下大模型,</p> <p><img src="https://blog.laoda.de/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fimg.laoda.de%2Fi%2F2025%2F08%2F28%2F160417-1.webp&amp;size=m" alt="f0ba80de97e161c2021d2cebc76c1306.png"></p> <p>这边可以看到很多的模型供应商,</p> <p><img src="https://blog.laoda.de/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fimg.laoda.de%2Fi%2F2025%2F08%2F28%2F160412-1.webp&amp;size=m" alt="img"></p> <p>我们本地部署,就用ollama,</p> <p><img src="https://blog.laoda.de/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fimg.laoda.de%2Fi%2F2025%2F08%2F28%2F160409-1.webp&amp;size=m" alt="img"></p> <p>点击安装,</p> <p><img src="https://blog.laoda.de/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fimg.laoda.de%2Fi%2F2025%2F08%2F28%2F160407-1.webp&amp;size=m" alt="img"></p> <p>安装好了之后,来添加模型,</p> <p><img src="https://blog.laoda.de/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fimg.laoda.de%2Fi%2F2025%2F08%2F28%2F160405-1.webp&amp;size=m" alt="img"></p> <p>这里的基础URL,填写:<code>http://host.docker.internal:11434</code></p> <p><img src="https://blog.laoda.de/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fimg.laoda.de%2Fi%2F2025%2F08%2F28%2F160402-1.webp&amp;size=m" alt="img"></p> <p>模型名称的话,看看我们ollama里下载了什么模型,打开终端,输入</p> <pre><code class="language-bash">ollama list </code></pre> <p>可以看到我这边下载了很多模型,</p> <p><img src="https://blog.laoda.de/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fimg.laoda.de%2Fi%2F2025%2F08%2F28%2F160359-1.webp&amp;size=m" alt="img"></p> <p>我们就选择gpt最近刚开源的<code>gpt-oss:20b</code></p> <p>其他信息填写如图,</p> <p><img src="https://blog.laoda.de/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fimg.laoda.de%2Fi%2F2025%2F08%2F28%2F160355-1.webp&amp;size=m" alt="632a2fed26d697929b7faa2867d9895b.png"></p> <p>这边是支持函数调用的,选择是,不支持视觉,</p> <p><img src="https://blog.laoda.de/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fimg.laoda.de%2Fi%2F2025%2F08%2F28%2F160350-1.webp&amp;size=m" alt="81e6c15393c9cfc5032318734b6b51e4.png"></p> <p>这样我们就添加好一个模型啦。</p> <p><img src="https://blog.laoda.de/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fimg.laoda.de%2Fi%2F2025%2F08%2F28%2F160337-1.webp&amp;size=m" alt="img"></p> <p>如果你的本地电脑算力不够,也可以选择使用云端的模型,比如DeepSeek之类的,</p> <p>添加方法也很简单,下载模型供应商,然后在这边点击配置,</p> <p><img src="https://blog.laoda.de/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fimg.laoda.de%2Fi%2F2025%2F08%2F28%2F160335-1.webp&amp;size=m" alt="img"></p> <p>填入你在DeepSeek官网拿到的API Key就行了。</p> <p>OpenAI、ANTHROPIC也是类似的方法,填API Key就可以。</p> <p>接着,我们就来创建一个空白应用,</p> <p><img src="https://blog.laoda.de/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fimg.laoda.de%2Fi%2F2025%2F08%2F28%2F160332-1.webp&amp;size=m" alt="img"></p> <p>这边选择最简单的,创建一个聊天助手,</p> <p><img src="https://blog.laoda.de/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fimg.laoda.de%2Fi%2F2025%2F08%2F28%2F160331-1.webp&amp;size=m" alt="img"></p> <p>创建的过程中,你可以调试,这边的自定义程度会高一些,</p> <p><img src="https://blog.laoda.de/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fimg.laoda.de%2Fi%2F2025%2F08%2F28%2F160327-1.webp&amp;size=m" alt="img"></p> <p>创建好之后,可以选择发布,或者点击探索,</p> <p><img src="https://blog.laoda.de/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fimg.laoda.de%2Fi%2F2025%2F08%2F28%2F160319-1.webp&amp;size=m" alt="img"></p> <p>就可以在这边调出使用了。</p> <p>当然,这只是最简单的功能,dify的魅力在于<strong>降低AI应用开发的门槛,快速构建和部署生产级的生成式AI应用</strong></p> <p>它通过以下核心功能来实现这个目标:</p> <ol> <li><strong>简化开发流程</strong>:Dify是一个开源的大语言模型(LLM)应用开发平台,融合了后端即服务(BaaS)和LLMOps理念,提供可视化的Prompt编排、数据集管理、RAG(检索增强生成)引擎、Agent框架等功能,使开发者无需深入底层技术也能快速搭建AI应用。</li> <li><strong>支持低代码/无代码开发</strong>:通过直观的界面和拖拽操作,非技术人员也能参与AI应用的定义和数据运营,极大地降低了开发门槛。</li> <li><strong>灵活的模型支持</strong>:Dify支持多种主流大语言模型(如GPT、Claude、DeepSeek等)以及开源模型,开发者可根据需求灵活选择和切换模型。</li> <li><strong>高效的RAG和Agent功能</strong>:Dify的RAG技术通过结合外部知识库提升生成内容的准确性,而Agent框架赋予AI自主推理和任务执行能力,适用于复杂场景如智能客服、内容生成等。189.html)</li> <li><strong>企业级应用支持</strong>:Dify提供私有化部署、API集成、多租户隔离等功能,适合企业构建内部AI网关或快速将AI能力融入现有业务,助力AI转型。</li> </ol> <p>总结来说,Dify通过提供一站式、易用的开发平台,帮助开发者、企业甚至非技术人员快速实现AI应用创意,节省开发时间,专注于创新和业务需求,特别适合从原型设计到生产部署的全流程管理。</p> <p>大家可以自行探索一下Dify的高级玩法,我自己也在慢慢摸索学习,之后有机会可以和大家分享!</p>

2025/8/28
阅读更多

支持MCP,轻量又全面的记账软件——ezBookkeeping|好玩儿的Docker项目

<img src="https://blog.laoda.de/plugins/feed/assets/telemetry.gif?title=%E6%94%AF%E6%8C%81MCP%EF%BC%8C%E8%BD%BB%E9%87%8F%E5%8F%88%E5%85%A8%E9%9D%A2%E7%9A%84%E8%AE%B0%E8%B4%A6%E8%BD%AF%E4%BB%B6%E2%80%94%E2%80%94ezBookkeeping%EF%BD%9C%E5%A5%BD%E7%8E%A9%E5%84%BF%E7%9A%84Docker%E9%A1%B9%E7%9B%AE&amp;url=/archives/docker-compose-install-ezbookkeeping" width="1" height="1" alt="" style="opacity:0;"> <p>前阵子去欧洲玩了一段时间,鸽了挺久了,今天有点时间,立马来和大家分享好玩儿的Docker项目。</p> <h2 id="1--介绍">1. 介绍</h2> <p>ezBookkeeping 是一款轻量级的个人理财应用,你可以自己架设(自托管)。它界面简洁,用起来很顺手,记账功能也强大。</p> <p>这款应用的设计核心就是<strong>简单</strong>和<strong>便携</strong>。它部署起来毫不费力,上手快,而且对系统资源的占用极小。无论是微型服务器、NAS 设备,还是树莓派,都能轻松运行。</p> <p>ezBookkeeping 支持全平台,对各种设备都很友好。在手机、平板和电脑上用,体验都一样流畅。它还支持 PWA(渐进式网页应用),你可以直接把它添加到手机主屏幕,像本地应用一样方便地使用。</p> <h3 id="1-1-功能特点">1.1 功能特点</h3> <p><strong>开源与自托管</strong></p> <ul> <li>为你提供隐私和数据控制权</li> </ul> <p><strong>轻量又快速</strong></p> <ul> <li>性能卓越,即使在低配置环境下也能流畅运行</li> </ul> <p><strong>安装简单</strong></p> <ul> <li>支持 Docker 部署</li> <li>兼容 SQLite, MySQL, PostgreSQL 数据库</li> <li>跨平台运行(支持 Windows, macOS, Linux)</li> <li>支持多种处理器架构 (x86, amd64, ARM)</li> </ul> <p><strong>界面友好</strong></p> <ul> <li>界面已针对移动和桌面设备优化</li> <li>支持 PWA,在手机上可获得类似原生应用的体验</li> <li>提供深色模式</li> </ul> <p><strong>AI 赋能</strong></p> <ul> <li>支持 MCP (Model Context Protocol),可集成 AI 功能</li> </ul> <p><strong>强大的记账功能</strong></p> <ul> <li>支持两级账户和分类</li> <li>可为交易添加图片附件</li> <li>具备地图位置追踪功能</li> <li>支持周期性(重复)交易</li> <li>提供高级筛选、搜索、数据可视化和分析功能</li> </ul> <p><strong>本地化与全球化</strong></p> <ul> <li>支持多种语言和货币</li> <li>自动更新汇率</li> <li>支持多时区显示</li> <li>可自定义日期、数字和货币格式</li> </ul> <p><strong>安全保障</strong></p> <ul> <li>支持双重身份验证 (2FA)</li> <li>登录频率限制</li> <li>应用锁定功能(支持 PIN 码 / WebAuthn)</li> </ul> <p><strong>数据导入/导出</strong></p> <ul> <li>支持多种格式,包括 CSV, OFX, QFX, QIF, IIF, Camt.053, MT940, GnuCash, Firefly III, Beancount 等等</li> </ul> <p>简单来说,这是一个记账的项目,然后它还可以支持MCP,可以搭配大语言模型一起食用。</p> <p><img src="https://blog.laoda.de/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fimg.laoda.de%2Fi%2F2025%2F08%2F12%2F203854-1.webp&amp;size=m" alt="fa0b25dbc1ebb8c3e364c5de96542aff.png"></p> <p><img src="https://blog.laoda.de/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fimg.laoda.de%2Fi%2F2025%2F08%2F12%2F203856-1.webp&amp;size=m" alt="6cd6cae5e006175532d59840d5683924.png"></p> <h2 id="2--相关地址">2. 相关地址</h2> <p>官方GitHub地址:<a href="https://github.com/mayswind/ezbookkeeping">https://github.com/mayswind/ezbookkeeping</a> (目前958个star,欢迎大家去给作者点星星!)</p> <p>官方Demo:<a href="https://ezbookkeeping-demo.mayswind.net">https://ezbookkeeping-demo.mayswind.net</a></p> <h2 id="3--搭建环境">3. 搭建环境</h2> <ul> <li>服务器:咕咕这边用的是<a href="https://gao.ee/lca">莱卡云香港服务器</a>,当然你也可以选择其他<a href="https://blog.laoda.de/vps">高性价比的服务器</a>。内存1G以上即可,硬盘当然是越大越好啦。</li> <li>系统:Debian 11 (<a href="https://blog.laoda.de/archives/useful-script/">DD 脚本</a> 非必需 DD,用原来的系统也 OK,之后教程都是用 Debian 或者 Ubuntu 搭建~)</li> <li>安装好 Docker、Docker-compose(<a href="https://blog.laoda.de/archives/hello-docker/">相关脚本</a>)</li> <li>【必需】域名一枚,并做好解析到服务器上(<a href="https://blog.laoda.de/archives/namesilo/">域名购买、域名解析</a> <a href="https://www.bilibili.com/video/BV1Sy4y1k7kZ/">视频教程</a>)</li> <li>【非必需】提前安装好宝塔面板海外版本 aapanel,并安装好 Nginx(<a href="https://forum.aapanel.com/d/9-aapanel-linux-panel-6812-installation-tutorial">安装地址</a>)</li> <li>【非必需本教程选用】安装好 Nginx Proxy Manager(<a href="https://blog.laoda.de/archives/nginxproxymanager/">相关教程</a>)</li> </ul> <h2 id="4--搭建视频-有时间补充--">4. 搭建视频(有时间补充!)</h2> <h3 id="4-1-YouTube">4.1 YouTube</h3> <p>视频地址:</p> <h3 id="4-2-哔哩哔哩">4.2 哔哩哔哩</h3> <p>哔哩哔哩:</p> <h2 id="5--搭建方式">5. 搭建方式</h2> <h3 id="5-1-安装-Docker-与-Nginx-Proxy-Manager">5.1 安装 Docker 与 Nginx Proxy Manager</h3> <p>可以直接参考这篇内容:</p> <p><a href="https://blog.laoda.de/archives/nginxproxymanager/">https://blog.laoda.de/archives/nginxproxymanager/</a></p> <h3 id="5-2-创建安装目录">5.2 创建安装目录</h3> <p>创建一下安装的目录:</p> <pre><code class="language-bash">sudo -i mkdir -p /root/data/docker_data/ezbookkeeping cd ezbookkeeping </code></pre> <p>我们来简单修改一下作者提供的<code>docker-compose.yml</code>文件</p> <pre><code class="language-bash">vim docker-compose.yml </code></pre> <p>咕咕修改之后的:</p> <pre><code class="language-yaml">services: ezbookkeeping: image: mayswind/ezbookkeeping container_name: ezbookkeeping restart: unless-stopped ports: - 8082:8080 volumes: - /etc/localtime:/etc/localtime:ro - ./storage:/ezbookkeeping/storage - ./log:/ezbookkeeping/log # - ./ezbookkeeping.ini:/ezbookkeeping/conf/ezbookkeeping.ini environment: - EBK_DATABASE_TYPE=mysql - EBK_DATABASE_HOST=mysql:3306 - EBK_DATABASE_NAME=ezbookkeeping - EBK_DATABASE_USER=ezbookkeeping - EBK_DATABASE_PASSWD=ezbookkeeping - EBK_LOG_MODE=file - EBK_SECURITY_SECRET_KEY=its_should_be_a_random_string - EBK_MCP_ENABLE_MCP=true # depends_on: # mysql: # condition: service_healthy mysql: image: mysql:8.0 container_name: ezbookkeeping-mysql restart: unless-stopped volumes: - ./data:/var/lib/mysql environment: - MYSQL_DATABASE=ezbookkeeping - MYSQL_USER=ezbookkeeping - MYSQL_PASSWORD=ezbookkeeping - MYSQL_ROOT_PASSWORD=ezbookkeeping healthcheck: test: ["CMD", "mysqladmin", "ping", "-p ezbookkeeping"] retries: 3 timeout: 5s </code></pre> <p>修改完成之后,可以在英文输入法下,按 <code>i</code> 修改,完成之后,按一下 <code>esc</code>,然后 <code>:wq</code> 保存退出。</p> <p>接着创建文件夹,赋予文件权限:</p> <pre><code class="language-bash">cd /root/data/docker_data/ezbookkeeping mkdir ./{data,log,storage} chmod a+rw {log,storage} </code></pre> <h3 id="5-3-打开服务器防火墙-非必需-并访问网页">5.3 打开服务器防火墙(非必需)并访问网页</h3> <p>打开防火墙的端口 <code>8082</code></p> <p>举例,腾讯云打开方法如下(部分服务商没有自带的面板防火墙,就不用这步操作了):</p> <img src="https://blog.laoda.de/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fimg.laoda.de%2Fi%2F2022%2F06%2F30%2Fzlio5h-2.webp&amp;size=m" alt="image-20220630215240864" style="zoom: 25%;"> <img src="https://blog.laoda.de/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fimg.laoda.de%2Fi%2F2022%2F06%2F30%2F10h3dqt-2.webp&amp;size=m" alt="image-20220630220546335" style="zoom: 25%;"> <p>类似图中的,这边我们填 <code>8082</code>,示例填 <code>ezbookkeeping</code> ,确定即可(如果你在 docker-compose 文件里换了 <code>9009</code>,这边就需要填 <code>9009</code>,以此类推)</p> <img src="https://blog.laoda.de/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fimg.laoda.de%2Fi%2F2025%2F07%2F04%2F093305-1.webp&amp;size=m" alt="56a42aff23098af08c1ae587e19739ae.png" style="zoom:33%;"> <p>查看端口是否被占用(以 <code>8082</code> 为例),输入:</p> <pre><code class="language-bash">lsof -i:8082 #查看 8082 端口是否被占用,如果被占用,重新自定义一个端口 </code></pre> <p>如果啥也没出现,表示端口未被占用,我们可以继续下面的操作了~</p> <p>如果出现:</p> <pre><code class="language-bash">-bash: lsof: command not found </code></pre> <p>运行:</p> <pre><code class="language-bash">apt install lsof #安装 lsof </code></pre> <p>如果端口没有被占用(被占用了就修改一下端口,比如改成 <code>8381</code>,注意 docker 命令行里和防火墙都要改)</p> <h3 id="5-4-启动-ezbookkeeping">5.4 启动 ezbookkeeping</h3> <pre><code>cd /root/data/docker_data/ezbookkeeping docker compose up -d # 注意,老版本用户用 docker-compose up -d </code></pre> <p>等待拉取好镜像,出现 <code>done</code>的字样之后,</p> <p>理论上我们就可以输入 <code>http://ip:8082</code> 访问了。</p> <p>但是这边我们推荐先搞一下反向代理!</p> <p>做反向代理前,你需要一个域名!</p> <p><a href="https://loll.cc/ns">namesilo</a> 上面 xyz 后缀的域名一年就 7 块钱,可以年抛。(冷知识,namesilo上 6位数字的xyz续费永远都是0.99美元 = =)</p> <p>如果想要长期使用,还是建议买 com 后缀的域名,更加正规一些,可以输入 <code>laodade</code> 来获得 1 美元的优惠(不知道现在还有没有)</p> <p><a href="https://loll.cc/ns">namesilo</a> 自带隐私保护,咕咕一直在用这家,价格也是这些注册商里面比较低的,关键是他家不像其他家域名注册商,没有七七八八的套路!(就是后台界面有些 <del>丑</del> 古老 = =)</p> <p><a href="https://blog.laoda.de/archives/namesilo/">【域名购买】Namesilo 优惠码和域名解析教程(附带服务器购买推荐和注意事项)</a></p> <p>我们接着往下看!</p> <h2 id="6--反向代理">6. 反向代理</h2> <h3 id="6-1-利用-Nginx-Proxy-Manager">6.1 利用 Nginx Proxy Manager</h3> <p>在添加反向代理之前,确保你已经完成了域名解析,不会的可以看这个:<strong>域名一枚,并做好解析到服务器上</strong>(<a href="https://blog.laoda.de/archives/namesilo/">域名购买、域名解析</a> <a href="https://www.bilibili.com/video/BV1Sy4y1k7kZ/">视频教程</a>)</p> <p><img src="https://blog.laoda.de/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fimg.laoda.de%2Fi%2F2025%2F07%2F04%2F093302-1.webp&amp;size=m" alt="4b00cb7fe121c71233685dcc787fdf32.png"></p> <p>之后,登陆 Nginx Proxy Manager(不会的看这个:<strong>安装 Nginx Proxy Manager</strong>(<a href="https://blog.laoda.de/archives/nginxproxymanager/">相关教程</a>))</p> <blockquote> <p><strong>注意:</strong></p> <p>Nginx Proxy Manager(以下简称 NPM)会用到 <code>80</code>、<code>443</code> 端口,所以本机不能占用(比如原来就有 Nginx)</p> </blockquote> <p>直接丢几张图:</p> <img src="https://blog.laoda.de/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fimg.laoda.de%2Fi%2F2025%2F08%2F12%2F201955-1.webp&amp;size=m" alt="9a07c1b059cf95aae62081a18f2ad5d8.png" style="zoom:33%;"> <img src="https://blog.laoda.de/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fimg.laoda.de%2Fi%2F2025%2F08%2F12%2F201945-1.webp&amp;size=m" alt="c1fa9d7b7a32927f020093975c6fd8ea.png" style="zoom:33%;"> <img src="https://blog.laoda.de/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fimg.laoda.de%2Fi%2F2025%2F08%2F12%2F201941-1.webp&amp;size=m" alt="396509498ad5fd92b67f6a782cade133.png" style="zoom:33%;"> <img src="https://blog.laoda.de/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fimg.laoda.de%2Fi%2F2025%2F08%2F12%2F201935-1.webp&amp;size=m" alt="e5362e20c7d6d271d1e8103b0511ac04.png" style="zoom:33%;"> <blockquote> <p>注意填写对应的 <code>域名</code>、<code>IP</code> 和 <code>端口</code>,按文章来的话,应该是 <code>8082</code></p> </blockquote> <p><strong>IP 填写:</strong></p> <p>如果 Nginx Proxy Manager 和 ezbookkeeping 在同一台服务器上,可以在终端输入:</p> <pre><code class="language-bash">ip addr show docker0 </code></pre> <p>查看对应的 Docker 容器内部 IP。</p> <img src="https://blog.laoda.de/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fimg.laoda.de%2Fi%2F2022%2F09%2F29%2Fpcdnz5-2.webp&amp;size=m" style="zoom:50%;"> <p>否则直接填 <strong>ezbookkeeping 所在的服务器 IP</strong> 就行。</p> <h3 id="6-2-利用宝塔面板">6.2 利用宝塔面板</h3> <p>发现还是有不少小伙伴习惯用宝塔面板,这边也贴一个宝塔面板的反代配置:</p> <p>直接新建一个站点,不要数据库,不要 php,纯静态即可。</p> <p>然后打开下面的配置,修改 Nginx 的配置。</p> <img src="https://blog.laoda.de/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fimg.laoda.de%2Fi%2F2025%2F08%2F12%2F201927-1.webp&amp;size=m" alt="image-20220819150345725" style="zoom: 33%;"> <img src="https://blog.laoda.de/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fimg.laoda.de%2Fi%2F2022%2F08%2F19%2Fowbbkb-2.webp&amp;size=m" alt="image-20220819150542867" style="zoom: 33%;"> <p>代码如下:</p> <pre><code class="language-nginx">location / { proxy_pass http://127.0.0.1:8082/; # 注意改成你实际使用的端口 rewrite ^/(.*)$ /$1 break; proxy_redirect off; proxy_set_header Host $host; proxy_set_header X-Forwarded-Proto $scheme; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header Upgrade-Insecure-Requests 1; proxy_set_header X-Forwarded-Proto https; } </code></pre> <p>此方法对 90% 的反向代理都能生效,然后就可以用域名来安装访问了。</p> <p>有同学可能会问,为什么不直接用宝塔自带的反向代理功能。</p> <img src="https://blog.laoda.de/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fimg.laoda.de%2Fi%2F2025%2F07%2F04%2F093233-1.webp&amp;size=m" alt="image-20220819150730128" style="zoom: 33%;"> <p>也可以,不过咕咕自己之前遇到过当有多个网站需要反代的时候,在这边设置会报错的情况 = =</p> <p>所以后来就不用了,直接用上面的方法来操作了。</p> <h2 id="7--使用教程">7. 使用教程</h2> <p>登录你的域名之后,可以看到首页:</p> <p><img src="https://blog.laoda.de/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fimg.laoda.de%2Fi%2F2025%2F08%2F12%2F201919-1.webp&amp;size=m" alt="7a03c1ab0e2dd10aa34612cba853e3ed.png"></p> <p>这个时候需要创建一个新账号:</p> <p><img src="https://blog.laoda.de/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fimg.laoda.de%2Fi%2F2025%2F08%2F12%2F201916-1.webp&amp;size=m" alt="826b774725884ccd0ba5d7bb5758c241.png"></p> <p><img src="https://blog.laoda.de/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fimg.laoda.de%2Fi%2F2025%2F08%2F12%2F201914-1.webp&amp;size=m" alt="39679f5841b71f65fb9f2133518e24c5.png"></p> <p>这里的预设分类,可以选择打开:</p> <p><img src="https://blog.laoda.de/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fimg.laoda.de%2Fi%2F2025%2F08%2F12%2F201911-1.webp&amp;size=m" alt="5f1aacee44cf4f3e03d077634743e047.png"></p> <p>进入后台,结构很清晰:</p> <p><img src="https://blog.laoda.de/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fimg.laoda.de%2Fi%2F2025%2F08%2F12%2F201909-1.webp&amp;size=m" alt="694dcf2d58bfaadaa204d13269549e9d.png"></p> <p>我们先来添加一下账户:</p> <p><img src="https://blog.laoda.de/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fimg.laoda.de%2Fi%2F2025%2F08%2F12%2F201907-1.webp&amp;size=m" alt="3cefdb5bf5493e24f16be3df8c491cfe.png"></p> <p>比如,取名就叫做“咕咕的账本”:</p> <img src="https://blog.laoda.de/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fimg.laoda.de%2Fi%2F2025%2F08%2F12%2F201904-1.webp&amp;size=m" alt="b9869d80991a30d1f4e46bf18999a778.png" style="zoom:50%;"> <p>先来手动添加一笔试试看:</p> <p><img src="https://blog.laoda.de/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fimg.laoda.de%2Fi%2F2025%2F08%2F12%2F201859-1.webp&amp;size=m" alt="f3b17b3c3f41f5eb0e4f5521bbf1d214.png"></p> <p>可以正常添加:</p> <p><img src="https://blog.laoda.de/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fimg.laoda.de%2Fi%2F2025%2F08%2F12%2F201857-1.webp&amp;size=m" alt="e9f717d2e4a0fd9ef38bb95eb89a79bb.png"></p> <h3 id="MCP配置">MCP配置</h3> <p>一开始我们就说了,这个账本支持MCP。</p> <p>MCP(模型上下文协议)是由 Anthropic 开发的一种开放协议,它能让 AI 模型安全地连接到外部数据源和和工具。</p> <p>简单来说,MCP 就像一座桥梁,它提供了一种通用的标准,让 AI 工具能安全地获取信息和执行操作,同时确保数据安全和用户可以掌控一切。</p> <p>有了 MCP 协议,你就可以用自己喜欢的 AI 工具来:</p> <ul> <li><strong>添加交易记录</strong>:比如,直接用自然语言(就是日常说话的方式)来创建交易,或者从不同的文件格式中批量导入交易记录。</li> <li><strong>查询交易数据</strong>:比如,让你的 AI 工具帮你分析历史交易记录。</li> <li><strong>以及做更多的事情</strong>。</li> </ul> <p>更多MCP相关介绍可以看这边:https://ezbookkeeping.mayswind.net/mcp</p> <p>这边我们来看看怎么配置。</p> <p>首先找到右上角的用户设置,</p> <img src="https://blog.laoda.de/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fimg.laoda.de%2Fi%2F2025%2F08%2F12%2F201854-1.webp&amp;size=m" alt="e66ee48541f398f0c84c40b839e68388.png" style="zoom:33%;"> <p>生成MCP令牌:</p> <p>有两种形式,分别是普通的令牌和json配置:</p> <p><img src="https://blog.laoda.de/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fimg.laoda.de%2Fi%2F2025%2F08%2F12%2F201850-1.webp&amp;size=m" alt="7bcc4a9580e9e7d3c1fa1a74d0401b90.png"></p> <img src="https://blog.laoda.de/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fimg.laoda.de%2Fi%2F2025%2F08%2F12%2F201848-1.webp&amp;size=m" alt="ab7a8343aff185cf6ab7475b925f0ea4.png" style="zoom:50%;"> <p>普通:</p> <pre><code class="language-bash">eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyVG9rZW5JZCI6IjMzOTQ1NTM3NTIyOTE5OTc4MzYiLCJqdGkiOiIzNzY4ODA2Njg4MjQ1NjbWUiOiJSb3kiLCJ0eXBlIjo1LCJpYXQiOjE3NTQ5ODc3NzcsImV4cCI6MTA5NzgzNTk4MTR9.7JL_4kzKhihatpfQxIkhD6Z_ibZ_vBYtcQ_D9UCV--0 </code></pre> <p>JSON配置:</p> <img src="https://blog.laoda.de/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fimg.laoda.de%2Fi%2F2025%2F08%2F12%2F201843-1.webp&amp;size=m" alt="c5dfb5b8dfc24e1ca0e2303fece2cde2.png" style="zoom:50%;"> <pre><code class="language-json">{ "mcpServers": { "ezbookkeeping-mcp": { "type": "streamable-http", "url": "http://localhost:8080/mcp", "headers": { "Authorization": "Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyVG9rZW5JZCI6IjMzOTQ1NTM3NTIyOTE5OTc4MzYiLCJqdGkiOiIzNzY4ODA2Njg4hbWUiOiJSb3kiLCJ0eXBlIjo1LCJpYXQiOjE3NTQ5ODc3NzcsImV4cCI6MTA5NzgzNTk4MTR9.7JL_4kzKhihatpfQxIkhD6Z_ibZ_vBYtcQ_D9UCV--0" } } } } </code></pre> <p>这边可以保存下来,下次打开就看不到了。</p> <p><img src="https://blog.laoda.de/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fimg.laoda.de%2Fi%2F2025%2F08%2F12%2F201836-1.webp&amp;size=m" alt="29a8936f32b3247acb4510626a85c87b.png"></p> <h3 id="Cherry-Studio配置">Cherry Studio配置</h3> <p>这边以Cherry Studio为例,看看MCP怎么配置。</p> <p><img src="https://blog.laoda.de/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fimg.laoda.de%2Fi%2F2025%2F08%2F12%2F201834-1.webp&amp;size=m" alt="706dbf1ee98c6368863418e1a638da1c.png"></p> <p>配置情况如图:</p> <p><img src="https://blog.laoda.de/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fimg.laoda.de%2Fi%2F2025%2F08%2F12%2F201831-1.webp&amp;size=m" alt="57f3580946e4072cba358958444ff08f.png"></p> <ul> <li><code>名称</code>:主要用于识别,例如:<code>ezBookkeeping</code></li> <li><code>类型</code>:选择 <code>streamableHttp</code> 协议</li> <li><code>URL</code>:为 <code>你的域名/mcp</code></li> <li><code>请求头</code>: 为 <code>Authorization=Bearer {token}</code></li> </ul> <p>接着可以查看一下可以调用的工具:</p> <p><img src="https://blog.laoda.de/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fimg.laoda.de%2Fi%2F2025%2F08%2F12%2F201829-1.webp&amp;size=m" alt="c24978aa0964d06246f059399f92ee88.png"></p> <p>然后为了确保大模型能正确理解什么是今天,还需要加个时间MCP,这次我们选择json导入,</p> <pre><code class="language-json">{ "mcpServers": { "mcp-server-time": { "command": "uvx", "args": ["mcp-server-time"] } } } </code></pre> <p><img src="https://blog.laoda.de/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fimg.laoda.de%2Fi%2F2025%2F08%2F12%2F201826-1.webp&amp;size=m" alt="3a3ab3900b6dba588cbcdf54b8f47245.png"></p> <p><img src="https://blog.laoda.de/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fimg.laoda.de%2Fi%2F2025%2F08%2F12%2F201824-1.webp&amp;size=m" alt="ca6653fe3ec6dfe069da60592b40b369.png"></p> <p>这下,你应该有两个MCP服务了。</p> <p><img src="https://blog.laoda.de/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fimg.laoda.de%2Fi%2F2025%2F08%2F12%2F201822-1.webp&amp;size=m" alt="8b12dc8711e6936375bb1d880271102a.png"></p> <p>查看一下时间工具的功能:</p> <p><img src="https://blog.laoda.de/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fimg.laoda.de%2Fi%2F2025%2F08%2F12%2F201820-1.webp&amp;size=m" alt="369abc91ad148e042f9e796c8580312e.png"></p> <p>接着,我们到Cherry Studio里新建一个助手,</p> <p><img src="https://blog.laoda.de/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fimg.laoda.de%2Fi%2F2025%2F08%2F12%2F201817-1.webp&amp;size=m" alt="399ab6071f945797e0fb61349d00cd16.png"></p> <p>选择启用这两个MCP服务:</p> <p><img src="https://blog.laoda.de/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fimg.laoda.de%2Fi%2F2025%2F08%2F12%2F201816-1.webp&amp;size=m" alt="4775185163eb58be74204ea756d6b915.png"></p> <p>测试一下,可以看到回答的时候,调用了MCP服务,</p> <p><img src="https://blog.laoda.de/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fimg.laoda.de%2Fi%2F2025%2F08%2F12%2F201813-1.webp&amp;size=m" alt="b65c160f578c98b0f083b0add6a749bb.png"></p> <p>这边提示错误,我没有说记录到哪个账本:</p> <p><img src="https://blog.laoda.de/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fimg.laoda.de%2Fi%2F2025%2F08%2F12%2F201810-1.webp&amp;size=m" alt="4f1fc419fafb5fc9f55764aca9b20b5d.png"></p> <p><img src="https://blog.laoda.de/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fimg.laoda.de%2Fi%2F2025%2F08%2F12%2F201809-1.webp&amp;size=m" alt="900832a98c968a38f541aff971a5b140.png"></p> <p>还需要说明交易类别,</p> <p><img src="https://blog.laoda.de/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fimg.laoda.de%2Fi%2F2025%2F08%2F12%2F201807-1.webp&amp;size=m" alt="6cc43ba6b7f0df94f756c836bc1b5de4.png"></p> <p>后续,</p> <p><img src="https://blog.laoda.de/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fimg.laoda.de%2Fi%2F2025%2F08%2F12%2F201805-1.webp&amp;size=m" alt="fca2836dc485e298003061c20dd71ec3.png"></p> <p>记录成功!</p> <p><img src="https://blog.laoda.de/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fimg.laoda.de%2Fi%2F2025%2F08%2F12%2F201802-1.webp&amp;size=m" alt="756e050ca91dfb4f2a1f5b3020baa75c.png"></p> <p>不过这次似乎没有调用时间MCP,时间似乎有问题……</p> <p>然后我尝试使用Deepseek,直接不调用MCP服务了,很奇怪。</p> <p><img src="https://blog.laoda.de/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fimg.laoda.de%2Fi%2F2025%2F08%2F12%2F201756-1.webp&amp;size=m" alt="8c18d8491e297ce50cc941024a31a3c2.png"></p> <p>后续一直没调用MCP服务,暂时还没解决,我的是Mac,有解决的小伙伴可以评论区分享一下。</p> <h3 id="7-1-更新-ezbookkeeping">7.1 更新 ezbookkeeping</h3> <pre><code class="language-bash">cd /root/data/docker_data/ezbookkeeping docker compose pull docker compose up -d # 请不要使用 docker compose stop 来停止容器,因为这么做需要额外的时间等待容器停止;docker compose up -d 直接升级容器时会自动停止并立刻重建新的容器,完全没有必要浪费那些时间。 docker image prune # prune 命令用来删除不再使用的 docker 对象。删除所有未被 tag 标记和未被容器使用的镜像 </code></pre> <p>提示:</p> <pre><code class="language-bash">WARNING! This will remove all dangling images. Are you sure you want to continue? [y/N] </code></pre> <p>输入 <code>y</code></p> <p>利用 Docker 搭建的应用,更新非常容易~</p> <h3 id="7-2-卸载-ezbookkeeping">7.2 卸载 ezbookkeeping</h3> <p>同样进入安装页面,先停止所有容器。</p> <pre><code class="language-bash">cd /root/data/docker_data/ezbookkeeping docker compose down cd .. rm -rf /root/data/docker_data/ezbookkeeping # 完全删除 </code></pre> <p>可以卸载得很干净。</p> <h2 id="8--常见问题及注意点">8. 常见问题及注意点</h2> <p>还是MCP调用的问题,不是非常稳定,暂时不确定是不是Cherry Studio的问题。</p> <h2 id="9--结尾">9. 结尾</h2> <p>祝大家用得开心,有问题可以去 GitHub 提 <a href="https://github.com/mayswind/ezbookkeeping/issues">Issues</a>,也可以在评论区互相交流探讨。</p> <p>同时,项目处于初期,有能力给项目做贡献的同学,也欢迎积极加入到 [项目]https://github.com/mayswind/ezbookkeeping) 中来,贡献自己的一份力量!</p> <p>最后,感谢开发人员们的辛苦付出,让我们能用到这么优秀的项目!也希望开源项目越来越好!</p> <h2 id="10--参考资料">10. 参考资料</h2> <p>官方GitHub:<a href="https://github.com/mayswind/ezbookkeeping">https://github.com/mayswind/ezbookkeeping</a></p>

2025/8/12
阅读更多

拒绝恶意收购,十分钟搭建AList的替代品——OpenList|好玩儿的Docker项目

<img src="https://blog.laoda.de/plugins/feed/assets/telemetry.gif?title=%E6%8B%92%E7%BB%9D%E6%81%B6%E6%84%8F%E6%94%B6%E8%B4%AD%EF%BC%8C%E5%8D%81%E5%88%86%E9%92%9F%E6%90%AD%E5%BB%BAAList%E7%9A%84%E6%9B%BF%E4%BB%A3%E5%93%81%E2%80%94%E2%80%94OpenList%EF%BD%9C%E5%A5%BD%E7%8E%A9%E5%84%BF%E7%9A%84Docker%E9%A1%B9%E7%9B%AE&amp;url=/archives/docker-compose-install-openlist" width="1" height="1" alt="" style="opacity:0;"> <p>最近事情比较多,鸽了好一阵子了。前阵子Alist被收购的事情,相信不少小伙伴可能已经有了解。</p> <p>这边给还不清楚的小伙伴简单梳理一下。</p> <h2 id="1--介绍">1. 介绍</h2> <p>AList是一个广受欢迎的开源项目,支持多种云存储的统一管理和WebDAV功能,因其便捷性和强大的网盘聚合能力在开发者社区中积累了近5万星标。</p> <p>咕咕在自己的博客里也介绍过这个项目——<a href="https://blog.laoda.de/archives/docker-install-alist">【好玩的Docker项目】目前最好用的网盘直链程序——AList,支持市面上几乎所有网盘!可以代替Olaindex!</a> (突然发现已经是三年前的事情了 = =)</p> <p>然而,2025年6月爆出的项目被出售事件引发了开源社区的激烈反响。</p> <h3 id="事件背景">事件背景</h3> <ul> <li><strong>项目出售</strong>:据多方消息,AList被原开发者Xhofe出售给贵州不够科技有限公司(BugoTech),交易过程未公开,缺乏社区通知。原开发者确认项目已交由公司运营,自己仅保留代码审查权,main分支启用PR审核和CI自动构建以确保透明性。</li> <li><strong>争议操作</strong>:新维护者(账号alist666)提交的PR #8633被发现包含收集用户设备信息并上传至私有服务器的代码,引发“供应链投毒”担忧,虽因社区压力撤回,但信任危机加剧。文档被大幅修改,移除原作者信息,新增商业化内容(如VIP技术支持和QQ群),官网域名从alist.nn.ci更换为alistgo.com,旧链接404。</li> <li><strong>收购方背景</strong>:不够科技此前收购Hutool、LNMP等项目,伴随权限争议和疑似“投毒”历史,社区对其声誉存疑。</li> </ul> <h3 id="社区反应">社区反应</h3> <ol> <li> <p><strong>愤怒与失望</strong>:</p> <ul> <li><strong>信任崩塌</strong>:社区对原开发者Xhofe未提前公告的“暗箱操作”表示强烈不满,认为此举背叛了开源精神,伤害了用户和贡献者的信任。GitHub Issues区被大量批评淹没,新维护者删除质疑帖并移除反对者进一步激化矛盾。</li> <li><strong>隐私担忧</strong>:AList涉及网盘Token和Cookie等敏感数据,用户担心新版本可能泄露隐私或被植入恶意代码。社区建议锁定v3.40.0版本,解除网盘授权,并备份重要文件。</li> <li><strong>对收购方的质疑</strong>:不够科技的“黑历史”被广泛讨论,社区将其与此前LNMP、Oneinstack的闭源和投毒争议联系起来,称其为“傻逼公司”“bug科技”,对其动机和代码安全性高度警惕。</li> </ul> </li> <li> <p><strong>社区自救与分叉</strong>:</p> <ul> <li><strong>分叉项目</strong>:开发者迅速行动,创建了OpenList等分叉项目,移除不可信代码并审计近半年提交,短时间内获数千星标,显示社区对替代方案的热情。</li> <li><strong>替代方案</strong>:技术博客推荐Zdir、Cloudreve等私有网盘程序,鼓励用户迁移以规避风险。</li> <li><strong>安全建议</strong>:社区提出锁定版本、校验哈希、隔离网络、自行编译等措施,强调去中心化分发和社区监督的重要性。</li> </ul> </li> <li> <p><strong>对原开发者的复杂情绪</strong>:</p> <ul> <li><strong>理解与支持</strong>:部分开发者(如@DIYgod)表示理解Xhofe的决定,认为开源维护的长期孤独和无回报让变现成为合理选择,祝福其获得回报。贡献图显示Xhofe个人提交超90%代码,凸显其付出。</li> <li><strong>批评与指责</strong>:更多用户认为Xhofe将社区贡献和用户数据“卖给有前科的公司”是不道德的,称其“把用户当交易品”,损害了开源生态的信任。</li> </ul> </li> </ol> <h3 id="评价">评价</h3> <ul> <li><strong>对原开发者的行为</strong>:Xhofe作为主要贡献者有权在MIT/AGPL-3.0许可证框架内转让项目,但未公开透明的做法违背了开源社区的信任契约。 虽然其后续承诺代码审查,但“悄然退出”和“配合移除贡献者痕迹”让社区感到被背叛,凸显了单一开发者主导项目的风险。</li> <li><strong>对收购方的操作</strong>:不够科技的历史记录和新代码的隐私收集行为引发合理担忧,其商业化导向(如VIP服务)与开源精神冲突。删除批评和接管仓库的“粗暴”操作进一步破坏信任,可能导致AList用户流失。</li> <li><strong>社区的应对</strong>:社区的激烈反应反映了对开源项目透明度和数据安全的重视。分叉项目和替代方案的迅速涌现展现了开源生态的韧性,但也暴露了国内开源项目变现难、社区监督机制不足的问题。</li> <li><strong>对开源生态的启示</strong>:AList事件敲响警钟,提醒用户关注项目维护的可持续性和开发者的稳定性。建议建立贡献者许可协议(CLA)和社区监督机制,防止类似“野蛮收购”。用户应谨慎选择涉及敏感数据的开源工具,优先考虑社区主导的项目。</li> </ul> <h3 id="结论">结论</h3> <p>AList被收购事件引发了开源社区的信任危机,社区的愤怒源于对隐私安全和开源精神的双重担忧。尽管部分人理解原开发者的变现动机,但其隐秘操作和收购方的争议历史让事件成为“开源之耻”。社区通过分叉和替代方案展现了自救能力,但事件也暴露了国内开源生态在商业化与信任之间的矛盾。用户应保持警惕,优先选择透明、社区驱动的项目,同时推动更完善的开源治理机制。</p> <p>这一期,我们就来分享一个Alist的开源替代——OpenList。</p> <p><img src="https://blog.laoda.de/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fimg.laoda.de%2Fi%2F2025%2F07%2F04%2F093316-1.webp&amp;size=m" alt="ecd1fc88db4311c5f021b48eea89155e.png"></p> <p><img src="https://blog.laoda.de/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fimg.laoda.de%2Fi%2F2025%2F07%2F04%2F093313-1.webp&amp;size=m" alt="b052fa406d61edce52e385f01a60fddf.png"></p> <blockquote> <p>如果你之前安装过 Alist,其实只要在<code>docker-compose.yml</code>修改两处:</p> <ul> <li>镜像名从 <code>xhofe/alist</code> 改为 <code>openlistteam/openlist</code></li> <li>映射的容器内的路径,由 <code>/opt/alist/</code> 改为 <code>/opt/openlist/</code>(如果添加了本地存储,记得也修改一下根文件夹路径,其他网盘不影响)</li> </ul> <p>运行之前,最好对原来的数据进行一次备份,运行之后,原来怎么使用,现在还是一样的。</p> </blockquote> <h2 id="2--相关地址">2. 相关地址</h2> <p>官方GitHub地址:<a href="https://github.com/OpenListTeam/OpenList">https://github.com/OpenListTeam/OpenList</a> (目前10.6k个star,欢迎大家去给作者点星星!)</p> <h2 id="3--搭建环境">3. 搭建环境</h2> <ul> <li>服务器:咕咕这边用的是OVH的杜甫,当然你也可以选择其他<a href="https://blog.laoda.de/vps">高性价比的服务器</a>。内存1G以上即可,硬盘当然是越大越好啦。</li> <li>系统:Debian 11 (<a href="https://blog.laoda.de/archives/useful-script/">DD 脚本</a> 非必需 DD,用原来的系统也 OK,之后教程都是用 Debian 或者 Ubuntu 搭建~)</li> <li>安装好 Docker、Docker-compose(<a href="https://blog.laoda.de/archives/hello-docker/">相关脚本</a>)</li> <li>【必需】域名一枚,并做好解析到服务器上(<a href="https://blog.laoda.de/archives/namesilo/">域名购买、域名解析</a> <a href="https://www.bilibili.com/video/BV1Sy4y1k7kZ/">视频教程</a>)</li> <li>【非必需】提前安装好宝塔面板海外版本 aapanel,并安装好 Nginx(<a href="https://forum.aapanel.com/d/9-aapanel-linux-panel-6812-installation-tutorial">安装地址</a>)</li> <li>【非必需本教程选用】安装好 Nginx Proxy Manager(<a href="https://blog.laoda.de/archives/nginxproxymanager/">相关教程</a>)</li> </ul> <h2 id="4--搭建视频-有时间补充--">4. 搭建视频(有时间补充!)</h2> <h3 id="4-1-YouTube">4.1 YouTube</h3> <p>视频地址:</p> <h3 id="4-2-哔哩哔哩">4.2 哔哩哔哩</h3> <p>哔哩哔哩:</p> <h2 id="5--搭建方式">5. 搭建方式</h2> <h3 id="5-1-安装-Docker-与-Nginx-Proxy-Manager">5.1 安装 Docker 与 Nginx Proxy Manager</h3> <p>可以直接参考这篇内容:</p> <p><a href="https://blog.laoda.de/archives/nginxproxymanager/">https://blog.laoda.de/archives/nginxproxymanager/</a></p> <h3 id="5-2-创建安装目录">5.2 创建安装目录</h3> <p>创建一下安装的目录:</p> <pre><code class="language-bash">sudo -i mkdir -p /root/data/docker_data/openlist cd openlist </code></pre> <p>我们来简单修改一下作者提供的<code>docker-compose.yml</code>文件</p> <pre><code class="language-bash">vim docker-compose.yml </code></pre> <p>咕咕修改之后的(其实就是修改了端口号和删除了<code>version: "3.7"</code>):</p> <pre><code class="language-yaml">services: openlist: image: 'openlistteam/openlist:latest' container_name: openlist volumes: - '/data:/opt/openlist/data' - './share:/opt/openlist/share' ports: - '5288:5244' environment: - PUID=0 - PGID=0 - UMASK=022 restart: unless-stopped </code></pre> <p>修改完成之后,可以在英文输入法下,按 <code>i</code> 修改,完成之后,按一下 <code>esc</code>,然后 <code>:wq</code> 保存退出。</p> <h3 id="5-3-打开服务器防火墙-非必需-并访问网页">5.3 打开服务器防火墙(非必需)并访问网页</h3> <p>打开防火墙的端口 <code>5288</code></p> <p>举例,腾讯云打开方法如下(部分服务商没有自带的面板防火墙,就不用这步操作了):</p> <img src="https://blog.laoda.de/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fimg.laoda.de%2Fi%2F2022%2F06%2F30%2Fzlio5h-2.webp&amp;size=m" alt="image-20220630215240864" style="zoom: 25%;"> <img src="https://blog.laoda.de/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fimg.laoda.de%2Fi%2F2022%2F06%2F30%2F10h3dqt-2.webp&amp;size=m" alt="image-20220630220546335" style="zoom: 25%;"> <p>类似图中的,这边我们填 <code>5288</code>,示例填 <code>openlist</code> ,确定即可(如果你在 docker-compose 文件里换了 <code>9009</code>,这边就需要填 <code>9009</code>,以此类推)</p> <img src="https://blog.laoda.de/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fimg.laoda.de%2Fi%2F2025%2F07%2F04%2F093305-1.webp&amp;size=m" alt="56a42aff23098af08c1ae587e19739ae.png" style="zoom:33%;"> <p>查看端口是否被占用(以 <code>5288</code> 为例),输入:</p> <pre><code class="language-bash">lsof -i:5288 #查看 5288 端口是否被占用,如果被占用,重新自定义一个端口 </code></pre> <p>如果啥也没出现,表示端口未被占用,我们可以继续下面的操作了~</p> <p>如果出现:</p> <pre><code class="language-bash">-bash: lsof: command not found </code></pre> <p>运行:</p> <pre><code class="language-bash">apt install lsof #安装 lsof </code></pre> <p>如果端口没有被占用(被占用了就修改一下端口,比如改成 <code>8381</code>,注意 docker 命令行里和防火墙都要改)</p> <h3 id="5-4-启动-openlist">5.4 启动 openlist</h3> <pre><code>cd /root/data/docker_data/openlist docker compose up -d # 注意,老版本用户用 docker-compose up -d </code></pre> <p>等待拉取好镜像,出现 <code>done</code>的字样之后,</p> <p>理论上我们就可以输入 <code>http://ip:5288</code> 访问了。</p> <p>但是这边我们推荐先搞一下反向代理!</p> <p>做反向代理前,你需要一个域名!</p> <p><a href="https://loll.cc/ns">namesilo</a> 上面 xyz 后缀的域名一年就 7 块钱,可以年抛。(冷知识,namesilo上 6位数字的xyz续费永远都是0.99美元 = =)</p> <p>如果想要长期使用,还是建议买 com 后缀的域名,更加正规一些,可以输入 <code>laodade</code> 来获得 1 美元的优惠(不知道现在还有没有)</p> <p><a href="https://loll.cc/ns">namesilo</a> 自带隐私保护,咕咕一直在用这家,价格也是这些注册商里面比较低的,关键是他家不像其他家域名注册商,没有七七八八的套路!(就是后台界面有些 <del>丑</del> 古老 = =)</p> <p><a href="https://blog.laoda.de/archives/namesilo/">【域名购买】Namesilo 优惠码和域名解析教程(附带服务器购买推荐和注意事项)</a></p> <p>我们接着往下看!</p> <h2 id="6--反向代理">6. 反向代理</h2> <h3 id="6-1-利用-Nginx-Proxy-Manager">6.1 利用 Nginx Proxy Manager</h3> <p>在添加反向代理之前,确保你已经完成了域名解析,不会的可以看这个:<strong>域名一枚,并做好解析到服务器上</strong>(<a href="https://blog.laoda.de/archives/namesilo/">域名购买、域名解析</a> <a href="https://www.bilibili.com/video/BV1Sy4y1k7kZ/">视频教程</a>)</p> <p><img src="https://blog.laoda.de/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fimg.laoda.de%2Fi%2F2025%2F07%2F04%2F093302-1.webp&amp;size=m" alt="4b00cb7fe121c71233685dcc787fdf32.png"></p> <p>之后,登陆 Nginx Proxy Manager(不会的看这个:<strong>安装 Nginx Proxy Manager</strong>(<a href="https://blog.laoda.de/archives/nginxproxymanager/">相关教程</a>))</p> <blockquote> <p><strong>注意:</strong></p> <p>Nginx Proxy Manager(以下简称 NPM)会用到 <code>80</code>、<code>443</code> 端口,所以本机不能占用(比如原来就有 Nginx)</p> </blockquote> <p>直接丢几张图:</p> <img src="https://blog.laoda.de/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fimg.laoda.de%2Fi%2F2025%2F07%2F04%2F093259-1.webp&amp;size=m" alt="1a1ff8255bb0ae59f7dcb9b492fa3f11.png" style="zoom:50%;"> <img src="https://blog.laoda.de/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fimg.laoda.de%2Fi%2F2025%2F07%2F04%2F093253-1.webp&amp;size=m" alt="e412c3244963f658bd1521eca54de921.png" style="zoom:50%;"> <img src="https://blog.laoda.de/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fimg.laoda.de%2Fi%2F2025%2F07%2F04%2F093249-1.webp&amp;size=m" alt="3f2b568516e72dab959680f84f40a83e.png" style="zoom:50%;"> <img src="https://blog.laoda.de/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fimg.laoda.de%2Fi%2F2025%2F07%2F04%2F093245-1.webp&amp;size=m" alt="3c9ea7731f53d6e51b7cf6cbe1c3c532.png" style="zoom:50%;"> <blockquote> <p>注意填写对应的 <code>域名</code>、<code>IP</code> 和 <code>端口</code>,按文章来的话,应该是 <code>5288</code></p> </blockquote> <p><strong>IP 填写:</strong></p> <p>如果 Nginx Proxy Manager 和 openlist 在同一台服务器上,可以在终端输入:</p> <pre><code class="language-bash">ip addr show docker0 </code></pre> <p>查看对应的 Docker 容器内部 IP。</p> <img src="https://blog.laoda.de/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fimg.laoda.de%2Fi%2F2022%2F09%2F29%2Fpcdnz5-2.webp&amp;size=m" style="zoom:50%;"> <p>否则直接填 <strong>openlist 所在的服务器 IP</strong> 就行。</p> <h3 id="6-2-利用宝塔面板">6.2 利用宝塔面板</h3> <p>发现还是有不少小伙伴习惯用宝塔面板,这边也贴一个宝塔面板的反代配置:</p> <p>直接新建一个站点,不要数据库,不要 php,纯静态即可。</p> <p>然后打开下面的配置,修改 Nginx 的配置。</p> <img src="https://blog.laoda.de/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fimg.laoda.de%2Fi%2F2022%2F08%2F19%2Fov4xrt-2.webp&amp;size=m" alt="image-20220819150345725" style="zoom: 33%;"> <img src="https://blog.laoda.de/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fimg.laoda.de%2Fi%2F2022%2F08%2F19%2Fowbbkb-2.webp&amp;size=m" alt="image-20220819150542867" style="zoom: 33%;"> <p>代码如下:</p> <pre><code class="language-nginx">location / { proxy_pass http://127.0.0.1:5288/; # 注意改成你实际使用的端口 rewrite ^/(.*)$ /$1 break; proxy_redirect off; proxy_set_header Host $host; proxy_set_header X-Forwarded-Proto $scheme; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header Upgrade-Insecure-Requests 1; proxy_set_header X-Forwarded-Proto https; } </code></pre> <p>此方法对 90% 的反向代理都能生效,然后就可以用域名来安装访问了。</p> <p>有同学可能会问,为什么不直接用宝塔自带的反向代理功能。</p> <img src="https://blog.laoda.de/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fimg.laoda.de%2Fi%2F2025%2F07%2F04%2F093233-1.webp&amp;size=m" alt="image-20220819150730128" style="zoom: 33%;"> <p>也可以,不过咕咕自己之前遇到过当有多个网站需要反代的时候,在这边设置会报错的情况 = =</p> <p>所以后来就不用了,直接用上面的方法来操作了。</p> <h2 id="7--使用教程">7. 使用教程</h2> <p>首先来输入命令:</p> <pre><code class="language-bash">docker compose logs </code></pre> <p>查看一下初始的登录密码:</p> <p><img src="https://blog.laoda.de/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fimg.laoda.de%2Fi%2F2025%2F07%2F04%2F093229-1.webp&amp;size=m" alt="3813385afd8b3e7c33c374f95c642d7b.png"></p> <p>这边的密码是<code>OVoPTw5B</code></p> <p>打开域名登录:</p> <p><img src="https://blog.laoda.de/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fimg.laoda.de%2Fi%2F2025%2F07%2F04%2F093226-1.webp&amp;size=m" alt="1ed1f3aeb85b7de9289c25f921a40340.png"></p> <p>默认用户名:<code>admin</code> <br> 密码:<code>OVoPTw5B</code></p> <p>这个等下都可以自己修改。</p> <p>登录之后发现是这样的,因为我们还没有添加任何的存储源:</p> <p><img src="https://blog.laoda.de/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fimg.laoda.de%2Fi%2F2025%2F07%2F04%2F093224-1.webp&amp;size=m" alt="b569358ee1fefe1b457c6c173dbf1ce7.png"></p> <p>点击首页下方的管理,来到后台。</p> <p>我们来演示一下"添加存储"的操作,</p> <p><img src="https://blog.laoda.de/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fimg.laoda.de%2Fi%2F2025%2F07%2F04%2F093221-1.webp&amp;size=m" alt="eed7a56a23a3f2fa0269ee851714737d.png"></p> <p>这边先选择一个"本地存储",</p> <p>挂载路径这边可以自己自定义,</p> <p>WebDAV策略选择“本地代理”就行,</p> <p><img src="https://blog.laoda.de/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fimg.laoda.de%2Fi%2F2025%2F07%2F04%2F093219-1.webp&amp;size=m" alt="919e9cc5a6bab65898daa321583a3e65.png"></p> <p>根文件夹路径要填docker容器内部的路径,这边前面我们在<code>docker-compose.yml</code>里面映射了<code>./share:/opt/openlist/share</code>,所以其实这边填<code>/opt/openlist/share</code>即可。</p> <p>其他的选项按需自己设置。</p> <p><img src="https://blog.laoda.de/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fimg.laoda.de%2Fi%2F2025%2F07%2F04%2F093211-1.webp&amp;size=m" alt="699adef16557fd910c5120f59f020c84.png"></p> <p>点击保存之后,再返回首页,就有显示了,后续可以自己把文件传到服务器,这边就会显示出来了。</p> <p><img src="https://blog.laoda.de/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fimg.laoda.de%2Fi%2F2025%2F07%2F04%2F093208-1.webp&amp;size=m" alt="293e180d47b86f783314960b790c9935.png"></p> <p>当然,AList之所以火爆,就是因为他可以添加很多个网盘的索引,</p> <p><img src="https://blog.laoda.de/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fimg.laoda.de%2Fi%2F2025%2F07%2F04%2F093206-1.webp&amp;size=m" alt="78a73028105f168bc635347f0a2265eb.png"></p> <p><img src="https://blog.laoda.de/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fimg.laoda.de%2Fi%2F2025%2F07%2F04%2F093205-1.webp&amp;size=m" alt="0dce80aeb7a0f61fe8c8a0105e26c227.png"></p> <p>因为不同的小伙伴用的网盘也不一样,具体的细节,大家可以参考官方的文档来添加:<a href="https://docs.oplist.org/zh/guide/drivers/common.html">https://docs.oplist.org/zh/guide/drivers/common.html</a></p> <h3 id="7-1-更新-openlist">7.1 更新 openlist</h3> <pre><code class="language-yaml">cd /root/data/docker_data/openlist docker compose pull docker compose up -d # 请不要使用 docker compose stop 来停止容器,因为这么做需要额外的时间等待容器停止;docker compose up -d 直接升级容器时会自动停止并立刻重建新的容器,完全没有必要浪费那些时间。 docker image prune # prune 命令用来删除不再使用的 docker 对象。删除所有未被 tag 标记和未被容器使用的镜像 </code></pre> <p>提示:</p> <pre><code class="language-bash">WARNING! This will remove all dangling images. Are you sure you want to continue? [y/N] </code></pre> <p>输入 <code>y</code></p> <p>利用 Docker 搭建的应用,更新非常容易~</p> <h3 id="7-2-卸载-openlist">7.2 卸载 openlist</h3> <p>同样进入安装页面,先停止所有容器。</p> <pre><code class="language-bash">cd /root/data/docker_data/openlist docker compose down cd .. rm -rf /root/data/docker_data/openlist # 完全删除 </code></pre> <p>可以卸载得很干净。</p> <h2 id="8--常见问题及注意点">8. 常见问题及注意点</h2> <p>目前 OpenList 已经发布了 4.0 正式版,但依然存在一些已知问题,不过大部分功能已可稳定使用,具体迁移进度可在 <a href="https://github.com/OpenListTeam/OpenList/issues/6">OpenList 迁移工作总结</a> 中查看。</p> <p>大家记得多多更新,使用最新版本的docker镜像。</p> <p><img src="https://blog.laoda.de/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fimg.laoda.de%2Fi%2F2025%2F07%2F04%2F093202-1.webp&amp;size=m" alt="a17367ce1aa891a163371be6f72c8b9f.png"></p> <p>目前项目的赞助页面还未建立:<a href="https://docs.oplist.org/zh/zh/guide/sponsor.html">https://docs.oplist.org/zh/zh/guide/sponsor.html</a></p> <p>后续建立了我也会支持,同时也希望有条件的小伙伴多多支持,赞赏开发者们一杯咖啡。这样也能让开源项目能够健康生存下去,少受商业的裹挟。</p> <h2 id="9--结尾">9. 结尾</h2> <p>祝大家用得开心,有问题可以去 GitHub 提 <a href="https://github.com/OpenListTeam/OpenList/issues">Issues</a>,也可以在评论区互相交流探讨。</p> <p>同时,项目处于初期,有能力给项目做贡献的同学,也欢迎积极加入到 [项目]https://github.com/OpenListTeam/OpenList) 中来,贡献自己的一份力量!</p> <p>最后,感谢开发人员们的辛苦付出,让我们能用到这么优秀的项目!也希望开源项目越来越好!</p> <h2 id="10--参考资料">10. 参考资料</h2> <p>官方GitHub:<a href="https://github.com/OpenListTeam/OpenList">https://github.com/OpenListTeam/OpenList</a></p>

2025/7/4
阅读更多

保护图片隐私!十分钟搭建一个小而美的离线图片处理工具——mazanoke|好玩儿的Docker项目

<img src="https://blog.laoda.de/plugins/feed/assets/telemetry.gif?title=%E4%BF%9D%E6%8A%A4%E5%9B%BE%E7%89%87%E9%9A%90%E7%A7%81%EF%BC%81%E5%8D%81%E5%88%86%E9%92%9F%E6%90%AD%E5%BB%BA%E4%B8%80%E4%B8%AA%E5%B0%8F%E8%80%8C%E7%BE%8E%E7%9A%84%E7%A6%BB%E7%BA%BF%E5%9B%BE%E7%89%87%E5%A4%84%E7%90%86%E5%B7%A5%E5%85%B7%E2%80%94%E2%80%94mazanoke%EF%BD%9C%E5%A5%BD%E7%8E%A9%E5%84%BF%E7%9A%84Docker%E9%A1%B9%E7%9B%AE&amp;url=/archives/docker-compose-install-mazanoke" width="1" height="1" alt="" style="opacity:0;"> <h2 id="1--唠嗑">1. 唠嗑</h2> <p>之前和大家分享过:<a href="https://blog.laoda.de/archives/docker-compose-install-reubah">一个本地化图片处理工具——Reubah</a></p> <p>今天我们再来分享一个UI更简洁漂亮的支持docker自建的本地图像处理工具——mazanoke</p> <h2 id="2--mazanoke简介">2. mazanoke简介</h2> <p><img src="https://blog.laoda.de/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fimg.laoda.de%2Fi%2F2025%2F05%2F09%2F105022-1.webp&amp;size=m" alt="1199394abe5998fca4e955a6de88e657.png"></p> <p>MAZANOKE 是一个简单的图像优化工具,可以在你的浏览器中运行,离线工作,并且始终保持你的图像私密,是市面上那些宣传“免费”在线图片处理工具的替代品。</p> <h3 id="2-1-功能">2.1 功能</h3> <h4 id="---在浏览器中优化图像">🖼️ 在浏览器中优化图像</h4> <ul> <li>调整图像质量</li> <li>设置目标文件大小</li> <li>设置最大宽度/高度</li> <li>从剪贴板粘贴图像</li> <li>在 JPG、PNG、WebP 之间转换</li> <li>从 HEIC、AVIF、GIF、SVG 转换</li> </ul> <h4 id="--注重隐私">🔒 注重隐私</h4> <ul> <li>离线工作</li> <li>设备内处理图像</li> <li>删除 EXIF 数据(位置、日期等)</li> <li>无追踪</li> <li>可安装的网络应用程序</li> </ul> <h4 id="计划中的功能">计划中的功能</h4> <ul> <li>一次上传多个文件</li> <li>支持更多图像文件类型 <ul> <li>最近添加了从:HEIC、AVIF、GIF、SVG → JPG/PNG/WebP 的转换</li> </ul></li> <li>记住上次使用的设置</li> <li>裁剪图片</li> </ul> <h3 id="2-2-使用">2.2 使用</h3> <p>官方Demo地址:<a href="https://mazanoke.com/">MAZANOKE.com</a></p> <p><img src="https://blog.laoda.de/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fimg.laoda.de%2Fi%2F2025%2F05%2F09%2F105032-1.webp&amp;size=m" alt="cc6769191e8b166df0677018f4d76af8.png"></p> <p><img src="https://blog.laoda.de/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fimg.laoda.de%2Fi%2F2025%2F05%2F09%2F105036-1.webp&amp;size=m" alt="29a023ebea96d7dcb6b64e30898cf42a.png"></p> <p>所见即所得,不多赘述。</p> <p>其实,这个本质就是一个html页面,你可以直接点击右上角下载,这样就能离线使用了。</p> <p><img src="https://blog.laoda.de/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fimg.laoda.de%2Fi%2F2025%2F05%2F09%2F105110-1_1.webp&amp;size=m" alt="85e4bd6fbe3b94f1eb19ed78fcaa0eb8.png"></p> <p><img src="https://blog.laoda.de/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fimg.laoda.de%2Fi%2F2025%2F05%2F09%2F105110-1.webp&amp;size=m" alt="45cc17bffdd5d8d699576a19c0cfd328.png"></p> <p>或者是在这边下载:<a href="https://github.com/civilblur/mazanoke/releases">https://github.com/civilblur/mazanoke/releases</a></p> <p>然后打开里面的<code>index.html</code>也可以直接离线使用。</p> <p>当然如果你看到这边还想自己用docker搭建一个,那我们就继续往下!</p> <h2 id="3--相关地址">3. 相关地址</h2> <p>官方GitHub地址:<a href="https://github.com/civilblur/mazanoke">https://github.com/civilblur/mazanoke</a> (目前1K个star,欢迎大家去给项目点星星!)</p> <h2 id="4--搭建环境">4. 搭建环境</h2> <ul> <li>服务器:咕咕这边用的香港的<a href="http://gao.ee/lca">莱卡云服务器</a>,建议服务器内存1G以上,当然你也可以选择其他<a href="https://blog.laoda.de/vps">高性价比的服务器</a>。</li> <li>系统:Debian 11 (<a href="https://blog.laoda.de/archives/useful-script/">DD 脚本</a> 非必需 DD,用原来的系统也 OK,之后教程都是用 Debian 或者 Ubuntu 搭建~)</li> <li>安装好 Docker、Docker-compose(<a href="https://blog.laoda.de/archives/hello-docker/">相关脚本</a>)</li> <li>【必需】域名一枚,并做好解析到服务器上(<a href="https://blog.laoda.de/archives/namesilo/">域名购买、域名解析</a> <a href="https://www.bilibili.com/video/BV1Sy4y1k7kZ/">视频教程</a>)</li> <li>【非必需】提前安装好宝塔面板海外版本 aapanel,并安装好 Nginx(<a href="https://forum.aapanel.com/d/9-aapanel-linux-panel-6812-installation-tutorial">安装地址</a>)</li> <li>【非必需本教程选用】安装好 Nginx Proxy Manager(<a href="https://blog.laoda.de/archives/nginxproxymanager/">相关教程</a>)</li> </ul> <h2 id="5--搭建视频-过俩天补充-----">5. 搭建视频(过俩天补充 = =)</h2> <h3 id="5-1-YouTube">5.1 YouTube</h3> <p>视频地址:</p> <h3 id="5-2-哔哩哔哩">5.2 哔哩哔哩</h3> <p>哔哩哔哩:</p> <h2 id="6--搭建方式">6. 搭建方式</h2> <h3 id="6-1-安装-Docker-与-Nginx-Proxy-Manager">6.1 安装 Docker 与 Nginx Proxy Manager</h3> <p>可以直接参考这篇内容:</p> <p><a href="https://blog.laoda.de/archives/nginxproxymanager/">https://blog.laoda.de/archives/nginxproxymanager/</a></p> <h3 id="6-2-创建安装目录">6.2 创建安装目录</h3> <p>创建一下安装的目录:</p> <pre><code class="language-bash">sudo -i mkdir -p /root/data/docker_data/mazanoke cd /root/data/docker_data/mazanoke </code></pre> <p>接着我们来编辑下<code>docker-compose.yml</code></p> <pre><code class="language-bash">vim docker-compose.yml </code></pre> <pre><code class="language-yaml"># Basic compose file for deploying MAZANOKE services: mazanoke: container_name: mazanoke image: ghcr.io/civilblur/mazanoke:latest ports: - "3474:80" restart: unless-stopped </code></pre> <p>其中的左边的<code>3474</code>可以改成服务器上没有用过的端口,记得修改自己的用户名和密码,修改完成之后,可以在英文输入法下,按 <code>i</code> 修改,完成之后,按一下 <code>esc</code>,然后 <code>:wq</code> 保存退出。</p> <h3 id="6-3-查看端口是否被占用">6.3 查看端口是否被占用</h3> <p>查看端口是否被占用(以 <code>3474</code> 为例),输入:</p> <pre><code class="language-bash">lsof -i:3474 #查看 3474 端口是否被占用,如果被占用,重新自定义一个端口 </code></pre> <p>如果啥也没出现,表示端口未被占用,我们可以继续下面的操作了~</p> <p>如果出现:</p> <pre><code class="language-bash">-bash: lsof: command not found </code></pre> <p>运行:</p> <pre><code class="language-bash">apt install lsof #安装 lsof </code></pre> <p>如果端口没有被占用(被占用了就修改一下端口,比如改成 <code>8381</code>,注意 docker 命令行里和防火墙都要改)</p> <h3 id="6-4-启动-mazanoke">6.4 启动 mazanoke</h3> <pre><code>cd /root/data/docker_data/mazanoke docker compose up -d # 注意,老版本用户用 docker-compose up -d </code></pre> <p>耐心等待拉取好镜像,出现 <code>done</code>的字样之后,</p> <p>理论上我们就可以输入 <code>http://ip:3474</code> 访问了。</p> <p>但是这边这个服务必须先搞一下反向代理!http使用明文传输太危险,我们部署在公网一定要考虑使用反向代理工具配置SSL!</p> <p>做反向代理前,你需要一个域名!</p> <p><a href="https://loll.cc/ns">namesilo</a> 上面 xyz 后缀的域名一年就 7 块钱,可以年抛。(冷知识,namesilo上 6位数字的xyz续费永远都是0.99美元 = =)</p> <p>如果想要长期使用,还是建议买 com 后缀的域名,更加正规一些,可以输入 <code>laodade</code> 来获得 1 美元的优惠(不知道现在还有没有)</p> <p><a href="https://loll.cc/ns">namesilo</a> 自带隐私保护,咕咕一直在用这家,价格也是这些注册商里面比较低的,关键是他家不像其他家域名注册商,没有七七八八的套路!(就是后台界面有些 <del>丑</del> 古老 = =)</p> <p><a href="https://blog.laoda.de/archives/namesilo/">【域名购买】Namesilo 优惠码和域名解析教程(附带服务器购买推荐和注意事项)</a></p> <p>我们接着往下看!</p> <h2 id="7--反向代理">7. 反向代理</h2> <h3 id="7-1-利用-Nginx-Proxy-Manager">7.1 利用 Nginx Proxy Manager</h3> <p>在添加反向代理之前,确保你已经完成了域名解析,不会的可以看这个:<strong>域名一枚,并做好解析到服务器上</strong>(<a href="https://blog.laoda.de/archives/namesilo/">域名购买、域名解析</a> <a href="https://www.bilibili.com/video/BV1Sy4y1k7kZ/">视频教程</a>) (名称改成你自己想要的域名前缀即可)</p> <p><img src="https://blog.laoda.de/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fimg.laoda.de%2Fi%2F2025%2F05%2F09%2F105106-1.webp&amp;size=m" alt="dbbf5fa382d005f7f413603b4c0238f9.png"></p> <p>之后,登陆 Nginx Proxy Manager(不会的看这个:<strong>安装 Nginx Proxy Manager</strong>(<a href="https://blog.laoda.de/archives/nginxproxymanager/">相关教程</a>))</p> <blockquote> <p><strong>注意:</strong></p> <p>Nginx Proxy Manager(以下简称 NPM)会用到 <code>80</code>、<code>443</code> 端口,所以本机不能占用(比如原来就有 Nginx)</p> </blockquote> <p>直接丢几张图:</p> <img src="https://blog.laoda.de/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fimg.laoda.de%2Fi%2F2025%2F05%2F09%2F105056-1.webp&amp;size=m" alt="546316e8d575d6f70533e1e09eb412cd.png" style="zoom:50%;"> <img src="https://blog.laoda.de/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fimg.laoda.de%2Fi%2F2025%2F04%2F16%2F205949-1.webp&amp;size=m" alt="c2075b8e6a85bff585396fd4c0bca1ed.png" style="zoom:50%;"> <img src="https://blog.laoda.de/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fimg.laoda.de%2Fi%2F2025%2F05%2F09%2F105051-1.webp&amp;size=m" alt="6cc81ae5550620c5aaf8444ab60ac58e.png" style="zoom:50%;"> <img src="https://blog.laoda.de/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fimg.laoda.de%2Fi%2F2025%2F04%2F16%2F205935-1.webp&amp;size=m" alt="f5137b06b270769cc2ebca7019f47e35.png" style="zoom:50%;"> <blockquote> <p>注意填写对应的 <code>域名</code>、<code>IP</code> 和 <code>端口</code>,按文章来的话,应该是 <code>3474</code></p> </blockquote> <p><strong>IP 填写:</strong></p> <p>如果 Nginx Proxy Manager 和 mazanoke 在同一台服务器上,可以在终端输入:</p> <pre><code class="language-bash">ip addr show docker0 </code></pre> <p>查看对应的 Docker 容器内部 IP。</p> <img src="https://blog.laoda.de/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fimg.laoda.de%2Fi%2F2024%2F12%2F12%2F153819-1.webp&amp;size=m" style="zoom:50%;"> <p>否则直接填 mazanoke 所在的服务器 IP 就行。</p> <p>然后访问域名就可以访问了!</p> <h3 id="7-2-利用宝塔面板">7.2 利用宝塔面板</h3> <p>发现还是有不少小伙伴习惯用宝塔面板,这边也贴一个宝塔面板的反代配置:</p> <p>直接新建一个站点,不要数据库,不要 php,纯静态即可。</p> <p>然后打开下面的配置,修改 Nginx 的配置。</p> <img src="https://blog.laoda.de/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fimg.laoda.de%2Fi%2F2024%2F12%2F12%2F153816-1.webp&amp;size=m" alt="image-20220819150345725" style="zoom: 33%;"> <img src="https://blog.laoda.de/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fimg.laoda.de%2Fi%2F2024%2F12%2F12%2F153814-1.webp&amp;size=m" alt="image-20220819150542867" style="zoom: 33%;"> <p>代码如下:</p> <pre><code class="language-nginx">location / { proxy_pass http://127.0.0.1:3474/; # 注意改成你实际使用的端口 rewrite ^/(.*)$ /$1 break; proxy_redirect off; proxy_set_header Host $host; proxy_set_header X-Forwarded-Proto $scheme; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header Upgrade-Insecure-Requests 1; proxy_set_header X-Forwarded-Proto https; } </code></pre> <p>此方法对 90% 的反向代理都能生效,然后就可以用域名来安装访问了。</p> <p>有同学可能会问,为什么不直接用宝塔自带的反向代理功能。</p> <img src="https://blog.laoda.de/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fimg.laoda.de%2Fi%2F2024%2F12%2F12%2F153804-1.webp&amp;size=m" alt="image-20220819150730128" style="zoom: 33%;"> <p>也可以,不过咕咕自己之前遇到过当有多个网站需要反代的时候,在这边设置会报错的情况 = =</p> <p>所以后来就不用了,直接用上面的方法来操作了。</p> <h2 id="8--使用教程">8. 使用教程</h2> <p>非常简单,所见即所得。</p> <h3 id="8-1-更新-mazanoke">8.1 更新 mazanoke</h3> <p>这个项目后续应该也会有更新,所以提供一个更新的方式。</p> <pre><code class="language-yaml">cd /root/data/docker_data/mazanoke docker compose pull docker compose up -d # 请不要使用 docker compose stop 来停止容器,因为这么做需要额外的时间等待容器停止;docker compose up -d 直接升级容器时会自动停止并立刻重建新的容器,完全没有必要浪费那些时间。 docker image prune # prune 命令用来删除不再使用的 docker 对象。删除所有未被 tag 标记和未被容器使用的镜像 </code></pre> <p>提示:</p> <pre><code class="language-bash">WARNING! This will remove all dangling images. Are you sure you want to continue? [y/N] </code></pre> <p>输入 <code>y</code></p> <p>利用 Docker 搭建的应用,更新非常容易~</p> <h3 id="8-2-卸载-mazanoke">8.2 卸载 mazanoke</h3> <p>同样进入安装页面,先停止所有容器。</p> <pre><code class="language-bash">cd /root/data/docker_data/mazanoke docker compose down cd .. rm -rf /root/data/docker_data/mazanoke # 完全删除 </code></pre> <p>可以卸载得很干净。</p> <h2 id="9--常见问题及注意点">9. 常见问题及注意点</h2> <p>大家有问题欢迎评论区交流。</p> <h2 id="10--结尾">10. 结尾</h2> <p>祝大家用得开心,有问题可以去 GitHub 提 <a href="https://github.com/civilblur/mazanoke/issues">Issues</a>,也可以在评论区互相交流探讨。</p> <p>同时,有能力给项目做贡献的同学,也欢迎积极加入到 <a href="https://github.com/civilblur/mazanoke/mazanoke">项目</a> 中来,贡献自己的一份力量!</p> <p>最后,感谢作者<a href="https://github.com/civilblur/mazanoke">@civilblur</a>的辛苦付出,让我们能用到这么优秀的项目!欢迎大家都去给这个项目点个 star ⭐️</p> <h2 id="参考资料">参考资料</h2> <p>官方GitHub:<a href="https://github.com/civilblur/mazanoke">https://github.com/civilblur/mazanoke</a></p>

2025/5/9
阅读更多

10分钟搭建一个高颜值的在线开发工具箱—— jisuxiang|好玩儿的Docker项目

<img src="https://blog.laoda.de/plugins/feed/assets/telemetry.gif?title=10%E5%88%86%E9%92%9F%E6%90%AD%E5%BB%BA%E4%B8%80%E4%B8%AA%E9%AB%98%E9%A2%9C%E5%80%BC%E7%9A%84%E5%9C%A8%E7%BA%BF%E5%BC%80%E5%8F%91%E5%B7%A5%E5%85%B7%E7%AE%B1%E2%80%94%E2%80%94%20jisuxiang%EF%BD%9C%E5%A5%BD%E7%8E%A9%E5%84%BF%E7%9A%84Docker%E9%A1%B9%E7%9B%AE&amp;url=/archives/docker-compose-install-jisuxiang" width="1" height="1" alt="" style="opacity:0;"> <h2 id="1--唠嗑">1. 唠嗑</h2> <p>之前和大家分享过<a href="https://blog.laoda.de/archives/docker-compose-install-it-tools">【好玩儿的Docker项目】10分钟搭建一个高颜值的在线工具箱,用户体验很棒!</a>,</p> <p><img src="https://blog.laoda.de/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fimg.laoda.de%2Fi%2F2025%2F04%2F22%2F162922-1.webp&amp;size=m" alt="6bae1f25866c4f1b60943721f69796c1.png"></p> <p>功能很丰富,不过不支持中文。</p> <p>这一期来介绍一个中文的,高颜值的在线开发工具箱。</p> <h2 id="2--jisuxiang简介">2. jisuxiang简介</h2> <p>极速箱是一个高颜值的在线开发工具箱,提供各种程序员必备的开发工具,帮助开发者提升编程效率。本项目使用 Next.js 和 TailwindCSS 开发,拥有美观现代的界面设计。</p> <p>在线演示: <a href="https://www.jisuxiang.com/">https://www.jisuxiang.com</a> - 立即体验!</p> <h3 id="2-1---功能特点">2.1 🚀 功能特点</h3> <ul> <li><strong>多种分类工具</strong>:JSON处理、编码解码、网络测试等多种实用工具</li> <li><strong>响应式设计</strong>:适配各种设备尺寸,提供最佳用户体验</li> <li><strong>暗色主题</strong>:保护眼睛的界面设计,适合长时间使用</li> <li><strong>高效搜索</strong>:快速找到所需工具的强大搜索功能</li> <li><strong>收藏系统</strong>:保存常用工具,方便快速访问</li> <li><strong>多语言支持</strong>:支持中文和英文界面</li> </ul> <h3 id="2-2---包含工具">2.2 🔧 包含工具</h3> <ul> <li>JSON格式化与验证</li> <li>HTTP请求测试</li> <li>时间戳转换</li> <li>编码解码工具</li> <li>正则表达式测试</li> <li>加密解密工具</li> <li>颜色选择与转换</li> <li>代码格式化</li> <li>JSON编辑器与转换器</li> <li>IP地址查询</li> <li>日期计算器</li> <li>时区转换</li> <li>文本统计</li> <li>HTML/Markdown转换</li> <li>图片压缩</li> <li>二维码生成</li> <li>CSS渐变生成器</li> <li>更多实用工具...</li> </ul> <h3 id="2-3-使用">2.3 使用</h3> <p><img src="https://blog.laoda.de/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fimg.laoda.de%2Fi%2F2025%2F04%2F22%2F162919-1.webp&amp;size=m" alt="c86398679044dcdd3df5487fe7c0d560.png"></p> <p><img src="https://blog.laoda.de/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fimg.laoda.de%2Fi%2F2025%2F04%2F22%2F162918-1.webp&amp;size=m" alt="338c4db9517abac9b37cdc226113f745.png"> <br> 当然如果你看到这边想自己搭建一个,那我们就继续往下看!</p> <h2 id="3--相关地址">3. 相关地址</h2> <p>官方GitHub地址:<a href="https://github.com/star7th/jisuxiang">https://github.com/star7th/jisuxiang</a> (目前261个star,欢迎大家去给项目点星星!)</p> <h2 id="4--搭建环境">4. 搭建环境</h2> <ul> <li>服务器:咕咕这边用的香港的<a href="https://loll.cc/tx">腾讯云轻量应用服务器</a>,建议服务器内存1G以上,当然你也可以选择其他<a href="https://blog.laoda.de/vps">高性价比的服务器</a>。</li> <li>系统:Debian 11 (<a href="https://blog.laoda.de/archives/useful-script/">DD 脚本</a> 非必需 DD,用原来的系统也 OK,之后教程都是用 Debian 或者 Ubuntu 搭建~)</li> <li>安装好 Docker、Docker-compose(<a href="https://blog.laoda.de/archives/hello-docker/">相关脚本</a>)</li> <li>【必需】域名一枚,并做好解析到服务器上(<a href="https://blog.laoda.de/archives/namesilo/">域名购买、域名解析</a> <a href="https://www.bilibili.com/video/BV1Sy4y1k7kZ/">视频教程</a>)</li> <li>【非必需】提前安装好宝塔面板海外版本 aapanel,并安装好 Nginx(<a href="https://forum.aapanel.com/d/9-aapanel-linux-panel-6812-installation-tutorial">安装地址</a>)</li> <li>【非必需本教程选用】安装好 Nginx Proxy Manager(<a href="https://blog.laoda.de/archives/nginxproxymanager/">相关教程</a>)</li> </ul> <h2 id="5--搭建视频-过俩天补充-----">5. 搭建视频(过俩天补充 = =)</h2> <h3 id="5-1-YouTube">5.1 YouTube</h3> <p>视频地址:</p> <h3 id="5-2-哔哩哔哩">5.2 哔哩哔哩</h3> <p>哔哩哔哩:</p> <h2 id="6--搭建方式">6. 搭建方式</h2> <h3 id="6-1-安装-Docker-与-Nginx-Proxy-Manager">6.1 安装 Docker 与 Nginx Proxy Manager</h3> <p>可以直接参考这篇内容:</p> <p><a href="https://blog.laoda.de/archives/nginxproxymanager/">https://blog.laoda.de/archives/nginxproxymanager/</a></p> <h3 id="6-2-创建安装目录">6.2 创建安装目录</h3> <p>创建一下安装的目录:</p> <pre><code class="language-bash">sudo -i mkdir -p /root/data/docker_data/jisuxiang cd /root/data/docker_data/jisuxiang </code></pre> <p>接着我们来编辑下<code>docker-compose.yml</code></p> <pre><code class="language-bash">vim docker-compose.yml </code></pre> <pre><code class="language-yaml">name: jisuxiang services: jisuxiang: container_name: jisuxiang restart: always ports: - 3001:3000 image: star7th/jisuxiang:latest </code></pre> <p>其中的左边的<code>3001</code>可以改成服务器上没有用过的端口,记得修改自己的用户名和密码,修改完成之后,可以在英文输入法下,按 <code>i</code> 修改,完成之后,按一下 <code>esc</code>,然后 <code>:wq</code> 保存退出。</p> <h3 id="6-3-查看端口是否被占用">6.3 查看端口是否被占用</h3> <p>查看端口是否被占用(以 <code>3001</code> 为例),输入:</p> <pre><code class="language-bash">lsof -i:3001 #查看 3001 端口是否被占用,如果被占用,重新自定义一个端口 </code></pre> <p>如果啥也没出现,表示端口未被占用,我们可以继续下面的操作了~</p> <p>如果出现:</p> <pre><code class="language-bash">-bash: lsof: command not found </code></pre> <p>运行:</p> <pre><code class="language-bash">apt install lsof #安装 lsof </code></pre> <p>如果端口没有被占用(被占用了就修改一下端口,比如改成 <code>8381</code>,注意 docker 命令行里和防火墙都要改)</p> <h3 id="6-4-启动-jisuxiang">6.4 启动 jisuxiang</h3> <pre><code>cd /root/data/docker_data/jisuxiang docker compose up -d # 注意,老版本用户用 docker-compose up -d </code></pre> <p>耐心等待拉取好镜像,出现 <code>done</code>的字样之后,</p> <p>理论上我们就可以输入 <code>http://ip:3001</code> 访问了。</p> <p>但是这边这个服务必须先搞一下反向代理!http使用明文传输太危险,我们部署在公网一定要考虑使用反向代理工具配置SSL!</p> <p>做反向代理前,你需要一个域名!</p> <p><a href="https://loll.cc/ns">namesilo</a> 上面 xyz 后缀的域名一年就 7 块钱,可以年抛。(冷知识,namesilo上 6位数字的xyz续费永远都是0.99美元 = =)</p> <p>如果想要长期使用,还是建议买 com 后缀的域名,更加正规一些,可以输入 <code>laodade</code> 来获得 1 美元的优惠(不知道现在还有没有)</p> <p><a href="https://loll.cc/ns">namesilo</a> 自带隐私保护,咕咕一直在用这家,价格也是这些注册商里面比较低的,关键是他家不像其他家域名注册商,没有七七八八的套路!(就是后台界面有些 <del>丑</del> 古老 = =)</p> <p><a href="https://blog.laoda.de/archives/namesilo/">【域名购买】Namesilo 优惠码和域名解析教程(附带服务器购买推荐和注意事项)</a></p> <p>我们接着往下看!</p> <h2 id="7--反向代理">7. 反向代理</h2> <h3 id="7-1-利用-Nginx-Proxy-Manager">7.1 利用 Nginx Proxy Manager</h3> <p>在添加反向代理之前,确保你已经完成了域名解析,不会的可以看这个:<strong>域名一枚,并做好解析到服务器上</strong>(<a href="https://blog.laoda.de/archives/namesilo/">域名购买、域名解析</a> <a href="https://www.bilibili.com/video/BV1Sy4y1k7kZ/">视频教程</a>) (名称改成你自己想要的域名前缀即可)</p> <p><img src="https://blog.laoda.de/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fimg.laoda.de%2Fi%2F2025%2F04%2F22%2F163004-1.webp&amp;size=m" alt="image-20250422163001796"></p> <p>之后,登陆 Nginx Proxy Manager(不会的看这个:<strong>安装 Nginx Proxy Manager</strong>(<a href="https://blog.laoda.de/archives/nginxproxymanager/">相关教程</a>))</p> <blockquote> <p><strong>注意:</strong></p> <p>Nginx Proxy Manager(以下简称 NPM)会用到 <code>80</code>、<code>443</code> 端口,所以本机不能占用(比如原来就有 Nginx)</p> </blockquote> <p>直接丢几张图:</p> <img src="https://blog.laoda.de/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fimg.laoda.de%2Fi%2F2025%2F04%2F22%2F162853-1.webp&amp;size=m" alt="9a1e1688e15e1fa88303c4df48fdb85b.png" style="zoom:50%;"> <img src="https://blog.laoda.de/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fimg.laoda.de%2Fi%2F2025%2F04%2F22%2F162851-1.webp&amp;size=m" alt="f49627216cee515fab28d95eac05b6f9.png" style="zoom:50%;"> <img src="https://blog.laoda.de/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fimg.laoda.de%2Fi%2F2025%2F04%2F22%2F162849-1.webp&amp;size=m" alt="9da4948e39b0777b72d7785d76b26cd6.png" style="zoom:50%;"> <img src="https://blog.laoda.de/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fimg.laoda.de%2Fi%2F2025%2F04%2F22%2F162841-1.webp&amp;size=m" alt="c26132063961e0de1048995075192bca.png" style="zoom:50%;"> <blockquote> <p>注意填写对应的 <code>域名</code>、<code>IP</code> 和 <code>端口</code>,按文章来的话,应该是 <code>3001</code></p> </blockquote> <p><strong>IP 填写:</strong></p> <p>如果 Nginx Proxy Manager 和 jisuxiang 在同一台服务器上,可以在终端输入:</p> <pre><code class="language-bash">ip addr show docker0 </code></pre> <p>查看对应的 Docker 容器内部 IP。</p> <img src="https://blog.laoda.de/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fimg.laoda.de%2Fi%2F2024%2F12%2F12%2F153819-1.webp&amp;size=m" style="zoom:50%;"> <p>否则直接填 jisuxiang 所在的服务器 IP 就行。</p> <p>然后访问域名就可以访问了!</p> <h3 id="7-2-利用宝塔面板">7.2 利用宝塔面板</h3> <p>发现还是有不少小伙伴习惯用宝塔面板,这边也贴一个宝塔面板的反代配置:</p> <p>直接新建一个站点,不要数据库,不要 php,纯静态即可。</p> <p>然后打开下面的配置,修改 Nginx 的配置。</p> <img src="https://blog.laoda.de/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fimg.laoda.de%2Fi%2F2024%2F12%2F12%2F153816-1.webp&amp;size=m" alt="image-20220819150345725" style="zoom: 33%;"> <img src="https://blog.laoda.de/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fimg.laoda.de%2Fi%2F2024%2F12%2F12%2F153814-1.webp&amp;size=m" alt="image-20220819150542867" style="zoom: 33%;"> <p>代码如下:</p> <pre><code class="language-nginx">location / { proxy_pass http://127.0.0.1:3001/; # 注意改成你实际使用的端口 rewrite ^/(.*)$ /$1 break; proxy_redirect off; proxy_set_header Host $host; proxy_set_header X-Forwarded-Proto $scheme; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header Upgrade-Insecure-Requests 1; proxy_set_header X-Forwarded-Proto https; } </code></pre> <p>此方法对 90% 的反向代理都能生效,然后就可以用域名来安装访问了。</p> <p>有同学可能会问,为什么不直接用宝塔自带的反向代理功能。</p> <img src="https://blog.laoda.de/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fimg.laoda.de%2Fi%2F2024%2F12%2F12%2F153804-1.webp&amp;size=m" alt="image-20220819150730128" style="zoom: 33%;"> <p>也可以,不过咕咕自己之前遇到过当有多个网站需要反代的时候,在这边设置会报错的情况 = =</p> <p>所以后来就不用了,直接用上面的方法来操作了。</p> <h2 id="8--使用教程">8. 使用教程</h2> <p>非常简单,开箱即用。</p> <h3 id="8-1-更新-jisuxiang">8.1 更新 jisuxiang</h3> <p>这个项目后续应该也会有更新,所以提供一个更新的方式。</p> <pre><code class="language-yaml">cd /root/data/docker_data/jisuxiang docker compose pull docker compose up -d # 请不要使用 docker compose stop 来停止容器,因为这么做需要额外的时间等待容器停止;docker compose up -d 直接升级容器时会自动停止并立刻重建新的容器,完全没有必要浪费那些时间。 docker image prune # prune 命令用来删除不再使用的 docker 对象。删除所有未被 tag 标记和未被容器使用的镜像 </code></pre> <p>提示:</p> <pre><code class="language-bash">WARNING! This will remove all dangling images. Are you sure you want to continue? [y/N] </code></pre> <p>输入 <code>y</code></p> <p>利用 Docker 搭建的应用,更新非常容易~</p> <h3 id="8-2-卸载-jisuxiang">8.2 卸载 jisuxiang</h3> <p>同样进入安装页面,先停止所有容器。</p> <pre><code class="language-bash">cd /root/data/docker_data/jisuxiang docker compose down cd .. rm -rf /root/data/docker_data/jisuxiang # 完全删除 </code></pre> <p>可以卸载得很干净。</p> <h2 id="9--常见问题及注意点">9. 常见问题及注意点</h2> <p>暂时没有。</p> <h2 id="10--结尾">10. 结尾</h2> <p>祝大家用得开心,有问题可以去 GitHub 提 <a href="https://github.com/star7th/jisuxiang/issues">Issues</a>,也可以在评论区互相交流探讨。</p> <p>同时,有能力给项目做贡献的同学,也欢迎积极加入到 <a href="https://github.com/star7th/jisuxiang">项目</a> 中来,贡献自己的一份力量!</p> <p>最后,感谢作者<a href="https://github.com/star7th/jisuxiang">@star7th</a>的辛苦付出,让我们能用到这么优秀的项目!欢迎大家都去给这个项目点个 star ⭐️</p> <h2 id="参考资料">参考资料</h2> <p>官方GitHub:<a href="https://github.com/star7th/jisuxiang">https://github.com/star7th/jisuxiang</a></p>

2025/4/22
阅读更多

Docker 一键部署网盘资源搜索与转存工具——CloudSaver|好玩儿的Docker项目

<img src="https://blog.laoda.de/plugins/feed/assets/telemetry.gif?title=Docker%20%E4%B8%80%E9%94%AE%E9%83%A8%E7%BD%B2%E7%BD%91%E7%9B%98%E8%B5%84%E6%BA%90%E6%90%9C%E7%B4%A2%E4%B8%8E%E8%BD%AC%E5%AD%98%E5%B7%A5%E5%85%B7%E2%80%94%E2%80%94CloudSaver%EF%BD%9C%E5%A5%BD%E7%8E%A9%E5%84%BF%E7%9A%84Docker%E9%A1%B9%E7%9B%AE&amp;url=/archives/docker-compose-install-cloudsaver" width="1" height="1" alt="" style="opacity:0;"> <h2 id="1--唠嗑">1. 唠嗑</h2> <p>最近事情比较多,很长时间没和大家分享好玩儿的Docker项目了,今天来分享一个网盘资源搜索与转存工具——CloudSaver</p> <h2 id="2--cloudsaver简介">2. cloudsaver简介</h2> <ul> <li>🔍 多源资源搜索 <ul> <li>支持多个资源订阅源搜索</li> <li>支持关键词搜索与资源链接解析</li> <li>支持豆瓣热门榜单展示</li> </ul></li> <li>💾 网盘资源转存 <ul> <li>支持 <code>115 网盘</code>,<code>夸克网盘</code>,<code>天翼网盘</code> 一键转存</li> <li>支持转存文件夹展示与选择</li> </ul></li> <li>👥 多用户系统 <ul> <li>支持用户注册登录</li> <li>支持管理员与普通用户权限区分</li> </ul></li> <li>📱 响应式设计 <ul> <li>支持 <code>PC</code> 端与移动端自适应布局</li> <li>针对不同设备优化的交互体验</li> </ul></li> </ul> <h2 id="2-1-特别声明">2.1 特别声明</h2> <ol> <li>本项目仅供学习交流使用,请勿用于非法用途</li> <li>仅支持个人使用,不支持任何形式的商业使用</li> <li>禁止在项目页面进行任何形式的广告宣传</li> <li>所有搜索到的资源均来自第三方,本项目不对其真实性、合法性做出任何保证</li> </ol> <h2 id="2-2-使用">2.2 使用</h2> <p>热门榜单</p> <p><img src="https://blog.laoda.de/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fimg.laoda.de%2Fi%2F2025%2F04%2F16%2F210035-1.webp&amp;size=m" alt="e8f72be3818444f667dca127dc190bdf.png"></p> <p>资源搜索</p> <p><img src="https://blog.laoda.de/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fimg.laoda.de%2Fi%2F2025%2F04%2F16%2F210031-1.webp&amp;size=m" alt="8fe62b57d3302defa6ba13cfcd6de7df.png"></p> <p>比如搜一个“甄嬛传”</p> <p><img src="https://blog.laoda.de/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fimg.laoda.de%2Fi%2F2025%2F04%2F16%2F210028-1.webp&amp;size=m" alt="9da44b07d7a5766171322a01b8e89ffc.png"></p> <p><img src="https://blog.laoda.de/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fimg.laoda.de%2Fi%2F2025%2F04%2F16%2F210024-1.webp&amp;size=m" alt="ed735f7e731c8bc0f19ce0c1885226e4.png"></p> <p>转存</p> <img src="https://blog.laoda.de/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fimg.laoda.de%2Fi%2F2025%2F04%2F16%2F210022-1.webp&amp;size=m" alt="a8712fdc4810b3db84ecb00a8009c4f7.png" style="zoom:50%;"> <p>转存成功</p> <p><img src="https://blog.laoda.de/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fimg.laoda.de%2Fi%2F2025%2F04%2F16%2F210012-1.webp&amp;size=m" alt="4967cf7578df6a02493cc3a2376f5b6f.png"></p> <p><img src="https://blog.laoda.de/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fimg.laoda.de%2Fi%2F2025%2F04%2F16%2F210008-1.webp&amp;size=m" alt="6714b19a5272c1d86f9e0c40f1386eaf.png"></p> <p>当然如果你看到这边想自己搭建一个,那我们就继续往下看!</p> <h2 id="3--相关地址">3. 相关地址</h2> <p>官方GitHub地址:<a href="https://github.com/jiangrui1994/cloudsaver">https://github.com/jiangrui1994/cloudsaver</a> (目前3.6K个star,欢迎大家去给项目点星星!)</p> <h2 id="4--搭建环境">4. 搭建环境</h2> <ul> <li>服务器:这边强烈建议用非大陆的服务器,不然必须要启用代理设置,否则会刷不到任何内容,咕咕这边用的香港的<a href="https://loll.cc/tx">腾讯云轻量应用服务器</a>,建议服务器内存1G以上,当然你也可以选择其他<a href="https://blog.laoda.de/vps">高性价比的服务器</a>。</li> <li>系统:Debian 11 (<a href="https://blog.laoda.de/archives/useful-script/">DD 脚本</a> 非必需 DD,用原来的系统也 OK,之后教程都是用 Debian 或者 Ubuntu 搭建~)</li> <li>安装好 Docker、Docker-compose(<a href="https://blog.laoda.de/archives/hello-docker/">相关脚本</a>)</li> <li>【必需】域名一枚,并做好解析到服务器上(<a href="https://blog.laoda.de/archives/namesilo/">域名购买、域名解析</a> <a href="https://www.bilibili.com/video/BV1Sy4y1k7kZ/">视频教程</a>)</li> <li>【非必需】提前安装好宝塔面板海外版本 aapanel,并安装好 Nginx(<a href="https://forum.aapanel.com/d/9-aapanel-linux-panel-6812-installation-tutorial">安装地址</a>)</li> <li>【非必需本教程选用】安装好 Nginx Proxy Manager(<a href="https://blog.laoda.de/archives/nginxproxymanager/">相关教程</a>)</li> </ul> <h2 id="5--搭建视频-过俩天补充-----">5. 搭建视频(过俩天补充 = =)</h2> <h3 id="5-1-YouTube">5.1 YouTube</h3> <p>视频地址:</p> <h3 id="5-2-哔哩哔哩">5.2 哔哩哔哩</h3> <p>哔哩哔哩:</p> <h2 id="6--搭建方式">6. 搭建方式</h2> <h3 id="6-1-安装-Docker-与-Nginx-Proxy-Manager">6.1 安装 Docker 与 Nginx Proxy Manager</h3> <p>可以直接参考这篇内容:</p> <p><a href="https://blog.laoda.de/archives/nginxproxymanager/">https://blog.laoda.de/archives/nginxproxymanager/</a></p> <h3 id="6-2-创建安装目录">6.2 创建安装目录</h3> <p>创建一下安装的目录:</p> <pre><code class="language-bash">sudo -i mkdir -p /root/data/docker_data/cloudsaver cd /root/data/docker_data/cloudsaver </code></pre> <p>接着我们来编辑下<code>docker-compose.yml</code></p> <pre><code class="language-bash">vim docker-compose.yml </code></pre> <pre><code class="language-yaml">services: cloudsaver: image: jiangrui1994/cloudsaver:latest container_name: cloud-saver ports: - "8009:8008" volumes: - ./data:/app/data - ./config:/app/config restart: unless-stopped </code></pre> <p>其中的左边的<code>8009</code>可以改成服务器上没有用过的端口,记得修改自己的用户名和密码,修改完成之后,可以在英文输入法下,按 <code>i</code> 修改,完成之后,按一下 <code>esc</code>,然后 <code>:wq</code> 保存退出。</p> <h3 id="6-3-查看端口是否被占用">6.3 查看端口是否被占用</h3> <p>查看端口是否被占用(以 <code>8009</code> 为例),输入:</p> <pre><code class="language-bash">lsof -i:8009 #查看 8009 端口是否被占用,如果被占用,重新自定义一个端口 </code></pre> <p>如果啥也没出现,表示端口未被占用,我们可以继续下面的操作了~</p> <p>如果出现:</p> <pre><code class="language-bash">-bash: lsof: command not found </code></pre> <p>运行:</p> <pre><code class="language-bash">apt install lsof #安装 lsof </code></pre> <p>如果端口没有被占用(被占用了就修改一下端口,比如改成 <code>8381</code>,注意 docker 命令行里和防火墙都要改)</p> <h3 id="6-4-启动-cloudsaver">6.4 启动 cloudsaver</h3> <pre><code>cd /root/data/docker_data/cloudsaver docker compose up -d # 注意,老版本用户用 docker-compose up -d </code></pre> <p>耐心等待拉取好镜像,出现 <code>done</code>的字样之后,</p> <p>理论上我们就可以输入 <code>http://ip:8009</code> 访问了。</p> <p>但是这边这个服务必须先搞一下反向代理!http使用明文传输太危险,我们部署在公网一定要考虑使用反向代理工具配置SSL!</p> <p>做反向代理前,你需要一个域名!</p> <p><a href="https://loll.cc/ns">namesilo</a> 上面 xyz 后缀的域名一年就 7 块钱,可以年抛。(冷知识,namesilo上 6位数字的xyz续费永远都是0.99美元 = =)</p> <p>如果想要长期使用,还是建议买 com 后缀的域名,更加正规一些,可以输入 <code>laodade</code> 来获得 1 美元的优惠(不知道现在还有没有)</p> <p><a href="https://loll.cc/ns">namesilo</a> 自带隐私保护,咕咕一直在用这家,价格也是这些注册商里面比较低的,关键是他家不像其他家域名注册商,没有七七八八的套路!(就是后台界面有些 <del>丑</del> 古老 = =)</p> <p><a href="https://blog.laoda.de/archives/namesilo/">【域名购买】Namesilo 优惠码和域名解析教程(附带服务器购买推荐和注意事项)</a></p> <p>我们接着往下看!</p> <h2 id="7--反向代理">7. 反向代理</h2> <h3 id="7-1-利用-Nginx-Proxy-Manager">7.1 利用 Nginx Proxy Manager</h3> <p>在添加反向代理之前,确保你已经完成了域名解析,不会的可以看这个:<strong>域名一枚,并做好解析到服务器上</strong>(<a href="https://blog.laoda.de/archives/namesilo/">域名购买、域名解析</a> <a href="https://www.bilibili.com/video/BV1Sy4y1k7kZ/">视频教程</a>) (名称改成你自己想要的域名前缀即可)</p> <p><img src="https://blog.laoda.de/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fimg.laoda.de%2Fi%2F2025%2F04%2F16%2F210003-1.webp&amp;size=m" alt="763a1f9516de8f3696de76b708488a6b.png"></p> <p>之后,登陆 Nginx Proxy Manager(不会的看这个:<strong>安装 Nginx Proxy Manager</strong>(<a href="https://blog.laoda.de/archives/nginxproxymanager/">相关教程</a>))</p> <blockquote> <p><strong>注意:</strong></p> <p>Nginx Proxy Manager(以下简称 NPM)会用到 <code>80</code>、<code>443</code> 端口,所以本机不能占用(比如原来就有 Nginx)</p> </blockquote> <p>直接丢几张图:</p> <img src="https://blog.laoda.de/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fimg.laoda.de%2Fi%2F2025%2F04%2F16%2F205956-1.webp&amp;size=m" alt="014632b85d0fda607f46f81e0ec796fa.png" style="zoom:50%;"> <img src="https://blog.laoda.de/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fimg.laoda.de%2Fi%2F2025%2F04%2F16%2F205949-1.webp&amp;size=m" alt="c2075b8e6a85bff585396fd4c0bca1ed.png" style="zoom:50%;"> <img src="https://blog.laoda.de/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fimg.laoda.de%2Fi%2F2025%2F04%2F16%2F205943-1.webp&amp;size=m" alt="09174f6d6ad957739017f97f42d7d184.png" style="zoom:50%;"> <img src="https://blog.laoda.de/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fimg.laoda.de%2Fi%2F2025%2F04%2F16%2F205935-1.webp&amp;size=m" alt="f5137b06b270769cc2ebca7019f47e35.png" style="zoom:50%;"> <blockquote> <p>注意填写对应的 <code>域名</code>、<code>IP</code> 和 <code>端口</code>,按文章来的话,应该是 <code>8009</code></p> </blockquote> <p><strong>IP 填写:</strong></p> <p>如果 Nginx Proxy Manager 和 cloudsaver 在同一台服务器上,可以在终端输入:</p> <pre><code class="language-bash">ip addr show docker0 </code></pre> <p>查看对应的 Docker 容器内部 IP。</p> <img src="https://blog.laoda.de/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fimg.laoda.de%2Fi%2F2024%2F12%2F12%2F153819-1.webp&amp;size=m" style="zoom:50%;"> <p>否则直接填 cloudsaver 所在的服务器 IP 就行。</p> <p>然后访问域名就可以访问了!</p> <h3 id="7-2-利用宝塔面板">7.2 利用宝塔面板</h3> <p>发现还是有不少小伙伴习惯用宝塔面板,这边也贴一个宝塔面板的反代配置:</p> <p>直接新建一个站点,不要数据库,不要 php,纯静态即可。</p> <p>然后打开下面的配置,修改 Nginx 的配置。</p> <img src="https://blog.laoda.de/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fimg.laoda.de%2Fi%2F2024%2F12%2F12%2F153816-1.webp&amp;size=m" alt="image-20220819150345725" style="zoom: 33%;"> <img src="https://blog.laoda.de/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fimg.laoda.de%2Fi%2F2024%2F12%2F12%2F153814-1.webp&amp;size=m" alt="image-20220819150542867" style="zoom: 33%;"> <p>代码如下:</p> <pre><code class="language-nginx">location / { proxy_pass http://127.0.0.1:8009/; # 注意改成你实际使用的端口 rewrite ^/(.*)$ /$1 break; proxy_redirect off; proxy_set_header Host $host; proxy_set_header X-Forwarded-Proto $scheme; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header Upgrade-Insecure-Requests 1; proxy_set_header X-Forwarded-Proto https; } </code></pre> <p>此方法对 90% 的反向代理都能生效,然后就可以用域名来安装访问了。</p> <p>有同学可能会问,为什么不直接用宝塔自带的反向代理功能。</p> <img src="https://blog.laoda.de/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fimg.laoda.de%2Fi%2F2024%2F12%2F12%2F153804-1.webp&amp;size=m" alt="image-20220819150730128" style="zoom: 33%;"> <p>也可以,不过咕咕自己之前遇到过当有多个网站需要反代的时候,在这边设置会报错的情况 = =</p> <p>所以后来就不用了,直接用上面的方法来操作了。</p> <h2 id="8--使用教程">8. 使用教程</h2> <h3 id="8-1-搜索与转存资源">8.1 搜索与转存资源</h3> <p>登陆,开始注册。</p> <p><img src="https://blog.laoda.de/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fimg.laoda.de%2Fi%2F2025%2F04%2F16%2F205919-1.webp&amp;size=m" alt="9af544f6a382375205d3e75c249aa659.png"></p> <p><img src="https://blog.laoda.de/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fimg.laoda.de%2Fi%2F2025%2F04%2F16%2F205910-1.webp&amp;size=m" alt="902ac98e6625f92856d6a3da78ae9958.png"></p> <p>默认注册码:</p> <ul> <li>管理员:<code>230713</code></li> <li>普通用户:<code>9527</code></li> </ul> <p>我们是管理员就用<code>230713</code></p> <p>为了安全起见,登陆之后第一件事情就去更改一下默认的注册码。</p> <p><img src="https://blog.laoda.de/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fimg.laoda.de%2Fi%2F2025%2F04%2F16%2F205902-1.webp&amp;size=m" alt="2f41c48836c96cf2d7d25cc28d06676b.png"></p> <h3 id="8-2-115-网盘-cookie获取">8.2 115 网盘 cookie获取</h3> <p>用户配置,这边就是看你转存到哪个网盘,</p> <p><img src="https://blog.laoda.de/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fimg.laoda.de%2Fi%2F2025%2F04%2F16%2F205854-1.webp&amp;size=m" alt="89f9fae000c28e144b7c346959186a7f.png"></p> <p>这边我们以115网盘为例子:</p> <ul> <li>从网页登录 <code>115网盘</code>;</li> <li>按 <code>F12</code> 打开 <code>开发者工具</code>;</li> <li>在<code>Network</code> –&gt; <code>Filter</code> 中输入 <code>get</code>,找到 <code>warning_get</code>,就可以获取到 <code>cookie</code> 的值;</li> </ul> <p><img src="https://blog.laoda.de/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fimg.laoda.de%2Fi%2F2025%2F04%2F16%2F205744-1.webp&amp;size=m" alt="915a61376931f64e734453c150f25318.png"></p> <p>如果115网盘空间不够,可以扫码开通会员,年付158元,可以扩容至5T。</p> <img src="https://blog.laoda.de/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fimg.laoda.de%2Fi%2F2025%2F04%2F16%2F205731-1.webp&amp;size=m" alt="IMG_6982.JPG" style="zoom:33%;"> <h3 id="8-3-更新-cloudsaver">8.3 更新 cloudsaver</h3> <p>这个项目后续应该也会有更新,所以提供一个更新的方式。</p> <pre><code class="language-yaml">cd /root/data/docker_data/cloudsaver docker compose pull docker compose up -d # 请不要使用 docker compose stop 来停止容器,因为这么做需要额外的时间等待容器停止;docker compose up -d 直接升级容器时会自动停止并立刻重建新的容器,完全没有必要浪费那些时间。 docker image prune # prune 命令用来删除不再使用的 docker 对象。删除所有未被 tag 标记和未被容器使用的镜像 </code></pre> <p>提示:</p> <pre><code class="language-bash">WARNING! This will remove all dangling images. Are you sure you want to continue? [y/N] </code></pre> <p>输入 <code>y</code></p> <p>利用 Docker 搭建的应用,更新非常容易~</p> <h3 id="8-4-卸载-cloudsaver">8.4 卸载 cloudsaver</h3> <p>同样进入安装页面,先停止所有容器。</p> <pre><code class="language-bash">cd /root/data/docker_data/cloudsaver docker compose down cd .. rm -rf /root/data/docker_data/cloudsaver # 完全删除 </code></pre> <p>可以卸载得很干净。</p> <h2 id="9--常见问题及注意点">9. 常见问题及注意点</h2> <p>咕咕实测,115资源最多,其他俩网盘有些资源可能失效。</p> <p>另外,本项目仅供学习交流使用,请勿用于非法用途。</p> <p>大家有问题欢迎评论区交流。</p> <h2 id="10--结尾">10. 结尾</h2> <p>祝大家用得开心,有问题可以去 GitHub 提 <a href="https://github.com/jiangrui1994/cloudsaver/issues">Issues</a>,也可以在评论区互相交流探讨。</p> <p>同时,有能力给项目做贡献的同学,也欢迎积极加入到 <a href="https://github.com/jiangrui1994/cloudsaver/cloudsaver">项目</a> 中来,贡献自己的一份力量!</p> <p>最后,感谢作者的辛苦付出,让我们能用到这么优秀的项目!欢迎大家都去给这个项目点个 star ⭐️</p> <h2 id="参考资料">参考资料</h2> <p>官方GitHub:<a href="https://github.com/jiangrui1994/cloudsaver">https://github.com/jiangrui1994/cloudsaver</a></p>

2025/4/16
阅读更多