SpacetimeDB 自托管实战指南:Ubuntu 24.04 上的 Nginx + Let's Encrypt + systemd 生产部署
【免费下载链接】SpacetimeDBDevelopment at the speed of light项目地址: https://gitcode.com/GitHub_Trending/sp/SpacetimeDB
本指南以 SpacetimeDB 官方自托管文档为主体,完整演示如何在一台全新的 Ubuntu 24.04 服务器上部署 SpacetimeDB:创建专用系统用户安装运行时、用 systemd 托管服务进程、通过 Nginx 反向代理暴露 WebSocket/HTTP 接口并配置 HTTPS 证书、在默认"封闭"策略下安全地向远程实例发布模块,以及如何升级版本与排查故障。读完本文,你将具备一套可直接复制的生产级自托管方案,并理解每一步背后的源码级原理。
前置条件
开始之前,你需要准备:
- 一台全新的 Ubuntu 24.04 服务器(VM 或任意云厂商实例);
- 一个已解析到该服务器 IP 的域名(例如
example.com); - 服务器上的
sudo权限。
整个部署流程涉及七个步骤:创建专用用户 → 配置 systemd 服务 → 安装并配置 Nginx → 用 Let's Encrypt 加固 HTTPS → 验证并配置 CLI 连接 → 升级版本 → 故障排查。
第一步:为 SpacetimeDB 创建专用系统用户
出于安全考虑,建议为 SpacetimeDB 创建一个专用的系统用户spacetimedb,让服务进程以最小权限运行,避免直接使用 root 或普通高权限账号:
sudo mkdir /stdb sudo useradd --system spacetimedb sudo chown -R spacetimedb:spacetimedb /stdb/stdb将作为 SpacetimeDB 的专属数据与安装根目录;useradd --system创建的是系统账户(无登录 shell、UID 落在系统范围内),适合守护进程场景;- 将
/stdb的所有权交给spacetimedb用户,后续安装与运行都无需 root 写权限。
然后以该用户身份执行官方安装脚本,并指定根目录与自动确认:
sudo -u spacetimedb bash -c 'curl -sSf https://install.spacetimedb.com | sh -s -- --root-dir /stdb --yes'--root-dir /stdb会把 CLI 二进制、版本目录与数据目录统一收敛到/stdb之下;--yes跳过交互确认,适合脚本化安装。从源码看,--root-dir是 CLI 与自更新工具共用的路径锚点:spacetimedb-update的所有子命令都接受该参数,并通过SpacetimePaths::from_root_dir派生出 bin 目录与数据目录。
第二步:创建 systemd 服务
为了让 SpacetimeDB 在开机时自动运行并在崩溃后自动拉起,需要编写一个 systemd unit 文件:
sudo nano /etc/systemd/system/spacetimedb.service写入如下内容:
[Unit] Description=SpacetimeDB Server After=network.target [Service] ExecStart=/stdb/spacetime --root-dir=/stdb start --listen-addr='127.0.0.1:3000' Restart=always User=spacetimedb WorkingDirectory=/stdb [Install] WantedBy=multi-user.target逐项说明:
ExecStart:以/stdb/spacetime启动服务。spacetime start只是 CLI 的转发入口,实际会 exec 真正的spacetimedb-standalone start(见 crates/cli/src/subcommands/start.rs),并把--data-dir与 JWT 密钥目录一并传下去;--listen-addr='127.0.0.1:3000':仅监听本机回环地址。standalone 的--listen-addr(-l)默认值是0.0.0.0:3000(监听所有网卡,见 crates/standalone/src/subcommands/start.rs),自托管时通常让 Nginx 独占公网入口,因此这里显式收窄到回环地址,避免服务直接暴露在公网;Restart=always:进程异常退出时由 systemd 自动重启;User=spacetimedb:以第一步创建的专用用户运行;WantedBy=multi-user.target:开机自启。
如果你希望把监听地址做成持久默认值,也可以在 CLI 配置文件(cli.toml)中写入listen_addr = "0.0.0.0:4000"。当配置存在时,Config::start_listen_addr会将其作为默认值注入,显式传入的--listen-addr优先(见 crates/cli/src/subcommands/start.rs 的注释说明)。
启用并启动服务:
sudo systemctl enable spacetimedb sudo systemctl start spacetimedb检查服务状态:
sudo systemctl status spacetimedb第三步:安装并配置 Nginx 反向代理
Nginx 负责把公网 80/443 端口的流量转发给回环地址上的 SpacetimeDB(默认 3000 端口),并为后续的 Let's Encrypt 证书提供承载。
安装 Nginx
sudo apt update sudo apt install nginx -y配置反向代理与路由白名单
创建站点配置文件:
sudo nano /etc/nginx/sites-available/spacetimedb写入以下内容(请将example.com替换为你自己的域名):
server { listen 80; server_name example.com; ######################################### # By default SpacetimeDB is completely open so that anyone can publish to it. If you want to block # users from creating new databases you should keep this section commented out. Otherwise, if you # want to open it up (probably for dev environments) then you can uncomment this section and then # also comment out the location / section below. ######################################### # location / { # proxy_pass http://localhost:3000; # proxy_http_version 1.1; # proxy_set_header Upgrade $http_upgrade; # proxy_set_header Connection "Upgrade"; # proxy_set_header Host $host; # } # Anyone can subscribe to any database. # Note: This is the only section *required* for the websocket to function properly. Clients will # be able to create identities, call reducers, and subscribe to tables through this websocket. location ~ ^/v1/database/[^/]+/subscribe$ { proxy_pass http://localhost:3000; proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection "Upgrade"; proxy_set_header Host $host; } # Uncomment this section to allow all HTTP reducer calls # location ~ ^/v1/[^/]+/call/[^/]+$ { # proxy_pass http://localhost:3000; # proxy_http_version 1.1; # proxy_set_header Upgrade $http_upgrade; # proxy_set_header Connection "Upgrade"; # proxy_set_header Host $host; # } # Uncomment this section to allow all HTTP sql requests # location ~ ^/v1/[^/]+/sql$ { # proxy_pass http://localhost:3000; # proxy_http_version 1.1; # proxy_set_header Upgrade $http_upgrade; # proxy_set_header Connection "Upgrade"; # proxy_set_header Host $host; # } # NOTE: This is required for the typescript sdk to function, it is optional # for the rust and the C# SDKs. location /v1/identity { proxy_pass http://localhost:3000; proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection "Upgrade"; proxy_set_header Host $host; } # Block all other routes explicitly. Only localhost can use these routes. If you want to open your # server up so that anyone can publish to it you should comment this section out. location / { allow 127.0.0.1; deny all; } }这段配置默认只放行两类路由,其余一律只允许本机访问:
location ~ ^/v1/database/[^/]+/subscribe$:数据库订阅 WebSocket 端点,客户端通过它创建身份、调用 reducer 并订阅表变更——这是 WebSocket 正常工作的唯一必需放行项。该端点对应源码路由GET /database/:name_or_identity/subscribe(见 crates/client-api/src/routes/database.rs);location /v1/identity:身份管理端点(POST /identity创建身份等,见 crates/client-api/src/routes/identity.rs),TypeScript SDK 依赖它,Rust 与 C# SDK 则属于可选;- 被注释掉的
^/v1/[^/]+/call/[^/]+$(HTTP reducer 调用,见 crates/client-api/src/routes/database.rs)与^/v1/[^/]+/sql$(HTTP SQL 请求,见 crates/client-api/src/routes/database.rs)默认不对外; - 兜底的
location /用allow 127.0.0.1; deny all;拒绝一切远端请求,从而阻止所有远程用户向你的实例发布数据库。
理解这段配置的关键在于路由结构:所有客户端路由都挂在/v1前缀之下(见 crates/client-api/src/routes/mod.rs),而健康检查/探活端点则是/v1/ping(见 crates/client-api/src/routes/mod.rs)。WebSocket 与 HTTP 调用共用同一套路径,因此需要Upgrade/Connection头转发来支持协议升级。
启用站点并重启 Nginx:
sudo ln -s /etc/nginx/sites-available/spacetimedb /etc/nginx/sites-enabled/ sudo systemctl restart nginx配置防火墙
确保防火墙放行 HTTPS(与 HTTP)流量:
sudo ufw allow 'Nginx Full' sudo ufw reloadNginx Full是 UFW 内置的规则,同时放行 80 与 443 端口。若 UFW 未启用,可以sudo ufw enable后再执行上述命令。
第四步:使用 Let's Encrypt 加固 HTTPS
安装 Certbot
sudo apt install certbot python3-certbot-nginx -ypython3-certbot-nginx是 Nginx 插件,允许 Certbot 自动改写 Nginx 配置并完成证书部署。
申请 SSL 证书
将example.com替换为你自己的域名后执行:
sudo certbot --nginx -d example.comCertbot 会通过 HTTP-01 挑战验证域名所有权,自动为 Nginx 配置 SSL 并重载服务。完成后重启 Nginx 以确认配置生效:
sudo systemctl restart nginx自动续期
Certbot 在安装时会自动注册一个 systemd 定时器certbot.timer。验证其处于活动状态:
sudo systemctl status certbot.timer证书有效期接近届满时,定时器会自动执行续期并重载 Nginx,无需人工干预。
第五步:验证安装并连接 CLI
在你的本地开发机上,把新服务器加入 CLI 的服务器配置(替换example.com):
spacetime server add self-hosted --url https://example.com从源码看,spacetime server add支持多个参数(见 crates/cli/src/subcommands/server.rs):
--url(必填):服务器 URL,支持https://example.com形式,也接受host:port写法;源码中会自动去除末尾/并解析出 host 与协议(https/http);self-hosted:为该服务器配置起一个昵称,后续所有命令可用昵称引用;-d/--default:将新服务器设为默认服务器;--no-fingerprint:跳过服务器指纹校验。
spacetime server add默认会向服务器请求并保存"指纹"(fingerprint),这是防止中间人攻击的信任锚点;如果服务器尚未就绪,可以先用--no-fingerprint跳过,稍后通过spacetime server fingerprint <name>补录。CLI 还提供配套的服务器管理子命令(同见 crates/cli/src/subcommands/server.rs):
spacetime server list:列出所有已保存的服务器配置及默认标记;spacetime server set-default <name>:切换默认服务器;spacetime server ping <name>:探测服务器在线状态(请求/v1/ping,见 crates/cli/src/subcommands/server.rs);spacetime server edit --new-name ... --url ...:修改昵称或 URL;spacetime server remove <name>:删除配置。
受限模式下的发布流程
由于第三步的 Nginx 配置默认封死了/路由,远端用户无法直接通过 HTTP 发布数据库。此时推荐的做法是:在本机构建模块,把编译产物拷贝到服务器主机上,再通过本机通道发布。例如:
spacetime build scp target/wasm32-unknown-unknown/release/spacetime_module.wasm ubuntu@<host>:/home/ubuntu/ ssh ubuntu@<host> spacetime publish -s local --bin-path spacetime_module.wasm <database-name>命令解析(对应 crates/cli/src/subcommands/publish.rs):
spacetime build:在本地把模块编译为 WASM(Rust 模块产物默认位于target/wasm32-unknown-unknown/release/);scp:把.wasm产物上传到服务器;spacetime publish -s local:-s/--server指定服务器(此处为默认的本地服务器,即服务器本机的 CLI 配置,能够直连 127.0.0.1 而不经过被封锁的 Nginx 路由);--bin-path spacetime_module.wasm:-b/--bin-path指示跳过构建、直接发布指定的预编译二进制(与--module-path、--build-options互斥)。
可以把上述命令封装成 shell 脚本,让发布流程更快更稳定;也可以将类似脚本接入 CI(例如 GitHub Actions),在"PR 合入 master"等事件触发时自动发布到服务器。
补充说明:发布到非本机地址时,CLI 会先打印You are about to publish to a non-local server: <host>并要求确认(见 crates/cli/src/subcommands/publish.rs)。在自动化场景下,可以用--yes跳过交互确认,它支持按类别细分:--yes=remote(跳过远程发布确认)、--yes=migrate、--yes=break-clients、--yes=skip-login、--yes=delete-data,或直接--yes(等价于--yes=all)。
第六步:升级 SpacetimeDB 版本
升级前先停止服务,避免运行中的实例被替换:
sudo systemctl stop spacetimedb升级到最新版本:
sudo -u spacetimedb -i -- spacetime --root-dir=/stdb version upgradespacetime version子命令是版本管理的入口,会把请求转发给自更新工具spacetimedb-update(见 crates/cli/src/subcommands/version.rs),后者负责从发布源下载并切换版本。upgrade会获取最新版本、安装并把当前版本指针切换到新版本(见 crates/update/src/cli/upgrade.rs)。
要安装指定版本,使用:
sudo -u spacetimedb -i -- spacetime --root-dir=/stdb install <version-number>install <version>安装指定版本;若加--use参数则在安装后立即切换过去(见 crates/update/src/cli/install.rs)。spacetime version还支持list(列出已安装版本)、uninstall <version>等子命令(见 crates/update/src/cli.rs)。
最后重新启动服务:
sudo systemctl start spacetimedb第七步:故障排查
SpacetimeDB 服务启动失败
查看服务日志定位错误:
sudo journalctl -u spacetimedb --no-pager | tail -20确认spacetimedb用户对可执行文件与数据目录拥有正确权限:
sudo ls -lah /stdb/spacetime如果缺少可执行权限,手动补上:
sudo chmod +x /stdb/spacetimeLet's Encrypt 证书续期异常
手动触发一次续期演练,检查报错:
sudo certbot renew --dry-runNginx 启动失败
先做配置语法自检:
sudo nginx -t发现错误后查看 Nginx 日志:
sudo journalctl -u nginx --no-pager | tail -20生产加固进阶:理解并调优 config.toml
首次以某个数据目录启动时,standalone 会在数据目录下写入一份默认的config.toml(见 crates/standalone/src/subcommands/start.rs),仓库内模板见 crates/standalone/config.toml。自托管场景下值得关注的关键配置段包括:
[module-http] enabled:是否允许数据库模块发起出站 HTTP 请求(覆盖 procedure 与模块 HTTP handler),默认允许;[logs] level/directives:日志级别过滤与 tracing 指令,模板默认开启spacetimedb*系列的 debug 日志,便于运维观察;[wasm] procedure-instance-pool-size:每个数据库的 WASM procedure 实例池上限,缺省按 OS 报告的 CPU 核数决定;[v8]段对应 JS 实例池;[websocket] ping-interval与idle-timeout:WebSocket 心跳 Ping 帧间隔(默认15s)与空闲超时(默认30s,必须大于 ping-interval),慢客户端也会被正确纳入空闲判定;[commitlog]:提交日志段大小、写缓冲、offset 索引与预分配策略等持久化相关参数,直接影响写吞吐与崩溃恢复。
修改config.toml后重启spacetimedb服务即可生效。结合 crates/standalone/config.toml 中的完整注释,你可以根据服务器规格与业务规模(连接数、模块调用频率、数据量)逐步调优。
至此,一台由 systemd 托管、Nginx 反向代理、Let's Encrypt 提供 HTTPS、默认拒绝远端发布的高安全性 SpacetimeDB 自托管实例即告完成,并且具备版本升级与完整排障手段,可作为生产或长期运行环境直接投入使用。
【免费下载链接】SpacetimeDBDevelopment at the speed of light项目地址: https://gitcode.com/GitHub_Trending/sp/SpacetimeDB
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考