ActivityWatch 自托管时间追踪:本地部署、外部访问与安全加固实践

发布时间:2026/9/15 8:58:04
ActivityWatch 自托管时间追踪:本地部署、外部访问与安全加固实践 1. 为什么在时间追踪这件事上我最终选了 ActivityWatch先说个背景我之前做自由职业每天坐在电脑前十几个小时但到了月底复盘时完全说不清时间到底花在哪了。试过手动计时软件记几天就放弃因为人一旦忙起来根本想不起点“开始”和“结束”。后来开始找自动记录工具才发现这个领域有个经典难题凡是好用的商业软件数据都躺在别人服务器上凡是能本地存数据的配置门槛又让人劝退。直到我接触了 ActivityWatch。这是个开源的时间追踪应用核心思路就是“自动记录”——装上之后它会通过 watcher 组件默默统计你正在使用哪个应用、浏览哪些网页、在编辑器里写了多久代码全程不需要手动打卡。因为它是开源的数据默认存在本地 SQLite 数据库里隐私完全自己掌控。而且官方提供了浏览器插件、桌面端、移动端多个组件生态虽然小众但该有的都有。这篇内容就围绕“本地部署 外部访问”展开我不只讲怎么把 ActivityWatch 跑起来还会讲清楚外部访问的几种方案怎么选、怎么做安全加固、以及我实际跑了大半年踩过的各种坑。适合三类人看一是想认真做时间复盘的自由职业者和远程办公族二是有隐私敏感需求、坚持数据自托管的人三是刚接触自托管、想找个轻量项目练手的朋友。2. 部署前的准备工具选型和架构认知2.1 为什么是 Docker 部署而不是直接跑源码ActivityWatch 官方支持两种运行方式直接源码运行和 Docker 容器运行。源码方式适合开发调试因为你能看到全部日志、能改代码但代价是要装 Python 环境、Node 环境、以及一堆前端构建工具升级时还要自己处理依赖冲突。我第一回就是源码跑的折腾了两个小时才把 Web UI 跑起来后来换成 Docker 重新部署前后不到五分钟。如果你的目标只是“用起来”直接选 Docker。官方镜像已经把服务端和 Web UI 打包好了数据目录也做了持久化映射升级时换个镜像标签就行。Docker 方式还有个好处ActivityWatch 由多个 watcher 进程组成比如窗口标题监听、浏览器插件、编辑器插件容器化之后彼此隔离出问题时单独重启一个容器就可以不用连坐整个服务。不过要注意多数 watcher 组件如浏览器插件、VS Code 插件是装在你日常用的电脑上的不属于 Docker 的管辖范围。Docker 主要跑的是核心服务端 aw-server、aw-webui、aw-watcher-afk 这几个角色理解清楚这点后面配置时才不会糊涂。2.2 ActivityWatch 的核心架构Watcher、Bucket、Event在动手部署前我建议花三分钟理解 ActivityWatch 的数据模型后面所有配置和排查都围绕它展开。ActivityWatch 的核心概念有三个Watcher采集器、Bucket数据桶、Event事件。Watcher 负责从各种来源采集数据比如 aw-watcher-window 会每秒记录当前活动窗口的标题和应用名aw-watcher-browser 会通过浏览器插件记录你访问的网址。它每采集到一条数据就把它写进一个指定的 BucketBucket 相当于一个分类容器比如“浏览器历史”是一个 bucket“编辑器活动”是另一个 bucket。而这些具体的数据点就叫 Event每条 Event 包含一个时间段start 和 end以及一段元数据比如应用名、网页标题。我们部署的核心服务端本质上就是一个接收和存储 Event 的后台服务。Web UI 则负责把这些 Event 聚合成可读的时间线。明白了这套链路当你发现某个时间段没有数据时排查方向就清晰了先看 watcher 有没有在运行再看数据有没有写入对应的 bucket最后看 UI 查询有没有报错。3. 本地部署 ActivityWatch 的完整实操3.1 使用 Docker Compose 快速拉起服务端我的部署环境是一台 Ubuntu 22.04 的迷你主机内网 IP 是 192.168.1.100Docker 和 Docker Compose 插件都已经装好。如果你还没装 Docker可以先执行官方一键脚本这里不赘述。下面是我一直在用的 docker-compose.yml直接保存到/opt/activitywatch/docker-compose.ymlversion: 3.8 services: activitywatch: image: activitywatch/activitywatch:latest container_name: activitywatch restart: unless-stopped ports: - 5600:5600 volumes: - ./aw-data:/data environment: - AW_DIR/data - AW_HOST0.0.0.0解释几个关键配置image用的是latest标签。官方在 Docker Hub 上发布的镜像会跟随版本更新我建议第一次部署用 latest 跑通流程稳定后固定到一个具体版本号比如v0.13.0避免某天升级带来不兼容变化。ports将宿主机的 5600 端口映射到容器内部的 5600。ActivityWatch 默认 Web 端口就是 5600官方文档里也提到这是不可配置的硬编码端口。volumes把主机的./aw-data目录挂载到容器内的/data这一步很关键。ActivityWatch 的所有数据库文件都在这个目录里如果哪天容器崩了只要这个目录还在数据就不会丢。environment里设置AW_HOST0.0.0.0是为了让服务监听所有网卡接口否则容器内部只有 localhost 能访问宿主机反而访问不到。配置写好后直接执行cd /opt/activitywatch docker compose up -d第一次启动会拉取镜像等一两分钟。完成后执行docker compose ps看到状态是 Up 就说明容器跑起来了。这时打开http://192.168.1.100:5600应该能看到 ActivityWatch 的 Web 界面左侧菜单有 Dashboard、Timeline、Buckets 等入口。3.2 初始化与启用常用 Watcher 组件服务端跑起来只是第一步真正的数据采集要靠 watcher 客户端。按照使用场景我把常用 watcher 分成三类桌面端 watcher必装aw-watcher-window和aw-watcher-afk。前者记录当前活动窗口标题和应用名后者监听键盘鼠标的输入事件用于判断你是否在电脑前。安装方式很简单直接去 ActivityWatch 官网或 GitHub Release 下载对应系统的安装包即可Windows 有 exemacOS 有 pkgLinux 有 AppImage。浏览器插件强烈推荐ActivityWatch 官方提供了 Chrome/Firefox 扩展装上之后需要在插件设置里填上服务端地址。默认填的是http://localhost:5600如果你像我一样浏览器和服务端不在同一台机器上这里要改成http://192.168.1.100:5600。改完测试连接显示 connected 就说明浏览器访问记录可以正常上报。编辑器插件可选VS Code 有 ActivityWatch 官方扩展JetBrains 系也有第三方插件。这类插件能记录你具体在哪个文件上花了多少时间对程序员做项目复盘非常有用。我见过不少新手装完服务端就以为完事了实际上电脑上的图标栏根本不会出现任何可见的程序。ActivityWatch 的 watcher 基本都是无界面静默运行的只有在浏览器插件图标上能看到一个小弹窗。判断运行是否正常的方法是打开 Web UI 的 Timelines 页面等一两分钟如果时间线上开始出现色块说明数据已成功上报。如果什么都没有优先检查 watcher 的配置文件和服务端地址有没有写错。3.3 数据目录、日志与基本验证方法数据文件都在/opt/activitywatch/aw-data下面结构大致是这样aw-data/ ├── activitywatch/ │ ├── activitywatch.db # 核心数据库 │ ├── ... ├── ...如果你要备份只需要把整个aw-data目录压缩存档即可。我每天晚上通过 cron 任务把它增量同步到另一块硬盘这个后面再细说。排查问题时看日志是最快的路径。用 Docker 部署时一条命令就能看到全部日志docker compose logs -f activitywatch正常使用中日志会定期出现 watcher 心跳和 bucketing 信息。如果看到连不上数据库、端口被占用之类的报错就按日志提示逐一排除。有一个关键词要记住heartbeat。ActivityWatch 的 watcher 和服务端之间通过 heartbeat 机制同步状态正常每 10-30 秒会有一条心跳记录如果长时间没有心跳说明某个 watcher 掉线了需要重启对应组件。4. 实现外部访问方案选型与安全加固4.1 外部访问的需求边界只看 vs 管理部署好之后我不满足于只在局域网内看数据。下班后或者出门在外想通过手机快速看一眼今天的工时分布这就需要从外网访问家里的 ActivityWatch。动手之前先想清楚一个关键问题你外部访问的目的是什么如果只是看数据我建议降低权限预期——只读就够了。如果希望在外网也能管理 watcher、改配置、删 bucket那就需要完整的 Web 访问能力。但权限越大暴露风险越大。ActivityWatch 的 Web UI 默认没有用户认证谁拿到地址谁就能看所以外部访问时必须做一层身份验证不能裸奔。围绕这个需求实际可行的方案有四类方案原理优点缺点适合场景反向代理 HTTPS通过 Nginx/Caddy 暴露 5600 端口并加基础认证配置简单、可控性强需要公网 IP 或端口映射有公网 IP 的宽带用户内网穿透工具frp通过一台公网服务器转发流量到内网服务不需要公网 IP多一跳、延迟略高没有公网 IP 的普通宽带异地组网工具Tailscale/ZeroTier组建虚拟局域网像访问内网一样访问服务安全、零公网暴露需要客户端安装个人设备少、追求省心IPv6 端口转发通过 IPv6 地址直连不占用额外服务器移动网络 IPv6 支持不一定好网络环境支持 IPv6 的用户4.2 推荐方案一Caddy 反向代理加基础认证我最终采用的是 Caddy 反向代理方案。选择 Caddy 而不是 Nginx 的原因很直接Caddy 自动申请和续期 HTTPS 证书不用手动处理一堆配置。如果你的服务需要通过浏览器插件上报数据一定要走 HTTPS否则浏览器插件在非安全上下文里会有各种限制。Caddy 的核心配置长这样放在Caddyfile里aw.example.com { reverse_proxy 192.168.1.100:5600 basicauth { timeuser $2a$14$...hashvalue... } }其中aw.example.com替换成你自己的域名并在 DNS 服务商那里把域名解析到你的公网 IP。basicauth是关键它用 HTTP 基本认证保护整个站点访问时需要输入用户名密码。密码 hash 可以用caddy hash-password命令生成caddy hash-password --plaintext YOUR_STRONG_PASSWORD生成的 hash 字符串填到配置里即可。这个方案的好处是所有流量都经过 HTTPS 加密认证由 Caddy 统一拦截ActivityWatch 本身根本不需要感知外部认证的存在逻辑清晰后期维护也方便。4.3 推荐方案二frp 内网穿透接入如果你没有公网 IPfrp 是另一套可靠方案。frp 的原理不复杂你在公网服务器上运行 frps服务端在家里的迷你主机上运行 frpc客户端frpc 主动连接 frps 建立一个长连接通道外部流量经由公网服务器转发到内网服务。frps 的配置公网服务器端frps.tomlbindPort 7000 auth.token YOUR_RANDOM_TOKENfrpc 的配置内网迷你主机端frpc.tomlserverAddr your-server-ip serverPort 7000 auth.token YOUR_RANDOM_TOKEN [[proxies]] name activitywatch type tcp localIP 127.0.0.1 localPort 5600 remotePort 5600配置完成后启动 frps 和 frpc你在公网访问http://your-server-ip:5600就能连回家里的 ActivityWatch 服务了。这个方案的硬伤是流量要绕道公网服务器延迟会高一些但时间追踪这种低频率页面访问完全感受不到差别。注意 frp 本身没有身份认证能力所以上述两个方案里我仍然建议你在 ActivityWatch 前面额外套一层 Caddy 做 HTTPS 和认证或者至少用 frp 自带的auth.token来防止别人蹭你的代理通道。4.4 安全加固外部访问必须做的三件事把服务暴露到公网后安全这事就不是可选项而是必选题。我分享三个自己实践中必须做的加固动作。第一强制 HTTPS。尤其当你用 Caddy 或 Nginx 反代时把 HTTP 全部重定向到 HTTPS。道理很简单时间追踪数据包含你的网页访问记录、应用使用习惯这些都是相当私密的信息明文传输等于把隐私直接扔在公网上。第二加认证授权。ActivityWatch 自身不提供多用户权限所以必须在代理层挡一道。除了上面说的 Caddy 基础认证你也可以用 Authelia 这类开源身份认证中间件做更细粒度的控制。如果嫌麻烦至少设置一个强密码的 basic auth。第三限制来源 IP 和行为。如果条件允许可以在防火墙规则里只允许特定国家的 IP 段访问或者限制访问频率。Caddy 可以配合 ratelimit 模块frp 也能在客户端限制请求频率。我的做法是只允许常用省份的 IP 段并且在路由器上做了连接数限制实测至今没有异常访问记录。注意任何暴露到公网的服务都存在被扫描和探测的风险。ActivityWatch 本身是个人级应用不建议把敏感数据放任在公网裸奔。如果只是想偶尔在外网瞄一眼优先考虑 Tailscale/ZeroTier 这类组网方案因为它们默认就不暴露任何公网端口安全等级比端口转发高一个量级。5. 日常使用中的问题排查与避坑总结5.1 浏览器插件常见连接问题我在实践过程中遇到最多的不是服务端出问题而是浏览器插件连不上。典型表现是插件图标上显示 disconnected或者数据根本没有浏览器记录。排查流程我总结成一张速查表现象可能原因解决办法插件测试连接失败服务端地址填错检查填写的地址是否带端口号是否可访问局域网内能连、外网连不上反代配置错误或端口未放行检查 Caddy/Nginx 日志、检查云安全组/路由端口映射插件显示 connected 但没有数据浏览器隐私模式阻止了扩展换个普通窗口测试或允许扩展在隐私模式下运行数据延迟很严重Watcher 心跳间隔设置问题默认间隔是 10 秒一般不需要改检查服务端负载还有一个容易踩的坑Caddy 反代时浏览器插件走 HTTPS 连接但 ActivityWatch 内部是 HTTP需要保证 Caddy 的reverse_proxy正确传递 WebSocket。时间追踪虽然不依赖 WebSocket但有些第三方插件会用到建议在 Caddy 的 proxy 配置里加上websocket相关的默认参数Caddy 2 默认支持自动升级 WebSocket不需要额外写。5.2 数据缺失、时间偏移和统计误差ActivityWatch 的统计不一定完全准确这是开源工具的常态理解它的误差来源比抱怨更有效。数据缺失最常见的原因是 watcher 崩溃或电脑休眠。比如 aw-watcher-window 在 Linux 上依赖 X11 的窗口信息如果你用的是 Wayland 会话部分版本会拿不到窗口标题数据自然就断了。解决办法是切换回 X11或者用第三方兼容版本。另外笔记本合盖进入休眠期间没有任何数据这也是正常的aw-watcher-afk 会把它归为 away 状态。时间偏移问题往往是系统时区配置不正确导致的。ActivityWatch 存储的是 UTC 时间戳Web UI 会根据浏览器时区显示本地时间。如果你的服务器时区设置错了或者浏览器所在设备时区变了看到的时间线就会整体偏移。排查方法很简单直接在容器里执行date看系统时间再对比 Web UI 上的时区设置。我习惯在 docker-compose.yml 的 environment 里加上TZAsia/Shanghai保证数据库记录的底层时间戳和本地时间一致。5.3 容器资源占用与长期运行的稳定性ActivityWatch 的资源占用非常轻量。我的迷你主机配置是 4 核 8GB 内存ActivityWatch 服务端长期占用大概 150MB 内存、CPU 平时几乎为 0偶尔查询时爬到 20% 左右。如果你是用低配设备跑这个负载完全在承受范围内。长期运行最需要注意的是日志膨胀和数据库增长。Docker 默认会保留所有 stdout 日志如果 ActivityWatch 天天跑日志文件会越攒越大。我习惯在 docker-compose.yml 里加上日志轮转配置services: activitywatch: ... logging: driver: json-file options: max-size: 20m max-file: 3数据库方面SQLite 文件会随时间增长但以我半年多的使用量来看总数据量也就几十 MB完全不需要手动清理。如果你发现自己某个 bucket 数据量异常大可以直接在 Web UI 的 Buckets 页面删除那个 bucketActivityWatch 会自动重建。5.4 升级与回滚注意事项ActivityWatch 的迭代节奏不算快但偶尔也会有行为变化。升级前务必先备份数据目录我吃过一次亏一次性从 0.11 跳到 0.13升级后旧数据读取正常但某个 watcher 的 bucket 结构变了导致时间线显示不完整。现在我的升级流程固定为先备份aw-data再拉取新镜像启动后访问 Web UI 看 bucket 列表是否完整。如果发现问题指定旧版本标签重新启动即可回滚。由于数据是独立的回滚镜像不会影响数据完整性。6. 数据复盘让 ActivityWatch 真正发挥价值6.1 用 API 拉取数据做周报统计ActivityWatch 提供了完整的 REST API这意味着你不需要手动去 Web UI 里一个个点就能把数据变成自己的周报。它的 API 接口地址通常是http://localhost:5600/api/0/。我用一个简单的 Python 脚本每周自动统计各类应用的使用时长import requests import datetime AW_API http://192.168.1.100:5600/api/0 start datetime.datetime.now() - datetime.timedelta(days7) end datetime.datetime.now() # 获取窗口活动 bucket buckets requests.get(f{AW_API}/buckets?start{start.isoformat()}end{end.isoformat()}).json() for bucket_id, bucket_info in buckets.items(): if window not in bucket_id: continue events requests.get(f{AW_API}/buckets/{bucket_id}/events?start{start.isoformat()}end{end.isoformat()}).json() total_seconds sum(e[duration] for e in events) print(f{bucket_id}: {total_seconds / 3600:.2f} hours)这个脚本的精髓在最后两行遍历所有 window bucket把事件时长累加并按小时输出。输出结果就是你这一周在各类应用上的总耗时。配合列表里的应用名再自己写个小组装就能得到一个类似“本周微信 12 小时、浏览器 30 小时、编辑器 25 小时”的分布表。6.2 数据可视化与自定义看板官方 Web UI 的 Dashboard 已经具备了基础的分类统计但如果你想看更多维度比如“某个项目文件夹对应的编辑器耗时”就得自己动手。我的做法是在 Grafana 里新建一个数据源通过 ActivityWatch API 把查询结果拉进来。具体方式是写一个 cron 脚本每 10 分钟从 API 拉一次最新数据写入 MySQL然后 Grafana 直接查 MySQL。这样就能在手机上随时打开一个漂亮的看板直观看到当天工作时间、最耗时的应用、以及连续工作多少分钟需要休息。不过说句大实话这套联动方案适合喜欢折腾的人。多数用户用官方 Web UI 就足够了它提供的按标签颜色分类的时间线已经非常直观没有必要为了“看起来高级”而引入额外组件。6.3 多设备统一收集与合并ActivityWatch 支持多设备数据汇总到同一个服务端。我在公司电脑和家里迷你主机上都装了 watcher两边的数据通过同一个服务端地址上报。Web UI 上会自动按设备名拆分成不同的 bucket查询时可以把所有设备的数据合并成一个时间线。这里有一个细节每台设备在安装 watcher 时生成的 device 名默认是主机名。如果你有好几台设备建议在安装时就把 device 名改成辨识度高的名字比如work-laptop、home-mini。否则后续看数据时你会分不清哪条时间线是哪台机器上的。这个修改在 Web UI 的 Buckets 页面就能操作不需要改配置文件。7. 我持续使用半年后的一些真实体会如果只看安装部署ActivityWatch 算不上一款“开箱即用”的工具它需要你花时间去理解 watcher、bucket 这些概念也需要你去配置外部访问的安全策略。但它的价值恰恰在这份动手折腾里。商业时间追踪软件给你一个完整闭环却把数据锁在云端ActivityWatch 把整个链路摊在你面前数据、逻辑、扩展点全部透明你能按照自己的实际需求改造它。我最满意的一次实践是有段时间觉得自己每天很忙但不知道忙什么。跑了一周 ActivityWatch 统计后发现浏览器时间占比高达 55%其中视频平台占了三分之一。这个数据让我下意识地调整了工作习惯把视频类站点挪到另一台闲置设备上工作电脑只保留生产力工具。第二周再看统计有效工作时间肉眼可见地涨了一截。这类“原来时间都去哪了”的观察就是时间追踪工具最大的价值。回到外部访问这件事我目前的最终方案是 Caddy Tailscale 同时跑。Caddy 管公网 HTTPS 访问Tailscale 管手机和家用设备之间的安全回连。两套方案互不干扰也不存在单点故障。如果你刚上手我建议先按最简方案走Docker 跑服务端、本地浏览器装好插件、局域网内看数据。等完全熟悉了再考虑反代和外网访问。数据是自己的服务是自己的慢慢搭建的过程本身也是自托管乐趣的一部分。

关于本文作者

来自尧图内容编辑团队

尧图内容编辑团队 内容团队

尧图内容编辑团队

本文由尧图网络内容编辑团队执笔。团队由资深项目经理、前端工程师与设计师组成,所有内容均来自亲手交付的真实项目,先讲清问题、再给出可落地的解法。尧图深耕北京网站建设十年,服务过京华建材集团、智造科技等各行业客户,把一线经验沉淀为可复用的行业观察。

  • 十年建站经验,覆盖建材、制造、服务、文创等
  • 项目经理把关选题与事实准确性
  • 工程师与设计师联合撰写专业细节
  • 统一编辑规范,保证文风与排版一致
  • 每月复盘转化数据,迭代选题方向

延伸阅读

相关资讯与近期热门内容

深度阅读推荐

建站决策前值得细读的三篇

网站改版的5个关键决策
2024-08-12

网站改版的5个关键决策

什么时候该改版、改到什么程度、如何避免流量掉光,京华建材集团改版复盘给出答案。

获取专属建站方案

看完文章,把您的行业与预算告诉我们,免费获取一份量身定制的官网建设方案与报价。

立即免费咨询