draw.example.com、gitlab.example.com、192.0.2.16 作为示例值。落地时替换为自己的域名、GitLab 地址和服务器 IP;OAuth 应用 ID 与 Secret 只放在部署机 .env 或密钥管理系统里。这套部署把 diagrams.net / Draw.io 跑成内网可控的 Web 服务,再通过 GitLab OAuth 接入仓库存储。入口由 Nginx 做 HTTPS 代理,容器侧同时启用 PlantUML 和图片导出服务,适合在 GitLab Wiki、技术文档和架构图维护场景里使用。
0. 部署边界#
| 项 | 值 |
|---|---|
| 服务 | diagrams.net / Draw.io |
| 部署方式 | Docker Compose |
| 主服务镜像 | jgraph/drawio |
| 容器名 | drawio |
| 容器内端口 | 8080 |
| 宿主机端口 | 8016 |
| 对外入口 | Nginx HTTPS 反向代理 |
| GitLab 回调路径 | https://draw.example.com/gitlab |
| 可选能力 | PlantUML、图片导出、中文默认语言、自托管静态资源 |
整体链路如下:
flowchart LR
browser["Browser"]
nginx["Nginx HTTPS
draw.example.com:8716"]
drawio["drawio
jgraph/drawio:8080"]
plantuml["plantuml-server
:8080"]
export["image-export
:8000"]
gitlab["GitLab
OAuth + Repository API"]
browser -->|HTTPS| nginx
nginx -->|HTTP proxy_pass| drawio
drawio -->|PLANTUML_URL| plantuml
drawio -->|EXPORT_URL| export
drawio -->|OAuth / Save / Open| gitlab
这里没有把 Draw.io 直接暴露到公网。公网入口只交给 Nginx,后端端口按实际拓扑限制来源。
1. 目录准备#
创建部署目录:
mkdir -p /data/docker-compose/drawio/data/fonts
cd /data/docker-compose/drawio
建议目录结构:
/data/docker-compose/drawio/
├── compose.yaml
├── .env
├── PreConfig.js
└── data/
└── fonts/
data/fonts 用于挂载自定义字体。PreConfig.js 用于观察和补丁 Draw.io 前端初始化配置,比如默认中文和 GitLab 开关。
2. GitLab OAuth 应用#
Draw.io 保存到 GitLab 时需要 OAuth 应用。用管理员账号进入 GitLab 后台:
Admin Area -> Applications -> New application
按下面方式创建:
| 配置项 | 值 |
|---|---|
| Name | drawio |
| Redirect URI | https://draw.example.com/gitlab |
| Confidential | 按 GitLab 后台默认策略启用 |
| Scopes | api、read_repository、write_repository |
创建后会拿到两项信息:
Application ID:写入.env的DRAWIO_GITLAB_ID。Secret:写入.env的DRAWIO_GITLAB_SECRET。
这两个值只在部署机 .env 和密钥管理系统里保存。截图发布前至少确认没有 Secret;如果截图里出现 Secret,先在 GitLab 后台轮换后再发布。
GitLab OAuth 应用创建完成后,页面上能看到 Application ID、更新 Secret 按钮、回调 URL 和授权范围。生产环境里只需要把 ID 和 Secret 写入 .env。

3. 环境变量#
在部署目录创建 .env:
DRAWIO_BASE_URL=https://draw.example.com
DRAWIO_VIEWER_URL=https://draw.example.com/js/viewer.min.js
DRAWIO_LIGHTBOX_URL=https://draw.example.com
DRAWIO_GITLAB_ID=CHANGE_ME_GITLAB_APPLICATION_ID
DRAWIO_GITLAB_SECRET=CHANGE_ME_GITLAB_APPLICATION_SECRET
DRAWIO_GITLAB_URL=https://gitlab.example.com
DRAWIO_CSP_HEADER="default-src 'self'; script-src 'self' https://storage.googleapis.com https://apis.google.com https://docs.google.com https://code.jquery.com 'unsafe-inline'; connect-src 'self' https://gitlab.example.com https://gitlab.com https://*.dropboxapi.com https://api.trello.com https://api.github.com https://raw.githubusercontent.com https://*.googleapis.com https://*.googleusercontent.com https://graph.microsoft.com https://*.1drv.com https://*.sharepoint.com https://*.google.com https://fonts.gstatic.com https://fonts.googleapis.com; img-src * data:; media-src * data:; font-src * about:; style-src 'self' 'unsafe-inline' https://fonts.googleapis.com; frame-src 'self' https://*.google.com;"
EXPORT_URL=http://image-export:8000/
PLANTUML_URL=http://plantuml-server:8080/
DRAWIO_SELF_CONTAINED=1
DRAWIO_PUSHER_MODE=2
TZ=Asia/Shanghai
限制权限:
chmod 600 /data/docker-compose/drawio/.env
DRAWIO_GITLAB_URL 填 GitLab 站点根地址,例如 https://gitlab.example.com。jgraph/drawio 容器启动脚本会基于这个值生成服务端 OAuth token 地址,即 ${DRAWIO_GITLAB_URL}/oauth/token。不要在这里再手动拼 /oauth/token,否则容易生成重复路径。
如果接入的是自建 GitLab,DRAWIO_CSP_HEADER 的 connect-src 里也要放行 GitLab 域名。否则页面能打开,但保存到 GitLab 时浏览器控制台可能出现 CSP 拦截。
4. Compose 配置#
compose.yaml:
services:
drawio:
image: jgraph/drawio
container_name: drawio
restart: always
env_file:
- .env
ports:
- "8016:8080"
volumes:
- ./data/fonts:/usr/share/fonts/drawio
- ./PreConfig.js:/usr/local/tomcat/webapps/draw/js/PreConfig.js
networks:
- drawionet
depends_on:
- plantuml-server
- image-export
logging:
driver: json-file
options:
max-size: "50m"
max-file: "2"
plantuml-server:
image: linkease/drawio-plantuml-server:2022121901
container_name: plantuml-server
restart: always
environment:
- TZ=Asia/Shanghai
networks:
- drawionet
expose:
- "8080"
image-export:
image: linkease/drawio-image-export:2022121901
container_name: image-export
restart: always
environment:
- TZ=Asia/Shanghai
networks:
- drawionet
expose:
- "8000"
networks:
drawionet:
driver: bridge
如果 Nginx 和 Draw.io 在同一台机器上,可以把端口收得更紧:
ports:
- "127.0.0.1:8016:8080"
如果 Nginx 是独立入口机,后端 Draw.io 需要被入口机访问,就保留 8016:8080,同时用防火墙或安全组只允许 Nginx 入口机访问 8016。
这里继续使用现场已有的 linkease/drawio-plantuml-server:2022121901 和 linkease/drawio-image-export:2022121901。如果环境允许统一使用官方镜像,也可以把两个辅助服务替换成 jgraph/plantuml-server 和 jgraph/export-server,但替换前要重新验证 PlantUML 渲染和图片导出。
5. PreConfig.js#
jgraph/drawio 每次启动都会生成 /usr/local/tomcat/webapps/draw/js/PreConfig.js。当 Compose 把 ./PreConfig.js 挂载到这个路径时,容器生成的内容会写回宿主机文件。
先创建空文件,避免 Docker 把同名路径创建成目录:
cd /data/docker-compose/drawio
touch PreConfig.js
docker compose up -d drawio
test -s PreConfig.js && echo "PreConfig generated"
如果需要默认中文,在容器启动后追加:
urlParams['lang'] = 'zh';
可以用命令补丁,刷新浏览器即可,不要为了这一步立即重启容器:
grep -q "urlParams\\['lang'\\] = 'zh'" PreConfig.js || \
printf "\\nurlParams['lang'] = 'zh';\\n" >> PreConfig.js
这个补丁在下一次容器启动时会被入口脚本重新生成的内容覆盖。升级、重启或重建后,再执行一次上面的补丁命令即可。需要长期固化时,把补丁命令放到部署脚本里,或者维护一个派生镜像。
如果 GitLab 入口没有出现在 Draw.io 的保存菜单里,检查 PreConfig.js 里是否存在 GitLab 配置。环境变量方式会在启动阶段写入:
window.DRAWIO_GITLAB_URL = 'https://gitlab.example.com';
window.DRAWIO_GITLAB_ID = 'CHANGE_ME_GITLAB_APPLICATION_ID';
不要手工删除这些 GitLab 相关配置。PreConfig.js 里只会出现 GitLab URL 和应用 ID,Secret 会写入容器内 WEB-INF/gitlab_client_secret,不要把容器文件和日志原样贴到公开渠道。
6. 启动和基础验证#
启动服务:
cd /data/docker-compose/drawio
docker compose up -d
查看状态:
docker compose ps
期望三个容器都是 Up:
NAME STATUS
drawio Up
plantuml-server Up
image-export Up
检查 Draw.io HTTP:
curl -I http://127.0.0.1:8016
检查日志:
docker logs --tail 120 drawio
日志中应能看到 PreConfig.js 初始化输出,并且 GitLab 环境变量不为空。如果日志里没有 GitLab 相关配置,优先检查 .env 是否被容器读取。检查时不要把 Secret 打到终端:
docker exec drawio sh -lc '[ -n "$DRAWIO_GITLAB_ID" ] && [ -n "$DRAWIO_GITLAB_SECRET" ] && [ -n "$DRAWIO_GITLAB_URL" ] && echo "GitLab env OK"'
验证辅助服务的网络连通:
docker exec drawio sh -lc 'getent hosts plantuml-server && getent hosts image-export'
7. Nginx 反向代理#
下面示例保留根路径代理,不做 /drawio/ 之类的子路径改写。Draw.io 的静态资源、OAuth 回调和前端路由都从根路径进入,最少踩坑。
如果 Nginx 和容器在同一台机器:
server {
listen 8716 ssl http2;
listen [::]:8716 ssl http2;
server_name draw.example.com;
ssl_certificate /etc/nginx/certs/example.com/fullchain.pem;
ssl_certificate_key /etc/nginx/certs/example.com/privkey.pem;
ssl_protocols TLSv1.2 TLSv1.3;
location / {
proxy_set_header Host $http_host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-NginX-Proxy true;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_pass http://127.0.0.1:8016;
}
}
如果 Nginx 在入口机,Draw.io 在后端机器,把 proxy_pass 指向后端内网地址:
proxy_pass http://192.0.2.16:8016;
加载前检查语法:
nginx -t
重载:
systemctl reload nginx
外部验证:
curl -I https://draw.example.com:8716/
如果使用标准 443 端口,把 Nginx listen 和访问地址改成自己的 HTTPS 入口即可。
8. GitLab Diagrams.net 集成#
如果要在 GitLab Wiki 或 Markdown 编辑器里直接打开自建 Draw.io,需要在 GitLab 管理后台启用 Diagrams.net:
Admin Area -> Settings -> General -> Diagrams.net
配置:
- 勾选
Enable Diagrams.net。 Diagrams.net URL填https://draw.example.com,如果使用自定义端口则填https://draw.example.com:8716。- 保存配置。
GitLab 官方文档说明,自托管 GitLab 可以接入公共 diagrams.net,也可以接入本地部署的 diagrams.net。离线或内网环境通常选择后者。
GitLab 后台保存后,Diagrams.net 区块会保留当前配置的 Draw.io URL。这个 URL 必须和 Nginx 对外入口一致,否则 Wiki 里打开的仍然不是自建实例。

验证方式:
- 进入一个测试项目的 Wiki。
- 新建或编辑页面。
- 插入 diagrams.net 图。
- 确认打开的是自建 Draw.io 地址。
- 保存后重新打开,确认 SVG 图能正常加载。
9. Review Gate#
部署前先过这些检查:
| 检查项 | 命令 | 继续条件 | 停止条件 |
|---|---|---|---|
| Docker | docker --version && docker compose version | 命令正常返回 | Docker/Compose 不可用 |
| 目录 | test -d /data/docker-compose/drawio | 目录存在且可写 | 部署目录不可写 |
.env | test -f .env && grep '^DRAWIO_GITLAB_ID=' .env | 已配置 OAuth 应用 | 仍是空值或占位值 |
| 权限 | stat -c '%a %n' .env | .env 权限为 600 | 普通用户可读 |
| 端口 | `ss -lntup | grep ‘:8016’ | |
| Nginx | nginx -t | 配置语法通过 | 语法失败 |
| OAuth 回调 | GitLab 应用后台检查 | Redirect URI 是 /gitlab | 回调地址和入口不一致 |
| 敏感信息 | `find . -type f ! -name .env -print0 | xargs -0 grep -IlE ‘gloas-|glpat-|BEGIN PRIVATE’ | true` |
只要 OAuth Secret 曾经出现在截图、聊天或仓库里,就先在 GitLab 后台重置,再更新 .env 并重启容器。
10. 功能验收#
最小验收不要只看首页能打开,要覆盖存储、回调和辅助服务:
| 场景 | 操作 | 通过标准 |
|---|---|---|
| 首页 | 打开 https://draw.example.com:8716/ | 页面正常加载,无静态资源 404 |
| GitLab 登录 | 选择保存到 GitLab | 跳转 GitLab OAuth,授权后回到 /gitlab |
| 仓库保存 | 选择测试项目保存 .drawio 或 .svg | GitLab 仓库中出现文件 |
| 重新打开 | 从 GitLab 打开刚才保存的图 | 图形内容完整 |
| PlantUML | 插入一段 PlantUML 图 | 能渲染图形 |
| 图片导出 | 导出 PNG/SVG/PDF | 文件可以下载,内容不为空 |
| GitLab Wiki | 在 Wiki 插入 diagrams.net 图 | 编辑器打开自建 Draw.io |
排查时先看 Draw.io 日志,再看 Nginx access/error log,最后看 GitLab OAuth 应用配置。OAuth 回调不一致是最常见的问题。
11. 备份和回滚#
Draw.io 本身接近无状态,关键配置在部署目录:
compose.yaml.envPreConfig.jsdata/fonts
备份:
cd /data/docker-compose
tar czf drawio-backup-$(date +%F-%H%M%S).tar.gz drawio/compose.yaml drawio/.env drawio/PreConfig.js drawio/data/fonts
修改镜像、Nginx 或 OAuth 配置前先备份。回滚时恢复文件后重启:
cd /data/docker-compose/drawio
docker compose down
docker compose up -d
如果问题发生在 GitLab OAuth 应用配置,优先恢复 Redirect URI、Scopes 和 Secret,而不是回滚容器。
12. 常见问题#
GitLab 保存菜单没有出现#
先检查 DRAWIO_GITLAB_ID、DRAWIO_GITLAB_SECRET、DRAWIO_GITLAB_URL 是否进入容器。不要用 docker compose config 直接 grep Secret:
docker exec drawio sh -lc '[ -n "$DRAWIO_GITLAB_ID" ] && [ -n "$DRAWIO_GITLAB_SECRET" ] && [ -n "$DRAWIO_GITLAB_URL" ] && echo "GitLab env OK"'
docker exec drawio sh -lc 'grep -q "DRAWIO_GITLAB" /usr/local/tomcat/webapps/draw/js/PreConfig.js && echo "PreConfig GitLab OK"'
如果 PreConfig.js 被宿主机旧文件覆盖,需要重新从容器生成文件,或者把 GitLab 配置补回去。
OAuth 提示 redirect_uri 不匹配#
GitLab 应用里的 Redirect URI 必须和入口完全一致:
https://draw.example.com/gitlab
如果入口使用 8716 端口,则应配置:
https://draw.example.com:8716/gitlab
协议、域名、端口和路径都要一致。
invalid_client 或 401#
通常是 DRAWIO_GITLAB_ID、DRAWIO_GITLAB_SECRET 或 DRAWIO_GITLAB_URL 错误。DRAWIO_GITLAB_URL 填 GitLab 根地址,不要额外拼 /oauth/token。
浏览器控制台出现 CSP 拦截#
自建 GitLab 域名需要进入 DRAWIO_CSP_HEADER 的 connect-src。改完 .env 后重建容器:
docker compose up -d --force-recreate drawio
再刷新页面重试 GitLab 保存流程。
PlantUML 或导出失败#
先检查容器互通:
docker exec drawio sh -lc 'getent hosts plantuml-server image-export'
docker logs --tail 100 plantuml-server
docker logs --tail 100 image-export
PLANTUML_URL 和 EXPORT_URL 必须使用 Compose 网络里的服务名,不要写宿主机外部域名。
反向代理后静态资源 404#
确认 Nginx 使用根路径代理:
location / {
proxy_pass http://127.0.0.1:8016;
}
不要把 Draw.io 强行挂到子路径,除非已经验证过所有静态资源、OAuth 回调和前端路由。
13. 发布前清理#
这类文档最容易泄露三样东西:OAuth Secret、GitLab 内部域名和后台截图里的凭据。发布前至少扫一遍 Markdown:
grep -nEi 'gloas-|glpat-|oauth2:|BEGIN (RSA|OPENSSH|PRIVATE)|Authorization:[[:space:]]*Bearer|client_secret|private_key|10\\.|172\\.(1[6-9]|2[0-9]|3[0-1])\\.|192\\.168\\.' drawio-docker-compose-deployment-blog.md || true
命中占位变量名是正常的,命中真实 token、真实内网地址或真实域名就停止发布。截图如果包含 Secret、私钥或不可公开的后台地址,先打码或轮换后再发布。
14. 参考链接#
- GitLab Diagrams.net 集成文档:https://docs.gitlab.com/administration/integration/diagrams_net/
- jgraph/docker-drawio:https://github.com/jgraph/docker-drawio
- diagrams.net Docker 说明:https://www.drawio.com/blog/diagrams-docker-app
