Docker 部署 doocs/md:运行微信 Markdown 编辑器
doocs/md 是一个把 Markdown 即时渲染为微信图文的 Web 编辑器,支持 KaTeX、Mermaid、PlantUML、代码高亮、本地草稿、图床和多种 AI 服务。官方 Docker 镜像把前端静态资源和一个监听 80 端口的轻量 HTTP 服务打包在一起,单个容器就能提供页面。
部署结论
| 项 | 结论 |
|---|---|
| GitHub 地址 | doocs/md |
| 官方镜像 | doocs/md,本文使用 doocs/md:2.1.0 |
| 项目版本 | 2.1.0 |
| 部署日期 | 2026-10-14 |
| 主部署方法 | image-run:直接运行官方版本镜像 |
| 备选方法 | source-build:需要修改前端或替换构建配置时,从源码构建 |
| 维护范围 | maintained:固定镜像版本,按容器生命周期更新和回滚 |
| 对外端口 | 容器 80;示例绑定到宿主机 127.0.0.1:8080 |
| 持久化位置 | 容器内无项目服务端数据卷;浏览器本地草稿、外部图床和 AI 服务配置在容器外管理 |
| 依赖服务 | 无数据库、缓存或其他必需容器 |
使用固定的 2.1.0 tag 可以避免 latest 在重新拉取后改变页面内容。官方 docker-md 仓库列出了 2.1.0、2.0.4 等版本镜像,并通过构建脚本为 linux/amd64 和 linux/arm64 构建多架构镜像。镜像没有官方 HEALTHCHECK,HTTP 页面探针是主要的运行验收手段。
架构说明
正在准备渲染...
查看源码
flowchart LR
browser[浏览器] --> host[127.0.0.1:8080]
host --> container[doocs/md:2.1.0]
container --> server[静态资源 HTTP 服务 :80]
browser --> local[浏览器本地草稿与设置]
browser --> external[图床或 AI 服务]容器只负责返回前端资源。Markdown 编辑、草稿自动保存以及部分偏好由浏览器完成;图床和 AI 请求是否可用,取决于浏览器端填写的服务配置与网络。删除或重建容器不会替代浏览器数据备份,也不会把外部服务的数据复制到 Docker 卷中。
前置条件
- Docker Engine 或 Docker Desktop 已安装,且当前用户可以执行
docker。 - 宿主机的
8080端口未被占用;需要其他端口时只改-p左侧端口。 - 浏览器能够访问宿主机地址。远程机器部署时,把
127.0.0.1:8080改成反向代理所在网络可达的绑定地址,并在代理层提供 HTTPS 和访问控制。 - 目标 CPU 为
linux/amd64或linux/arm64。其他架构应先确认 Docker Hub 的对应 manifest,再决定是否采用源码构建。
检查 Docker CLI:
docker version客户端和服务端都返回版本信息时,继续执行部署。若出现 Cannot connect to the Docker daemon,先启动 Docker Desktop 或 Docker Engine;这一步与 doocs/md 镜像无关。
使用官方镜像运行
先拉取固定版本:
docker pull doocs/md:2.1.0拉取成功后,创建容器并绑定本机回环地址:
docker run -d \
--name doocs-md \
--restart unless-stopped \
-p 127.0.0.1:8080:80 \
doocs/md:2.1.0命令返回容器 ID 后,页面入口是 http://127.0.0.1:8080。--restart unless-stopped 只控制容器进程的自动重启,不会为浏览器草稿创建服务端备份。
如果宿主机需要直接从局域网访问,把端口映射改为 -p 8080:80,并同时在防火墙或反向代理上限制来源。公网使用时不要把编辑器端口裸露在互联网上,见生产环境安全。
源码构建的适用条件
source-build 只适合需要修改 Vue/Vite 前端、替换静态资源或自行维护构建链的场景。仓库 README 给出的开发构建依赖 Node.js 22 和 pnpm,Docker 镜像构建还由 doocs/docker-md 的版本目录和多架构脚本负责;这条路径需要维护完整的构建上下文,不能用来替代固定镜像的快速运行。
验证部署结果
容器状态
docker ps --filter name=doocs-md --format 'table {{.Names}}\t{{.Image}}\t{{.Status}}\t{{.Ports}}'预期看到 doocs-md、doocs/md:2.1.0 和 0.0.0.0 或 127.0.0.1 到容器 80 端口的映射。没有输出时,查看 docker ps -a --filter name=doocs-md,再用 docker logs doocs-md 找到启动失败原因。
应用响应
curl -sS -o /dev/null -w '%{http_code}\n' http://127.0.0.1:8080/预期输出 200。浏览器打开同一地址后,应出现 Markdown 编辑器界面;若命令是 Connection refused,先检查容器状态与宿主端口是否被其他进程占用。
日志与健康
docker logs --tail 50 doocs-md
docker inspect --format '{{.State.Status}} {{.State.Restarting}}' doocs-md该镜像的服务端是简单的 Go HTTP 文件服务,成功启动时不一定输出应用横幅。docker inspect 预期为 running false;HTTP 200 比日志内容更适合作为页面健康信号。镜像没有声明 HEALTHCHECK,不要把 docker ps 中不存在的 (healthy) 当成失败。
无服务端卷的重建检查
doocs/md 不需要数据库或命名卷。可以用同一个固定 tag 重建容器,确认镜像可重复启动:
docker stop doocs-md
docker rm doocs-md
docker run -d \
--name doocs-md \
--restart unless-stopped \
-p 127.0.0.1:8080:80 \
doocs/md:2.1.0
curl -sS -o /dev/null -w '%{http_code}\n' http://127.0.0.1:8080/重建后的 200 只证明静态服务恢复。浏览器本地草稿能否保留,要在同一个浏览器配置文件中打开编辑器并检查草稿列表;清理浏览器站点数据、切换浏览器或使用隐私窗口都可能让本地草稿不可见。
首次使用与数据边界
打开 http://127.0.0.1:8080 后,可以在编辑器中创建 Markdown 草稿并预览微信排版。官方 2.1.0 镜像使用 v2.1.0 标签源码,文章通过 VueUse 的 useStorage 保存到浏览器 localStorage,键名为 MD__posts;记录包含标题、正文、历史版本、创建时间和更新时间。
在 Chrome 或 Edge 中可通过“开发者工具 → Application → Local Storage → 当前站点 → MD__posts”查看文章记录。存储按浏览器配置文件和站点来源(协议、主机、端口)隔离,所以 127.0.0.1:8080、localhost:8080 和正式域名会被视为不同的数据空间。数据文件由浏览器在本机用户配置目录中管理,不是每篇一个 Markdown 文件。Docker 容器本身没有文章目录,挂载 Docker 卷不会自动备份这些草稿。
较新的主分支已改用 IndexedDB:数据库名 doocs-md,文章位于 documents object store,初始化失败时降级到 localStorage。如果部署的是新版 latest 或自行构建的主分支,可通过“Application → IndexedDB → doocs-md → documents”检查。这个存储结构不能套用到本文固定的 2.1.0 标签;升级新版前应先导出重要文章。
| 数据 | 2.1.0 所在位置 | 容器删除后的结果 |
|---|---|---|
| 文章与历史记录 | 浏览器 localStorage 的 MD__posts | 同一来源、同一浏览器配置下通常保留;清理站点数据或更换来源后可能不可见 |
| 编辑偏好、AI 模型与密钥 | 浏览器 localStorage,包括 openai_key_<服务> 等键 | 不由容器托管;迁移与清理浏览器时需单独处理 |
| 图片 | 所选图床或文章引用的外部地址;AI 生成结果另有浏览器缓存 | 由图床或模型供应商保存,按对应服务的策略备份 |
2.1.0 容器不提供服务端文章数据库或账号云同步后端。较新主分支的配套云同步服务属于额外系统,部署 2.1.0 镜像不会自动启用云同步。
恢复单元是固定镜像版本、浏览器本地数据和外部图床配置。迁移文章时,优先把重要文章导出为 Markdown 文件,在新来源导入并验收标题、正文、历史和图片;普通单篇导出未必包含历史版本。需要保留完整浏览器状态时,使用浏览器配置备份方式,并保护其中的 AI 和图床密钥。通用 Docker 卷备份不能替代这些操作。
AI 能力与请求链路
Docker 镜像不包含大语言模型权重、推理服务或 GPU 运行时。AI 助手和文生图功能由前端根据配置调用 OpenAI 兼容接口,容器只负责提供 JavaScript、CSS 和页面资源。
默认内置服务
项目把默认服务标记为“内置服务”,默认端点是 https://proxy-ai.doocs.org/v1,默认 API Key 为空。仓库列出的默认聊天模型包括 Qwen、DeepSeek、GLM、InternLM,以及 OpenRouter 上的部分免费模型;这些模型名称只是前端预置选项,仓库没有声明某一个模型永久固定,也没有公开默认代理背后的具体供应商、配额或数据保留策略。
因此,选择“内置服务”代表浏览器把请求发送到 doocs 域名下的 AI 代理,而不是在 Docker 容器里本地推理。代理当前是否可用、实际路由到哪个模型和是否限流,应以服务端响应和项目最新说明为准。
直接使用第三方服务
2.1.0 的 AI 设置预置了 DeepSeek、OpenAI、通义千问、腾讯混元、火山方舟、硅基流动、302.AI、智谱 AI、百川、零一万物、月之暗面和百度千帆,并支持自定义 OpenAI 兼容服务。选择这些服务后,浏览器直接使用对应 endpoint;非默认服务配置了 API Key 时,前端会在请求中加入 Authorization: Bearer <API Key>。
聊天请求的实现流程如下:
- 前端把系统提示词、最近一段对话和可选的当前编辑器内容组装成
messages。 - 请求发送到
<endpoint>/chat/completions,携带模型、温度、最大 token 数和stream: true。 - 前端读取 SSE 流,逐段显示
content和推理内容。 - 对话记录写入浏览器本地缓存键
ai_conversation_*和ai_memory_context,不会写入 Docker 容器。
文生图使用相同的配置模式,请求发送到 <endpoint>/images/generations。默认图像模型是 Kwai-Kolors/Kolors,也可以选择 OpenAI、硅基流动、302.AI 或自定义服务。生成结果的地址、提示词和时间戳会暂存在浏览器 localStorage 的 ai_generated_images、ai_image_prompts 和 ai_image_timestamps 中,缓存按一小时过期清理。应及时下载图片,或上传到自己管理的图床再插入文章;缓存地址不保证长期有效,容器也不会归档图片文件。
AI 数据与安全边界
2.1.0 的 API Key、模型选择和 AI 对话缓存直接保存在浏览器 localStorage,不是服务端加密密钥库。较新的主分支才将密钥路由到 IndexedDB 的 secrets store,并把对话、生成图片缓存路由到 cache store。共享电脑使用后应删除敏感配置;不要把整份浏览器存储备份公开分享。
自托管 Docker 只改变页面的交付位置,不会自动把 AI 推理变成私有部署。使用内置服务时,提示词和编辑器上下文会发送到 proxy-ai.doocs.org;使用 OpenAI、DeepSeek 或其他供应商时,数据会发送到所选供应商。处理敏感文章前,应选择自己控制的兼容接口,或关闭 AI 工具,并按对应供应商的隐私、计费、留存和跨境策略评估风险。
日常运维
docker restart doocs-md
docker logs --tail 100 doocs-md
docker inspect --format '{{json .Config.Image}}' doocs-mdrestart 后再次访问首页并确认 HTTP 200。docker inspect 应显示 doocs/md:2.1.0,用于防止误运行了浮动的 latest。停止服务但保留容器:
docker stop doocs-md
docker start doocs-md删除容器不会删除镜像,也不会触及浏览器或图床数据。确认不再需要旧镜像后,再按镜像使用情况执行 docker image rm doocs/md:2.1.0;删除前应保留将要回滚的版本 tag。
升级与回滚
项目新版本发布后,先在 Docker Hub 和 doocs/docker-md 中确认新版本 tag,再替换容器。不要把 latest 直接作为长期维护版本:
NEW_VERSION="2.x.y" # 替换为已在 Docker Hub 确认存在的目标版本
docker pull "doocs/md:${NEW_VERSION}"
docker stop doocs-md
docker rm doocs-md
docker run -d \
--name doocs-md \
--restart unless-stopped \
-p 127.0.0.1:8080:80 \
"doocs/md:${NEW_VERSION}"
curl -sS -o /dev/null -w '%{http_code}\n' http://127.0.0.1:8080/升级验收至少包括容器为 running、首页返回 200、编辑器可以输入并预览 Markdown。若页面资源或交互出现回归,使用旧 tag 重复同一套停止、删除、运行命令即可回滚:
docker pull doocs/md:2.1.0
docker stop doocs-md
docker rm doocs-md
docker run -d \
--name doocs-md \
--restart unless-stopped \
-p 127.0.0.1:8080:80 \
doocs/md:2.1.0无服务端迁移脚本和数据库 schema 需要回退,但浏览器草稿和外部服务配置仍应单独验证。若新版本改变了前端存储格式,旧镜像回滚不保证已经写入新格式的浏览器数据可读;升级前先导出重要草稿。
生产环境安全
- 默认示例只绑定
127.0.0.1,适合让 Nginx、Caddy 或其他反向代理接管 HTTPS、域名和访问控制。对外代理时,把源站端口留在本机网络,按服务反向代理与 HTTPS的通用规则配置 TLS 和来源限制。 2.1.0的官方 Docker 路径只提供静态文件服务,没有服务端登录、权限或团队隔离配置。把 doocs/md 当作无认证编辑器部署;需要团队权限时,在反向代理或网络边界增加认证,不要仅凭页面地址推断已有账号系统。- 图床 token 和 AI API key属于浏览器或外部服务的敏感配置。不要把真实值写入 shell 历史、公开仓库、截图或客户端共享配置;发现泄露时按对应服务立即轮换。
- 使用固定 tag,并在升级前保留仍可回滚的旧 tag。镜像来源改变、改用第三方镜像前,先重新核对镜像仓库和摘要。
- 容器不需要 Docker Socket、特权模式或宿主目录挂载;不要为了“方便管理”额外添加这些权限。
通用的 Compose 安全基线、镜像更新和日志策略见Docker Compose 生产环境安全。
常见问题
端口 8080 已被占用
将 -p 127.0.0.1:8080:80 左侧改为可用端口,例如 -p 127.0.0.1:18080:80,然后用新的端口访问。容器内端口仍保持 80。
浏览器访问 404 或空白页
官方镜像按根路径提供页面,访问 http://127.0.0.1:8080/。/md/ 是源码开发构建的路径约定,直接运行官方镜像时不要把 /md/ 追加到 URL。若根路径仍异常,确认容器镜像确实是 doocs/md:2.1.0,并查看 docker logs doocs-md。
容器反复退出
运行 docker ps -a --filter name=doocs-md 和 docker logs doocs-md。如果主机架构不在官方多架构构建范围内,先在 docker image inspect doocs/md:2.1.0 中检查平台信息;不要把 --platform 随意改成未验证的架构。
AI 请求失败或一直没有输出
浏览器直接访问所选 AI endpoint,供应商必须允许编辑器来源的跨域请求(CORS)。在开发者工具 Network 中检查实际请求地址、状态码和响应:401 常与密钥相关,429 常与配额或限流相关,CORS 错误需要供应商或自己控制的 API 网关放行该来源。HTTPS 页面调用 HTTP 接口还可能被浏览器按混合内容阻止。编辑器的反向代理只代理静态页面,不会自动转发这些 AI 请求。
删除容器后草稿不见了
容器不保存浏览器本地草稿。优先检查是否换了浏览器、用户配置文件、隐私窗口或清除了站点数据;重要内容应在编辑器导出或同步到外部服务后再做容器更新。
总结
doocs/md:2.1.0 适合用单容器提供微信 Markdown 编辑器页面:固定版本镜像、绑定宿主机端口、用 HTTP 200 和容器状态完成验收。服务端无数据库和持久化卷,真正需要保护的是浏览器草稿、图床内容和 AI 凭据;升级与回滚围绕镜像 tag 展开,并在每次变更后重新检查编辑、预览和外部服务连接。