Umami 密码重置全攻略:Docker 部署下 npx 跑不通的真正原因与解决方法

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:用于连接 PostgreSQL
  • bcryptjs:用于生成密码哈希

然后直接执行 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-passwordnpm run change-password 可能无法正常使用。
  • PostgreSQL 部署的 Umami v2 使用 "user" 表。
  • "user" 是特殊关键字,SQL 中需要使用双引号。
  • 用户权限字段是 role,不是 is_admin
  • 最直接的方法是使用 Node.js + pg + bcryptjs 直接修改 PostgreSQL。
  • 修改完成后建议立即使用密码管理器保存密码,并更换为强密码。

如果这篇文章帮你解决了 Umami 密码重置问题,也欢迎在评论区分享你的部署方式,例如 Docker、源码部署或 Kubernetes,以及你在部署过程中遇到的其他问题。

上一篇