Umami 密码重置全攻略:Docker 部署下 npx 跑不通的真正原因与解决方法
适用版本: Umami v2.x + PostgreSQL(Docker / 宿主机 / 远程均可)
难度: ★☆☆☆☆
预计耗时: 3 分钟
如果你使用 Docker 部署了 Umami,却突然忘记了管理员密码,可能会发现官方提供的 npx umami reset-password 命令在 Docker 容器里根本无法正常运行。
别急,这并不是你的命令写错了。
本文将从问题原因开始,一步一步说明为什么官方 CLI 在 Docker 镜像中无法使用,以及如何直接通过 PostgreSQL 数据库重置 Umami 用户密码。
TL;DR
如果你已经确定自己的 Umami 使用的是 PostgreSQL,并且容器中存在 DATABASE_URL 环境变量,可以直接使用下面的命令。
首先获取 Umami 容器 ID:
docker ps
找到 Umami 对应的容器,例如:
CONTAINER ID IMAGE NAMES
a1b2c3d4e5f6 ghcr.io/umami-software/umami:postgresql umami
记住这里的 CONTAINER ID,后面的 <umami容器ID> 都需要替换成你自己的容器 ID。
然后执行:
docker exec -i <umami容器ID> sh -c 'cd /tmp && DATABASE_URL="$DATABASE_URL" NODE_PATH=/tmp/umami-fix/node_modules node -e "const{Client}=require(\"pg\");const bcrypt=require(\"bcryptjs\");(async()=>{const c=new Client({connectionString:process.env.DATABASE_URL});await c.connect();const hash=await bcrypt.hash(process.argv[1],10);const r=await c.query(\"UPDATE \\\"user\\\" SET password=\$1 WHERE username=\$2\",[hash,process.argv[2]]);console.log(\"updated\",r.rowCount,\"row(s)\");await c.end();})().catch(e=>{console.error(e.message);process.exit(1);})" <新密码> <用户名>'
例如你的容器 ID 是:
a1b2c3d4e5f6
用户名是:
admin
新密码是:
MyNewPassword123!
那么实际执行:
docker exec -i a1b2c3d4e5f6 sh -c 'cd /tmp && DATABASE_URL="$DATABASE_URL" NODE_PATH=/tmp/umami-fix/node_modules node -e "const{Client}=require(\"pg\");const bcrypt=require(\"bcryptjs\");(async()=>{const c=new Client({connectionString:process.env.DATABASE_URL});await c.connect();const hash=await bcrypt.hash(process.argv[1],10);const r=await c.query(\"UPDATE \\\"user\\\" SET password=\$1 WHERE username=\$2\",[hash,process.argv[2]]);console.log(\"updated\",r.rowCount,\"row(s)\");await c.end();})().catch(e=>{console.error(e.message);process.exit(1);})" MyNewPassword123! admin'
看到:
updated 1 row(s)
就说明密码修改成功。
一、首先找到你的 Umami 容器 ID
由于 Docker 容器 ID 是随机生成的,每个人的容器 ID 都不一样,因此本文后续统一使用:
<umami容器ID>
作为占位符。
执行:
docker ps
例如:
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS
a1b2c3d4e5f6 ghcr.io/umami-software/umami:postgresql "docker-entrypoint..." 2 hours ago Up 2 hours 0.0.0.0:3000->3000/tcp
其中:
a1b2c3d4e5f6
就是 Umami 容器 ID。
后面的命令中如果看到:
<umami容器ID>
请替换成你自己的 ID。
提示: 如果你使用 Docker Compose,也可以直接使用容器名称,例如
umami,不一定非要使用容器 ID。
二、为什么官方教程在 Docker 里跑不通?
Umami 是一款非常优秀的自托管网站分析工具,使用 Docker 部署时通常只需要执行:
docker-compose up -d
启动后访问:
http://your-host:3000
即可使用。
但是,当你忘记登录密码,需要通过命令行重置密码时,问题就出现了。
官方文档提供的方案通常类似:
docker exec -it <umami容器ID> npx umami reset-password --username admin
老版本还可以使用:
docker exec -it <umami容器ID> npm run change-password -- admin
然而在实际的 Docker 环境中执行时,很可能会出现:
npm error could not determine executable to run
或者:
> node scripts/change-password.js admin
Error: Cannot find module '/app/scripts/change-password.js'
看到这里可能会很疑惑:
明明是官方提供的命令,为什么执行不了?
原因其实在 Docker 镜像本身。
三、官方 Docker 镜像为什么没有 CLI?
Umami 官方 Docker 镜像:
ghcr.io/umami-software/umami:postgresql-latest
采用 Next.js 的 standalone 模式进行构建。
简单来说,最终 Docker 镜像并不是完整的 Umami 源代码环境,而是只保留运行程序所需要的代码和依赖。
因此最终镜像中可能不存在:
scripts/
目录以及完整的 Umami CLI 工具链。
所以执行:
npx umami reset-password
或者:
npm run change-password
自然就可能无法正常工作。
这并不是命令写错了,而是 Docker 镜像里没有对应的脚本和完整 CLI 环境。
四、终极方案:直接修改 PostgreSQL
既然 CLI 无法使用,那么最简单的方法就是:
绕过 Umami CLI,直接修改 PostgreSQL 中的用户密码。
Umami 本身也是通过 DATABASE_URL 连接 PostgreSQL,所以我们可以直接复用 Umami 容器中的数据库连接信息。
同时,容器中已经包含 Node.js,因此只需要临时安装:
pg:用于连接 PostgreSQLbcryptjs:用于生成密码哈希
然后直接执行 SQL 更新用户密码即可。
前置条件
执行下面的操作之前,请确认:
- Umami 容器正在运行
- Umami 使用 PostgreSQL
- Umami 容器中存在
DATABASE_URL环境变量 - 你知道需要修改的用户名
先执行:
docker ps
找到 Umami 容器 ID。
例如:
a1b2c3d4e5f6
后续命令中的:
<umami容器ID>
全部替换成这个 ID。
五、方案 A:修改指定用户密码
这是最推荐的方法。
将:
<umami容器ID>
替换成你的 Umami 容器 ID。
将:
<新密码>
替换成你想设置的新密码。
将:
<用户名>
替换成你的 Umami 用户名。
命令:
docker exec -i <umami容器ID> sh -c 'cd /tmp && DATABASE_URL="$DATABASE_URL" NODE_PATH=/tmp/umami-fix/node_modules node -e "const{Client}=require(\"pg\");const bcrypt=require(\"bcryptjs\");(async()=>{const c=new Client({connectionString:process.env.DATABASE_URL});await c.connect();const hash=await bcrypt.hash(process.argv[1],10);const r=await c.query(\"UPDATE \\\"user\\\" SET password=\$1 WHERE username=\$2\",[hash,process.argv[2]]);console.log(\"updated\",r.rowCount,\"row(s)\");await c.end();})().catch(e=>{console.error(e.message);process.exit(1);})" <新密码> <用户名>'
例如:
docker exec -i a1b2c3d4e5f6 sh -c 'cd /tmp && DATABASE_URL="$DATABASE_URL" NODE_PATH=/tmp/umami-fix/node_modules node -e "const{Client}=require(\"pg\");const bcrypt=require(\"bcryptjs\");(async()=>{const c=new Client({connectionString:process.env.DATABASE_URL});await c.connect();const hash=await bcrypt.hash(process.argv[1],10);const r=await c.query(\"UPDATE \\\"user\\\" SET password=\$1 WHERE username=\$2\",[hash,process.argv[2]]);console.log(\"updated\",r.rowCount,\"row(s)\");await c.end();})().catch(e=>{console.error(e.message);process.exit(1);})" MyNewPassword123! admin'
如果看到:
updated 1 row(s)
说明成功修改了 1 个用户的密码。
六、方案 B:不知道用户名,先查询用户
如果你不知道 Umami 的用户名,可以先查询数据库。
同样需要先找到 Umami 容器 ID:
docker ps
然后执行:
docker exec -i <umami容器ID> sh -c 'cd /tmp && DATABASE_URL="$DATABASE_URL" NODE_PATH=/tmp/umami-fix/node_modules node -e "const{Client}=require(\"pg\");(async()=>{const c=new Client({connectionString:process.env.DATABASE_URL});await c.connect();const r=await c.query(\"SELECT * FROM \\\"user\\\"\");console.log(JSON.stringify(r.rows,null,2));await c.end();})().catch(e=>{console.error(e.message);process.exit(1);})"'
执行后会以 JSON 格式列出用户信息。
例如可以看到:
user_id
username
role
created_at
updated_at
找到正确的:
username
然后使用方案 A修改密码。
七、方案 C:所有用户统一修改密码
如果你的 Umami 是个人使用的小型站点,而且数据库中只有一个用户,也可以直接修改所有用户的密码。
⚠️ 注意:此方法会修改所有用户的密码,团队站点不要轻易使用。
docker exec -i <umami容器ID> sh -c 'cd /tmp && DATABASE_URL="$DATABASE_URL" NODE_PATH=/tmp/umami-fix/node_modules node -e "const{Client}=require(\"pg\");const bcrypt=require(\"bcryptjs\");(async()=>{const c=new Client({connectionString:process.env.DATABASE_URL});await c.connect();const hash=await bcrypt.hash(process.argv[1],10);const r=await c.query(\"UPDATE \\\"user\\\" SET password=\$1\",[hash]);console.log(\"updated\",r.rowCount,\"row(s)\");await c.end();})().catch(e=>{console.error(e.message);process.exit(1);})" <新密码>'
例如:
docker exec -i a1b2c3d4e5f6 sh -c 'cd /tmp && DATABASE_URL="$DATABASE_URL" NODE_PATH=/tmp/umami-fix/node_modules node -e "const{Client}=require(\"pg\");const bcrypt=require(\"bcryptjs\");(async()=>{const c=new Client({connectionString:process.env.DATABASE_URL});await c.connect();const hash=await bcrypt.hash(process.argv[1],10);const r=await c.query(\"UPDATE \\\"user\\\" SET password=\$1\",[hash]);console.log(\"updated\",r.rowCount,\"row(s)\");await c.end();})().catch(e=>{console.error(e.message);process.exit(1);})" MyNewPassword123!'
执行后,数据库中的所有用户密码都会被修改。
八、命令到底做了什么?
虽然上面的命令看起来很长,但实际上逻辑并不复杂。
| 命令部分 | 作用 |
|---|---|
docker exec -i <umami容器ID> | 在 Umami 容器内部执行命令 |
npm i bcryptjs pg | 临时安装密码哈希和 PostgreSQL 驱动 |
DATABASE_URL="$DATABASE_URL" | 复用 Umami 容器已有的数据库连接信息 |
NODE_PATH=/tmp/umami-fix/node_modules | 告诉 Node.js 临时依赖安装在哪里 |
node -e "..." | 直接执行内联 JavaScript |
bcrypt.hash() | 对新密码进行哈希 |
UPDATE "user" | 更新 Umami 用户密码 |
rowCount | 输出实际修改了多少条数据 |
整体流程就是:
获取 DATABASE_URL
↓
连接 PostgreSQL
↓
bcrypt 生成密码哈希
↓
UPDATE "user"
↓
修改 password
↓
输出更新数量
九、两个非常容易踩的坑
1. Umami v2 使用的是 user 表
一些旧版本资料中可能会看到:
account
但是 Umami v2 使用的是:
"user"
如果使用:
UPDATE account SET ...
可能会出现:
relation "account" does not exist
正确写法:
UPDATE "user" SET password = ...
而不是:
UPDATE user SET password = ...
因为 user 属于 SQL 中的特殊关键字,所以这里需要使用双引号。
2. 用户权限字段是 role
Umami v2 中用户角色字段使用:
role
而不是:
is_admin
例如:
SELECT user_id, username, role FROM "user";
如果按照旧教程使用:
SELECT user_id, username, is_admin FROM "user";
就可能出现:
column "is_admin" does not exist
十、操作完成后记得做这三件事
1. 把密码保存到密码管理器
可以使用:
- 1Password
- Bitwarden
- KeePass
不要再把密码直接记录在文本文件或者聊天记录中。
2. 登录后立即修改为强密码
如果使用简单密码进行测试,登录成功后一定要立即修改。
推荐使用包含:
大小写字母 + 数字 + 特殊符号
的强密码。
3. 删除临时依赖
上面的操作会将临时依赖安装到:
/tmp/umami-fix
如果希望手动清理,可以执行:
docker exec -i <umami容器ID> rm -rf /tmp/umami-fix
十一、不同方案对比
| 方案 | 推荐程度 | 适用场景 | 缺点 |
|---|---|---|---|
| Docker 内 Node 直接修改 PostgreSQL | ⭐⭐⭐⭐⭐ | Docker 部署,最通用 | 需要容器能够安装 npm 包 |
宿主机 psql + bcryptjs | ⭐⭐⭐⭐ | 容器无法安装 npm | 宿主机需要准备相关环境 |
| SQLite 直接修改数据库 | ⭐⭐⭐ | SQLite 部署 | 需要直接操作 .db 文件 |
| 自定义 Umami 镜像 | ⭐⭐ | 长期维护、经常重置密码 | 构建和维护成本较高 |
| 删除容器重新初始化 | ⭐ | 数据完全不重要 | 可能导致历史分析数据丢失 |
十二、FAQ
user 表一定要加双引号吗?
是。
因为 user 属于 SQL 中的特殊关键字,因此建议始终写成:
"user"
例如:
UPDATE "user" SET password = ...
可以使用宿主机的 psql 吗?
可以。
如果 Umami 容器无法安装 npm 包,可以在宿主机操作。
Debian / Ubuntu:
apt install -y postgresql-client
CentOS / OpenCloudOS / RHEL:
yum install -y postgresql
然后获取 Umami 的数据库连接信息:
docker exec <umami容器ID> printenv DATABASE_URL
生成密码哈希:
npm i bcryptjs && node -e "console.log(require('bcryptjs').hashSync('123456', 10))"
最后通过 psql 更新数据库:
PGPASSWORD='xxx' psql -h 10.x.x.x -U umami -d umami \
-c "UPDATE \"user\" SET password='<hash>' WHERE username='admin'"
使用 npx umami@latest 能不能解决?
不能简单地解决这个问题。
例如:
npx umami@latest
虽然 npx 可以尝试下载 Umami 包,但 Docker 容器中并不一定存在完整的 Umami 运行环境和相关依赖。
本质问题依然是:
Docker 镜像并不是完整的 Umami 源代码 + CLI 开发环境。
挂载完整 Umami 源码能不能解决?
可以。
你可以将完整 Umami 源码挂载进容器,然后重新安装依赖,再执行相关脚本。
但是,如果只是为了偶尔重置一次密码,这样做实际上有些过于复杂。
直接修改数据库通常更加简单。
十三、SQLite 部署怎么办?
如果使用的是 SQLite,那么处理方式有所不同。
SQLite 数据库通常位于:
./umami.db
或者 Docker volume 对应的目录。
首先生成密码哈希:
node -e "console.log(require('bcryptjs').hashSync('123456', 10))"
然后使用:
sqlite3 /path/to/umami.db "UPDATE account SET password='<hash>' WHERE username='admin'"
SQLite 使用的是旧版 schema 中的:
account
因此不要直接把 PostgreSQL 的:
"user"
方案照搬过去。
总结
如果你使用 Docker 部署 Umami,并且忘记了管理员密码,不需要删除容器,更不需要重新初始化数据库。
核心思路就是:
绕过无法使用的 CLI,直接使用 Umami 的
DATABASE_URL连接 PostgreSQL,然后通过 bcrypt 生成密码哈希并更新"user"表。
本文最重要的几个结论:
- Docker 容器 ID 每个人都不同,不要直接复制别人的容器 ID。
- 使用
docker ps可以快速找到自己的 Umami 容器 ID。 - 本文所有
<umami容器ID>都需要替换成自己的实际容器 ID。 - Umami Docker 镜像采用 Next.js standalone 构建,并不是完整的开发环境。
npx umami reset-password或npm run change-password可能无法正常使用。- PostgreSQL 部署的 Umami v2 使用
"user"表。 "user"是特殊关键字,SQL 中需要使用双引号。- 用户权限字段是
role,不是is_admin。 - 最直接的方法是使用 Node.js +
pg+bcryptjs直接修改 PostgreSQL。 - 修改完成后建议立即使用密码管理器保存密码,并更换为强密码。
如果这篇文章帮你解决了 Umami 密码重置问题,也欢迎在评论区分享你的部署方式,例如 Docker、源码部署或 Kubernetes,以及你在部署过程中遇到的其他问题。