Docker 部署 TokenTelemetry:在本机分析 AI 编码会话
TokenTelemetry 从 Claude Code、Codex、Gemini CLI、Cursor、GitHub Copilot 等工具写入本机的会话文件中提取 Token、模型、成本估算、工具调用、计划和项目活动。Docker 部署适合希望隔离 Python/Node.js 依赖,同时保留本地日志分析能力的个人开发环境。
Codex 部署只把宿主机 ~/.codex/sessions 只读挂载到后端。该范围足以扫描新版 Codex 的 rollout-*.jsonl,并避免直接暴露 ~/.codex/auth.json、config.toml 和其他认证配置。下方 Compose 已完成静态解析;当前环境未启动容器,也没有读取真实会话内容做功能验收。
部署结论
| 项目 | 本文采用的值 |
|---|---|
| GitHub 地址 | VasiHemanth/tokentelemetry |
| 官方镜像 | 后端与前端 GHCR Packages |
| 项目版本 | 1.0.0 |
| 正式部署 | Docker Compose,前端与后端配套运行 |
| 快速体验 | 不提供 docker run:前后端、构建期 API 端口、日志挂载和数据卷需要保持一致 |
| 适用范围 | 单用户、本机历史会话分析;不作为公网多用户观测平台 |
| 主机架构 | Linux amd64、arm64 |
| 最低资源 | 官方没有给出容器部署的 CPU、内存和磁盘下限 |
| Web 页面 | 宿主机 127.0.0.1:13000 -> 前端 3000 |
| 后端 API | 宿主机 127.0.0.1:18000 -> 后端 8000 |
| 日志来源 | ${HOME}/.codex/sessions -> /root/.codex/sessions:ro |
| 应用数据 | Docker volume tokentelemetry_data -> /tt-data |
| 依赖服务 | 无需外部数据库或缓存 |
| 验证范围 | 官方文档与 Compose 静态检查;未运行容器 |
| 部署日期 | 2026-08-28 |
TokenTelemetry 当前没有 Git tag,项目元数据版本为 1.0.0。前端与后端需要使用同一次构建的镜像,避免 API 或页面字段不一致。
数据流与边界
浏览器只访问宿主机回环端口。前端根据构建时写入的 18000 请求后端;后端读取 Codex 会话文件,把扫描缓存、历史汇总和界面设置写入独立数据卷。Compose 不挂载 Docker socket,也不需要 API key。
正在准备渲染...
查看源码
flowchart LR
Browser[本机浏览器] -->|127.0.0.1:13000| Frontend[TokenTelemetry frontend]
Frontend -->|127.0.0.1:18000| Backend[TokenTelemetry backend]
Sessions[Codex sessions] -->|只读挂载| Backend
Backend --> Data[(tokentelemetry_data)]只读挂载能阻止容器修改 Codex 日志,不能阻止后端读取日志内容。会话文件可能包含提示词、模型输出、工具参数、命令、路径和项目名称;第三方镜像一旦被替换或被攻破,读取到的内容仍可能被外传。固定镜像、限制挂载范围和关闭非必要外联缺一不可。
部署前准备
主机需要 Docker Engine 或 Docker Desktop,以及 Docker Compose v2:
docker --version
docker compose version确认 Codex 已产生会话文件,并检查两个宿主机端口:
find "${HOME}/.codex/sessions" -type f -name 'rollout-*.jsonl' -print -quit
ss -lntp 2>/dev/null | grep -E ':(13000|18000):space:' || truemacOS 没有 ss 时使用:
lsof -nP -iTCP:13000 -sTCP:LISTEN
lsof -nP -iTCP:18000 -sTCP:LISTEN第一条命令没有输出通常表示 Codex 尚未生成会话,或 CODEX_HOME 使用了其他位置。先确认真实日志目录,不要为了让页面出现数据而挂载整个主目录。
Docker Compose 正式部署
准备部署目录
下面以 /opt/tokentelemetry 为长期目录。Compose 文件不包含认证信息,但目录仍应只允许管理员修改,避免本机其他账号替换镜像或挂载路径:
sudo install -d -m 0750 -o "$(id -u)" -g "$(id -g)" /opt/tokentelemetry
cd /opt/tokentelemetry保存 .env:
TZ=Asia/Shanghai
TT_DATA_VOLUME=tokentelemetry_data.env 只保存时区和卷名,不存放 Codex 或模型凭据。
写入 Compose 配置
保存为 /opt/tokentelemetry/compose.yaml:
services:
backend:
image: ghcr.io/vasihemanth/tokentelemetry-backend:sha-cecce1c@sha256:9abeee7b470c0676368b54df824fb62261f54686a2cb905309a499cde32a923c
environment:
TT_HOST: 0.0.0.0
TT_API_PORT: "8000"
TOKENTELEMETRY_DATA_DIR: /tt-data
TOKENTELEMETRY_HOME: /tt-data
TZ: ${TZ:-UTC}
DO_NOT_TRACK: "1"
TT_NO_UPDATE_CHECK: "1"
volumes:
- tt_data:/tt-data
- ${HOME}/.codex/sessions:/root/.codex/sessions:ro
ports:
- 127.0.0.1:18000:8000
healthcheck:
test:
- CMD-SHELL
- python3 -c "import socket; s=socket.socket(); s.settimeout(3); s.connect(('localhost', 8000)); s.close()"
interval: 15s
timeout: 5s
retries: 5
start_period: 10s
restart: unless-stopped
frontend:
image: ghcr.io/vasihemanth/tokentelemetry-frontend:sha-cecce1c@sha256:efd3d24840393d6f09037a0a3e400810630a169da9e7f6c5d9c40a4326e1aff7
ports:
- 127.0.0.1:13000:3000
depends_on:
backend:
condition: service_started
restart: unless-stopped
maintenance:
image: ghcr.io/vasihemanth/tokentelemetry-backend:sha-cecce1c@sha256:9abeee7b470c0676368b54df824fb62261f54686a2cb905309a499cde32a923c
profiles:
- maintenance
network_mode: none
volumes:
- tt_data:/tt-data
- ./backups:/backup
entrypoint:
- /bin/sh
- -lc
volumes:
tt_data:
name: ${TT_DATA_VOLUME:-tokentelemetry_data}TT_HOST=0.0.0.0 只控制后端在容器内部监听;宿主机仍由 127.0.0.1:18000:8000 限制在回环地址。预构建前端已经把 API 宿主机端口 18000 编译进客户端,不能只修改后端端口。需要其他端口时,应使用与后端相同版本的源码重新构建前端并设置 NEXT_PUBLIC_API_PORT。
SELinux enforcing 主机若拒绝读取 Codex 日志,把挂载末尾改为 :ro,z,让 Docker 为该目录设置共享容器标签。不要把 :ro 删除。
解析并启动
先检查最终配置是否仍然只发布回环端口、只挂载预期目录:
cd /opt/tokentelemetry
docker compose config --quiet
docker compose config | grep -E '127\.0\.0\.1|\.codex/sessions|/tt-data'静态解析没有输出且退出码为 0 后,再拉取和启动:
docker compose pull
docker compose up -d
docker compose psbackend 应先进入 running,健康检查通过后显示 healthy;frontend 应保持 running。
验证部署结果
容器与镜像
docker compose ps
docker compose images
docker inspect --format '{{.State.Health.Status}}' \
"$(docker compose ps -q backend)"预期后端健康状态为 healthy。docker compose images 应显示两个 sha-cecce1c 镜像,不能出现 latest。
页面与 API
curl -fsS http://127.0.0.1:18000/
curl -fsS http://127.0.0.1:18000/agents
curl -fsS -o /dev/null -w 'sessions HTTP %{http_code}\n' \
'http://127.0.0.1:18000/sessions?fresh=1'
curl -fsS -o /dev/null -w 'frontend HTTP %{http_code}\n' \
http://127.0.0.1:13000/后端根路径应返回 {"message":"TokenTelemetry API is running"},/agents 应包含 codex,两个 HTTP 检查应返回 200。不要把完整 /sessions 响应保存到公开日志;响应可能带有会话标题、项目路径和用量明细。
日志与挂载
docker compose logs --tail=100 backend frontend
docker compose exec backend sh -lc \
"find /root/.codex/sessions -type f -name 'rollout-*.jsonl' -print -quit"
docker inspect --format '{{range .Mounts}}{{println .Source "->" .Destination "rw=" .RW}}{{end}}' \
"$(docker compose ps -q backend)"日志不应持续出现重启、Python 异常或前端请求失败。Codex 挂载的 rw 应为 false,/tt-data 对应的命名卷应为 true。
持久化
在页面 Settings 中修改一个不敏感的显示偏好,然后重建容器:
docker compose up -d --force-recreate backend frontend
docker compose ps重新打开 http://127.0.0.1:13000/。显示偏好仍存在,才能证明 tokentelemetry_data 在容器重建后保留;这个检查不等于备份恢复演练。
开始分析 Codex 会话
打开 http://127.0.0.1:13000/ 后,按以下顺序确认数据是否合理:
- Dashboard 检查已识别 Agent、近期活动、模型分布和 Token 变化。
- Projects 按代码目录查看会话、工具使用和模型对比;Git worktree 可能归并到主仓库项目。
- Sessions 打开单次会话时间线,检查提示词、推理片段、工具调用、子 Agent 和最终结果。
- Analytics 按 Agent、模型和时间范围比较 Token 与估算成本。
- Settings 检查计费模式、保留策略和隐私开关。
成本数据来自会话记录和 TokenTelemetry 的价格表,不能替代模型提供方账单。Budget 只负责观察和提醒,不会阻止 Agent 继续消耗。Codex Skill 使用通常根据会话中读取 skills/<name>/SKILL.md 的工具调用推断,不是 Codex 提供的结构化 Skill 事件;MCP 和工具统计也受日志字段完整度影响。
当前最小挂载不会读取 ~/.codex/config.toml、插件缓存或完整 Skills 目录。因此页面能分析会话中已经记录的工具、MCP 和 Skill 痕迹,但无法完整展示本机已配置却未在会话中出现的项目。只有明确需要配置盘点时才增加单文件或单目录只读挂载,并先审查其中是否包含密钥、内部地址或私有指令。
接入其他 Agent
后端只能分析容器中可见的日志。增加其他 Agent 前,先在宿主机确认实际目录,再把最小必要路径挂载为只读。官方 Compose 给出的常见目标包括:
| Agent | 容器内默认目录 | 挂载建议 |
|---|---|---|
| Claude Code | /root/.claude | 优先定位会话子目录;整个目录可能包含配置与认证资料 |
| Gemini CLI | /root/.gemini | 只读挂载,并检查目录中的账号与项目配置 |
| Cline | /root/.cline | 需要 CLI SQLite 或扩展存储时分别挂载 |
| Cursor / VS Code Copilot | /root/.config/... | 路径含空格,Compose 中必须加引号 |
| Hermes Agent | /root/.hermes | TokenTelemetry 需要与 Hermes 日志位于同一主机 |
每增加一个挂载,后端可读取的数据范围都会扩大。不要把 ${HOME}、SSH 目录、云凭据目录或整个开发工作区挂进容器,也不要使用 privileged 或 /var/run/docker.sock。
日常运维
cd /opt/tokentelemetry
docker compose ps
docker compose logs --tail=100 backend frontend
docker compose restart backend frontend
docker compose exec backend sh -lc 'du -sh /tt-data; find /tt-data -maxdepth 2 -type f -print | head -n 30'风险提示:docker compose down 会删除容器和网络,但保留 tokentelemetry_data;docker compose down -v 和官方 make clean 会继续删除数据卷。执行任何卷删除操作前必须完成并验证“备份与恢复”章节中的归档,不要把这两个命令当成普通停止命令。
备份与恢复
Codex 原始会话仍保存在宿主机 ~/.codex/sessions,不属于 TokenTelemetry 数据卷。tokentelemetry_data 保存历史汇总、扫描缓存、摘要、偏好和计费配置,应按敏感数据处理。
maintenance profile 默认不会启动,也不挂载 Codex 会话目录。先创建独立备份目录,再停止两项服务并归档命名卷:
cd /opt/tokentelemetry
BACKUP_NAME="$(date +%F-%H%M%S)/tt-data.tar.gz"
BACKUP_DIR="${PWD}/backups/${BACKUP_NAME%/*}"
install -d -m 0700 "$BACKUP_DIR"
cp compose.yaml .env "$BACKUP_DIR/"
docker compose stop frontend backend
docker compose --profile maintenance run --rm \
-e BACKUP_NAME="$BACKUP_NAME" maintenance \
'tar -C /tt-data -czf "/backup/$BACKUP_NAME" .'
docker compose start backend frontend
sudo chmod 600 "$BACKUP_DIR/tt-data.tar.gz"
tar -tzf "$BACKUP_DIR/tt-data.tar.gz" | tail -n 20归档检查通过后,把整个时间戳目录复制到加密的异机存储。恢复时不要清空当前卷;先创建新卷并把归档解压进去:
cd /opt/tokentelemetry
BACKUP_DIR="/opt/tokentelemetry/backups/2026-08-28-120000"
test -f "$BACKUP_DIR/tt-data.tar.gz"
tar -tzf "$BACKUP_DIR/tt-data.tar.gz" >/dev/null
RESTORE_VOLUME="tokentelemetry_data_restore_$(date +%Y%m%d%H%M%S)"
docker volume create "$RESTORE_VOLUME"
cp .env .env.before-restore
sed -i.bak "s/^TT_DATA_VOLUME=.*/TT_DATA_VOLUME=${RESTORE_VOLUME}/" .env
BACKUP_NAME="${BACKUP_DIR##*/}/tt-data.tar.gz"
docker compose --profile maintenance run --rm \
-e BACKUP_NAME="$BACKUP_NAME" maintenance \
'tar -C /tt-data -xzf "/backup/$BACKUP_NAME"'
docker compose up -d --force-recreate backend frontend
curl -fsS http://127.0.0.1:18000/示例中的 BACKUP_DIR 必须替换为实际归档目录。页面设置、项目和历史汇总确认无误后,保留旧卷一段时间;恢复失败时把 .env.before-restore 移回 .env 并重新创建容器。
升级与回滚
升级前备份数据卷,并在官方仓库确认目标版本的 Compose、Dockerfile、更新说明和配套镜像。分别检查后端与前端镜像:
docker buildx imagetools inspect \
ghcr.io/vasihemanth/tokentelemetry-backend:sha-cecce1c
docker buildx imagetools inspect \
ghcr.io/vasihemanth/tokentelemetry-frontend:sha-cecce1c确认目标版本后,同时替换 Compose 中的两个 tag 和 digest,再执行:
cd /opt/tokentelemetry
docker compose config --quiet
docker compose pull
docker compose up -d
docker compose ps
curl -fsS http://127.0.0.1:18000/
docker compose logs --tail=100 backend frontend前后端必须来自同一次构建,避免 API 或页面字段不匹配。官方没有为每个版本提供数据库迁移和降级兼容承诺;回滚不能只替换镜像。出现历史数据异常时,恢复升级前数据卷,并把两个镜像一起切回升级前版本。
安全边界
~/.codex/sessions可能包含完整对话、工具参数、文件路径、命令和模型输出。只读不等于不可提取,镜像来源与 digest 必须纳入升级审查。- 两个端口只绑定宿主机
127.0.0.1。远程访问优先使用双端口 SSH 隧道:ssh -N -L 13000:127.0.0.1:13000 -L 18000:127.0.0.1:18000 user@host。 DO_NOT_TRACK=1关闭默认启用的匿名产品使用遥测,TT_NO_UPDATE_CHECK=1关闭 GitHub 更新检查。页面 Settings 应显示相应开关被环境策略关闭。- 不向 Compose 加入 Codex
auth.json、模型 API key、Docker socket、privileged或宿主机根目录。 - TokenTelemetry 的后端 API 没有为默认本机模式配置登录。任何反向代理、公网发布、LAN 或 tailnet 暴露都需要额外认证、TLS 和来源限制,不能只把
127.0.0.1改成0.0.0.0。 tokentelemetry_data会形成原始日志之外的历史副本和统计缓存。备份、卷快照和支持工单都应脱敏并限制访问。
常见问题
页面能打开但没有 Codex 会话
先确认宿主机和容器看到相同的 rollout 文件:
find "${HOME}/.codex/sessions" -type f -name 'rollout-*.jsonl' -print -quit
docker compose exec backend sh -lc \
"find /root/.codex/sessions -type f -name 'rollout-*.jsonl' -print -quit"
curl -fsS http://127.0.0.1:18000/agents宿主机有文件而容器没有时,检查 Compose 变量展开、Docker Desktop 文件共享和 SELinux 标签。Codex 使用自定义 CODEX_HOME 时,把真实 sessions 目录映射到容器内 /root/.codex/sessions。
前端出现空白数据或 API 请求失败
curl -fsS http://127.0.0.1:18000/
docker compose logs --tail=100 frontend backend预构建前端在构建时写入后端宿主机端口 18000。只修改端口映射会让浏览器继续请求旧端口;恢复 127.0.0.1:18000:8000,或使用与后端相同版本的源码重新构建前端并设置新的 NEXT_PUBLIC_API_PORT。
Linux 主机提示挂载权限或 permission denied
docker compose config | grep -A4 '/root/.codex/sessions'
ls -ld "${HOME}/.codex" "${HOME}/.codex/sessions"普通权限问题应修正宿主机目录的所有者和遍历权限,不要改成所有本机用户可读写。SELinux enforcing 主机把挂载改成 ${HOME}/.codex/sessions:/root/.codex/sessions:ro,z 后重新创建 backend。
端口 13000 或 18000 已被占用
ss -lntp 2>/dev/null | grep -E ':(13000|18000):space:' || true
lsof -nP -iTCP:13000 -sTCP:LISTEN 2>/dev/null || true
lsof -nP -iTCP:18000 -sTCP:LISTEN 2>/dev/null || true前端端口 13000 可以更换宿主机映射;后端端口 18000 与预构建前端绑定,不能单独修改。需要整体换端口时使用与后端相同版本的源码重建前端。
部署完成后
- 对照一条已知 Codex 会话检查模型、Token、工具和项目归属,不把成本估算当成账单。
- 设置合适的历史保留策略,并定期检查
tokentelemetry_data的增长与备份恢复结果。 - 增加其他 Agent 前逐个审查日志目录,保持只读和最小挂载,不一次性开放整个主目录。
总结
TokenTelemetry 通过配套的前后端镜像,在宿主机回环端口提供 AI 编码会话分析页面。Codex 部署只需把 ~/.codex/sessions 只读挂载到后端,就能扫描历史 rollout,并把汇总、缓存和界面设置保存在独立数据卷中。
TokenTelemetry 展示的是本地日志能够证明的历史数据。成本属于估算,Skill 使用可能来自路径推断,完整请求链路、压缩前后指标和模型提供方账单仍需要其他数据源;会话挂载和持久化卷都应视为敏感数据。
参考资料
- TokenTelemetry 官方仓库 - 核对日期:2026-08-28
- 部署源码版本 - 对应 Compose 与容器发布配置
- 官方 Compose - 核对服务、端口、挂载、数据卷和健康检查
- 官方生产镜像 overlay - 核对 GHCR 镜像命名与
sha-*标签 - 容器发布工作流 - 核对多架构、短 SHA 标签和前端 API 端口
- 后端 Dockerfile - 核对 Python 版本、后端端口和启动命令
- 前端 Dockerfile - 核对 Node.js、构建参数和前端端口
- TokenTelemetry Docker 文档 - 核对官方容器入口与运维命令