Grist 1.7.21 安装部署指南

grist-core v1.7.21 在 Ubuntu 24.04 服务器上的生产化部署全程实测:Node 22.12/Python 3.11 依赖准备、源码构建(yarn install/install:python/build 全回显)、/boot 引导与 go-live、systemd 托管、nginx 反代、离线包方案与 6 类排障速查;全部命令与回显来自 2026-10-28 真实部署。

GristGrist电子表格2026/10/28

Grist 1.7.21 安装部署指南

目标:在一台 Ubuntu 24.04 服务器上完成 grist-core v1.7.21 的生产化部署——从系统依赖、源码构建、Python 公式沙箱、首次引导(/boot)、systemd 服务托管,到 nginx 反向代理与验收,并附一套内网环境可用的离线包安装方案(方案B)。本文全部命令与回显均来自 2026-10-28 的真实部署实测(服务器 hostname grist-lab);产品背景与选型结论见 《Grist 1.7.21 介绍与选型指南》,逐项配置参数见 《Grist 1.7.21 配置参数详解》,日常运维与备份升级见 《Grist 1.7.21 常用运维命令与 API 实操详解》。


一、部署形态总览

grist-core 在裸机服务器上的生产形态由四层组成:

层组件说明
服务进程Node.js 22 + Express(sandbox/run.sh 启动)Web/UI/REST API/WebSocket 协同
公式引擎Python 3.11 沙箱(sandbox_venv3)执行 Python 公式,构建期随源码准备
元数据库SQLite(默认,landing.db)或 PostgreSQL(TYPEORM_* 切换)用户/组织/工作区/文档元数据
收口层systemd(grist.service)+ nginx(80 → 127.0.0.1:3000)开机自启、崩溃拉起、同源反代与 WebSocket 升级

本文给出两条安装路径:方案A 源码构建(主线,全程实测)与方案B 离线包(内网无公网环境,实测打包 235 MB、部署后服务正常拉起)。

二、环境准备

实测机基线:Ubuntu 24.04 (noble),2 核 4G 起步即可完成构建(node_modules 构建后约 724 MB,yarn install 全程约 7 分钟)。

2.1 安装 Node.js 22.12.0

grist-core v1.7.21 的 .nvmrc 锁定 v22.12.0,用官方 tarball 安装到 /usr/local/lib/nodejs 并链入 PATH:

cd /usr/local/lib && tar xzf node-v22.12.0-linux-x64.tar.xz
ln -sf /usr/local/lib/nodejs/node-v22.12.0-linux-x64/bin/node /usr/local/bin/node
ln -sf /usr/local/lib/nodejs/node-v22.12.0-linux-x64/bin/npm  /usr/local/bin/npm

2.2 安装 yarn 与 git

npm i -g yarn
apt install -y git

2.3 安装 Python 3.11(公式沙箱运行时)

Grist 公式引擎的官方运行时是 Python 3.11。Ubuntu 24.04 自带 3.12,从 deadsnakes PPA 装 3.11 并同时装好 venv 支持(后文 3.3 节排障的关键依赖):

add-apt-repository -y ppa:deadsnakes/ppa && apt update
apt install -y python3.11 python3.11-venv

2.4 工具链核对(实测回显)

$ node --version
v22.12.0
$ yarn --version
1.22.22
$ python3.11 --version
Python 3.11.18
$ git --version
git version 2.43.0

三、方案A:源码构建安装

3.1 获取源码并锁定版本

mkdir -p /opt/grist && cd /opt/grist
git clone https://github.com/gristlabs/grist-core.git
cd grist-core && git checkout v1.7.21
$ git describe --tags
v1.7.21

3.2 依赖安装:yarn install(约 7 分钟)

$ cd /opt/grist/grist-core && yarn install
yarn install v1.22.22
[1/4] Resolving packages...
warning Resolution field "@gristlabs/sqlite3@6.0.1-grist.1" is incompatible with requested version "@gristlabs/sqlite3@^5.1.4-grist.7"
[2/4] Fetching packages...
[3/4] Linking dependencies...
warning "@storybook/addon-essentials > @storybook/addon-docs > @mdx-js/react@3.1.1" has unmet peer dependency "@types/react@>=16".
[4/4] Building fresh packages...
...
+ Full edition ready (ext/ and ext/node_modules).
Done in 421.89s.

两点说明:

  • @gristlabs/sqlite3@6.0.1-grist.1 的解析告警是官方锁定版本(Grist 维护的 SQLite 分叉),不影响构建,无需处理;
  • 默认会拉取 full edition(ext/ 扩展目录)。若网络受限导致该步骤失败,可设 GRIST_SKIP_EXT_AUTOSETUP=1 跳过,仅损失部分可选扩展能力。

3.3 Python 公式沙箱:yarn install:python(含排障实录)

$ yarn install:python

第一次失败实录(系统默认 python3 是 3.12,且未装 venv 支持):

Making Python3 sandbox
The virtual environment was not created successfully because ensurepip is not
available.  On Debian/Ubuntu systems, you need to install the python3-venv
package using the following command.
    apt install python3.12-venv
...
error Command failed with exit code 1.
EXIT=1

原因与处理:构建脚本 buildtools/prepare_python.sh 用 PATH 里的 python3 建 venv,而 Grist 官方运行时是 3.11。装 python3.11-venv(2.3 节已装),再做一个 PATH 垫片让 python3 优先解析到 3.11:

mkdir -p /opt/grist/py311bin && ln -sf /usr/bin/python3.11 /opt/grist/py311bin/python3

重试成功:

$ PATH=/opt/grist/py311bin:$PATH yarn install:python
Python3 packages ready in sandbox_venv3
Done in 54.16s.
EXIT=0

3.4 构建:yarn build

$ PATH=/opt/grist/py311bin:$PATH yarn build
webpack 5.104.1 compiled with 3 warnings in 435 ms
Done in 76.97s.
EXIT=0

产物在 _build/,服务端入口为 _build/stubs/app/server/server.js(由 sandbox/run.sh 拉起)。

3.5 目录规划

mkdir -p /opt/grist/grist-docs     # 文档存储目录(GRIST_DATA_DIR)

最终布局:/opt/grist/grist-core(源码与构建产物)、/opt/grist/grist-docs(每个文档一个 SQLite 单文件)、/opt/grist/grist-core/landing.db(元数据库,首次启动自动生成)。

四、首次启动与 /boot 引导

4.1 手动启动验证

$ cd /opt/grist/grist-core
$ env PORT=3000 GRIST_HOST=127.0.0.1 GRIST_SERVE_SAME_ORIGIN=true \
    GRIST_SANDBOX_FLAVOR=unsandboxed GRIST_DATA_DIR=/opt/grist/grist-docs \
    GRIST_DEFAULT_EMAIL=admin@example.com GRIST_BOOT_KEY=grist-boot-2026 \
    bash sandbox/run.sh
2026-10-28 07:41:48.800 - info: == grist.log.level: info [default]
2026-10-28 07:41:48.800 - info: == grist.login.active: minimal
...
2026-10-28 07:41:48.800 - info: FlexServer is ready
Grist is available at http://localhost:3000

此时匿名访问首页会被引导到初始化门禁:

$ curl -s -o /dev/null -w '%{http_code} -> %{redirect_url}\n' http://127.0.0.1:3000/
302 -> http://127.0.0.1:3000/boot

4.2 /boot 三步引导(实测)

# 1) 校验 boot key
$ curl -s -X POST http://127.0.0.1:3000/boot/verify-boot-key \
    -H 'Content-Type: application/json' -d '{"bootKey":"grist-boot-2026"}'
{"adminEmail":null}
# 2) 用 boot key 登记 site admin 并登录
$ curl -s -o /dev/null -w '%{http_code}\n' -X POST http://127.0.0.1:3000/boot/login \
    -H 'Content-Type: application/json' \
    -d '{"bootKey":"grist-boot-2026","adminEmail":"admin@example.com"}'
204
# 3) 首页恢复
$ curl -s -o /dev/null -w '%{http_code} -> %{redirect_url}\n' http://127.0.0.1:3000/
302 -> http://127.0.0.1:3000/o/docs/

排障点:若启动时没有设置 GRIST_BOOT_KEY,服务会自动生成随机 key 并打印到日志,从日志里取用即可:

│   BOOT KEY: 92ea60b17cc373823dd78a78   │
│   Use this key at /boot to sign in.    │

4.3 登录系统认知(重要)

grist-core 内置的登录方式共五种:getgrist.com 账号 / OIDC / SAML / ForwardAuth / minimal。默认的 minimal 模式没有密码:所有访客自动以 GRIST_DEFAULT_EMAIL(本例 admin@example.com)身份登录。Grist 没有内置的用户名密码登录——生产环境请通过 OIDC/SAML 对接企业 IdP,或在反向代理层做 ForwardAuth,这是选型与安全评审时的关键结论。

4.4 上线(go-live):清除重启引导

引导完成后,若直接重启服务,/boot 门禁会再次出现。需要在引导态调用安装偏好接口把服务标记为"已在服务中"(该状态持久化到元数据库,重启后不再出现门禁):

$ curl -s -o /dev/null -w '%{http_code}\n' -X PATCH http://127.0.0.1:3000/api/install/prefs \
    -H 'Content-Type: application/json' \
    -d '{"onRestartClearSessions":true,"envVars":{"GRIST_IN_SERVICE":"true"}}'
200

五、systemd 托管与 nginx 反向代理

5.1 环境变量文件 /etc/grist.env

把 4.1 节的手动变量固化为 EnvironmentFile(实测文件内容,GRIST_SESSION_SECRET 请替换为自己的随机串):

PORT=3000
GRIST_HOST=127.0.0.1
GRIST_SERVE_SAME_ORIGIN=true
GRIST_SANDBOX_FLAVOR=unsandboxed
GRIST_DATA_DIR=/opt/grist/grist-docs
GRIST_DEFAULT_EMAIL=admin@example.com
GRIST_SESSION_SECRET=Xk9vPq2mRt7sWb1nJd4hYf6gLz0cEa8u

5.2 服务单元 /etc/systemd/system/grist.service

[Unit]
Description=Grist collaborative spreadsheet (grist-core v1.7.21)
After=network.target

[Service]
Type=simple
WorkingDirectory=/opt/grist/grist-core
EnvironmentFile=/etc/grist.env
ExecStart=/bin/bash sandbox/run.sh
Restart=on-failure
RestartSec=5
StandardOutput=append:/opt/grist/grist-docs/grist-service.log
StandardError=append:/opt/grist/grist-docs/grist-service.log

[Install]
WantedBy=multi-user.target
systemctl daemon-reload && systemctl enable --now grist

5.3 nginx 反向代理(80 → 127.0.0.1:3000)

/etc/nginx/sites-available/grist(软链到 sites-enabled),实测配置全文:

server {
    listen 80 default_server;
    server_name _;

    # Grist 导入/上传附件可能较大,放开请求体限制
    client_max_body_size 50m;

    location / {
        proxy_pass http://127.0.0.1:3000;
        proxy_http_version 1.1;
        proxy_set_header Host $host;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
        proxy_read_timeout 3600s;
        proxy_send_timeout 3600s;
    }
}

三个不可省略的要点:Upgrade/Connection 头是 WebSocket 协同的命脉;proxy_read_timeout 3600s 避免长连接空闲被掐;client_max_body_size 50m 保证大文件导入不被 nginx 先拦(该限制与 Grist 自身的 GRIST_MAX_UPLOAD_IMPORT_MB 是两道独立的门,联动分析见 《Grist 1.7.21 配置参数详解》)。

systemctl restart nginx

六、部署验收(实测)

# 3000 直连与 80 反代双通过
$ curl -s -o /dev/null -w 'direct3000=%{http_code} -> %{redirect_url}\n' http://127.0.0.1:3000/
direct3000=302 -> http://127.0.0.1:3000/o/docs/
$ curl -s -o /dev/null -w 'nginx80=%{http_code}\n' http://127.0.0.1/
nginx80=302
$ curl -s http://127.0.0.1/ | grep -o '<title>[^<]*</title>'
<title>Loading... - Grist</title>

服务状态:

$ systemctl is-active grist nginx
active
active

浏览器打开 http://<服务器IP>/ 即进入 Grist 工作区,首次以 GRIST_DEFAULT_EMAIL 的身份(minimal 模式自动登录)创建第一个文档。

七、方案B:离线包安装(内网环境实测)

适用于目标服务器无公网的场景:在联网机上完成方案A 的构建后整体打包,离线机解包即用。

1)联网机上打包(含 node_modules、_build、sandbox_venv3、ext/):

$ cd /opt/grist && tar czf grist-offline-v1.7.21.tar.gz grist-core
$ ls -l grist-offline-v1.7.21.tar.gz
-rw-r--r-- 1 root root 235271947 Oct 28 16:20 grist-offline-v1.7.21.tar.gz

2)离线机解包并清掉随包元数据库(避免把打包机的用户数据带过去):

mkdir -p /opt/grist/grist-offline
tar xzf grist-offline-v1.7.21.tar.gz -C /opt/grist/grist-offline
rm -f /opt/grist/grist-offline/grist-core/landing.db
mkdir -p /opt/grist/grist-docs-offline

3)环境文件 /etc/grist-offline.env 与服务单元 /etc/systemd/system/grist-offline.service(与方案A 同构,仅换端口与数据目录):

# /etc/grist-offline.env
PORT=3200
GRIST_HOST=127.0.0.1
GRIST_SERVE_SAME_ORIGIN=true
GRIST_SANDBOX_FLAVOR=unsandboxed
GRIST_DATA_DIR=/opt/grist/grist-docs-offline
GRIST_DEFAULT_EMAIL=admin@example.com
GRIST_SESSION_SECRET=Off8sQ4mRt0xWb3nJd6hYk2gLz5cEa7u
[Unit]
Description=Grist v1.7.21 offline-package instance
After=network.target

[Service]
Type=simple
WorkingDirectory=/opt/grist/grist-offline/grist-core
EnvironmentFile=/etc/grist-offline.env
ExecStart=/bin/bash sandbox/run.sh
Restart=on-failure
RestartSec=5

[Install]
WantedBy=multi-user.target

4)拉起并验收(实测回显):

$ systemctl enable --now grist-offline
$ systemctl is-active grist-offline
active
$ curl -s -o /dev/null -w 'offline3200=%{http_code}\n' http://127.0.0.1:3200/
offline3200=302

离线实例与主实例可并存(不同端口、不同数据目录),首次访问同样走 /boot 引导,流程同 4.2 节。

八、元数据库切 PostgreSQL:注意"全新安装态"

默认元数据库是 SQLite(landing.db)。通过 TypeORM 一组变量切换 PostgreSQL(多实例/专业备份体系建议),在 /etc/grist.env 追加一组 TYPEORM_* 变量后重启即可自动建表。一个实测确认的关键行为:切换元数据库等于全新安装——此前存在 SQLite 里的站点激活状态(/boot 门禁开关)不会跟随迁移,重启后会重现 /boot 引导,需要重新走一遍 4.2/4.4 节;同时 SQLite 时代的文档元数据不会出现在新库的 docs 表中(.grist 文件本身仍在数据目录,可另行恢复)。完整的切库参数、22 张自动建表清单与回滚方法在 《Grist 1.7.21 配置参数详解》中逐项展开。

九、常见问题排障速查

现象原因处理
yarn install:python 报 ensurepip is not available默认 python3 是 3.12 且缺 venv 包apt install python3.11-venv,用 3.11 PATH 垫片重跑(见 3.3 节)
warning Resolution field "@gristlabs/sqlite3@6.0.1-grist.1" is incompatible...官方锁定的 SQLite 分叉版本已知告警,不影响构建,忽略
重启后 /boot 门禁反复出现未做 go-live 持久化引导后 PATCH /api/install/prefs(见 4.4 节)
找不到 boot key未设 GRIST_BOOT_KEY从启动日志的 BOOT KEY 框取自动生成的 key
ext/ 扩展下载失败公网受限设 GRIST_SKIP_EXT_AUTOSETUP=1 后重跑 yarn install
nginx 直接返回 413请求体超 nginx 限制调大 client_max_body_size(与 Grist 侧上传限额是两道门)

十、系列导航