首页
/ Puter 部署实战指南:从本地开发到 Docker Compose 全栈自托管(基于官方 README 与仓库实现)

Puter 部署实战指南:从本地开发到 Docker Compose 全栈自托管(基于官方 README 与仓库实现)

2026-09-05 11:50:27作者:谭伦延

本文以 Puter 仓库中的多语言入门文档 README.es.md 为骨架,完整覆盖其中的本地开发、Docker 单容器、Docker Compose 与自托管四条上手路径,并结合仓库内的 tools/start.mjsDockerfiledocker-compose.ymldoc/self-hosting.md 等实现文件,补充每条路径背后的真实启动流程、配置键与首启细节。读完本文,你可以独立在本地跑起 Puter 开发环境,也可以在一台 Linux 主机上部署一套含数据库、缓存、KV、对象存储与反向代理的完整自托管栈。

Puter 是什么:五种典型用法

入口文档将 Puter 描述为“一个先进的、开源的、可自托管的互联网操作系统(Internet Computer)”,功能丰富、速度快、高度可扩展。按照 README.es.md 的说法,它可以被用作:

  • 私有个人云:集中存放所有文件、应用与游戏,随时随地从任何设备访问;
  • 发布平台:构建并发布网页、Web 应用与游戏;
  • 云盘替代品:类似 Dropbox / Google Drive / OneDrive 的定位,但界面和功能取向不同;
  • 远程桌面环境:面向服务器与工作站的浏览器内桌面;
  • 开源学习项目:围绕 Web 开发、云计算与分布式系统构建的社区。

对开发者而言,仓库 README.md 进一步列出了平台提供的开发面:AI、云存储、键值数据库与 Serverless Workers,应用构建后可发布到其 App Store。仓库采用 AGPL-3.0 许可证(package.jsonlicense 字段同为 AGPL-3.0-only),除非另有声明,仓库全部内容、子项目与模块均适用该协议;第三方库可能有各自许可。

系统要求

README.es.md 给出的系统要求如下:

项目 要求
操作系统 Linux、macOS、Windows
内存 最低 2GB(推荐 4GB)
存储 1GB 可用空间
Node.js 文档写作时标注 16+(推荐 23+)
npm 最新稳定版

需要注意一个以仓库为准的差异点:当前仓库的 package.json engines 字段声明了 "node": ">=24.0.0",且 Dockerfile 的构建与运行阶段均基于 node:24-slim。也就是说,从源码树本地运行 npm start 时,Node 24 是当前实际要求;西班牙语 README 中的 “16+/23+” 是较早版本留下的表述,建议以仓库清单为准。

路径一:本地开发(npm start)

入口文档给出的命令是:

git clone https://github.com/HeyPuter/puter
cd puter
npm install
npm start

启动后 Puter 运行在 http://puter.localhost:4100(若端口被占用则顺延到下一个可用端口)。

结合仓库源码看,npm start 实际执行的是 tools/start.mjs(由 package.json"start": "node ./tools/start.mjs" 绑定)。在默认模式下它依次做三件事(见 tools/start.mjs):

  1. npm run setupExtensions — 执行 tools/extensionSetup.mjs 准备扩展;
  2. npm run build:ts — 用 tsc -p tsconfig.build.json 编译后端 TypeScript,再由 tools/write-dist-package-json.mjs 写出 dist 的 package.json;
  3. --enable-source-maps 启动编译产物,并预加载 ./dist/src/backend/telemetry.js,最终入口是 ./dist/src/backend/index.js

此外,tools/start.mjs 还支持两个对开发者很有用的模式(这是入口文档没有展开的实现细节):

  • npm start --server=puter.com:跳过本地后端,仅在本机用 src/gui/dev-server.js 起 GUI,对接远程 Puter 后端。裸域名会被解析为 https://api.<domain>,完整 origin 则原样使用;
  • --extensions=<dir>[;<dir>...]:把仓库外的 GUI 扩展目录打进本地服务(多个目录用 ; 分隔),等价于设置 PUTER_GUI_EXTENSION_PATHS。相对路径基于执行 npm start 的目录(INIT_CWD)解析。

入口文档提示:如果 npm start 跑不起来,可查阅自托管排障文档;当前仓库中对应的自托管说明位于 doc/self-hosting.md

路径二:Docker 单容器

入口文档给出的单容器命令(一条式):

mkdir puter && cd puter && mkdir -p puter/config puter/data && sudo chown -R 1000:1000 puter && docker run --rm -p 4100:4100 -v `pwd`/puter/config:/etc/puter -v `pwd`/puter/data:/var/puter ghcr.io/heyputer/puter

同样会得到 http://puter.localhost:4100。这条命令的两个挂载点与 Dockerfile 严格对应,值得从镜像构建层面理解:

  • /etc/puter 是配置注入点Dockerfile 注释说明:自托管者把 config.json 挂载到 /etc/puter/config.json,v2 配置加载器会将其深度合并到内置的 config.default.json 之上,因此部分覆盖(partial override)即可,文件不存在则全部走默认值。运行时由 PUTER_CONFIG_PATH=/etc/puter/config.json 指定(Dockerfile)。
  • /var/puter 是持久数据目录,对应配置中所有指向 /var/puter 的运行时状态。
  • 镜像以非 root 用户运行(USER node),预建 /etc/puter/var/puter 并归属 node:node,这就是命令里 sudo chown -R 1000:1000 的原因——让宿主机目录对 UID 1000 可写。
  • 镜像 EXPOSE 4100,内置 HEALTHCHECK 每 30 秒对容器内 /test 端点做 wget --spider 探测(Dockerfile)。
  • 运行阶段是 node:24-slim 瘦身镜像,不含构建工具链;构建阶段编译后端 TS,并并行构建 GUI 与 puter-js 的 webpack 产物(Dockerfile),生成的 /dist/bundle.min.{js,css}/sdk/puter.js 在 CDN 键未配置时作为本地静态资源兜底。
  • 容器入口命令为 node -r ./dist/src/backend/telemetry.js ./dist/src/backend/index.jsDockerfile),与本地 npm start 的最终启动方式一致。

路径三:Docker Compose 全栈

Linux / macOS

入口文档给出的快速流程:

mkdir -p puter/config puter/data
sudo chown -R 1000:1000 puter
wget https://raw.githubusercontent.com/HeyPuter/puter/main/docker-compose.yml
docker compose up

Windows (PowerShell)

mkdir -p puter
cd puter
New-Item -Path "puter\config" -ItemType Directory -Force
New-Item -Path "puter\data" -ItemType Directory -Force
Invoke-WebRequest -Uri "https://raw.githubusercontent.com/HeyPuter/puter/main/docker-compose.yml" -OutFile "docker-compose.yml"
docker compose up

当前仓库的 compose 实际上是一套完整生产栈

需要说明的是,当前仓库的 docker-compose.yml 已演进为一套完整自托管栈,拉起 Puter 及其依赖的全部外部服务。按 doc/self-hosting.md 的表格整理:

容器 镜像 角色
puter-caddy caddy:2.11-alpine 80/443 反向代理,转发到 Puter(docker-compose.yml
puter ghcr.io/heyputer/puter 应用本体,监听 4100,仅对内暴露
puter-mariadb mariadb:11 SQL 数据库,首启自动应用 schema
puter-valkey valkey/valkey:8-alpine Redis 兼容缓存 + 限流后端
puter-dynamo amazon/dynamodb-local KV 存储,表在首启自动创建
puter-s3 rustfs/rustfs S3 兼容对象存储(文件注释给出 MinIO 替换方式)
puter-s3-init amazon/aws-cli 一次性容器,首启创建 puter-local bucket 后退出

可选服务(compose profile ai,按需启用):

容器 镜像 角色
puter-ollama ollama/ollama 本地 LLM(CPU;GPU 直通可选)
puter-ollama-init ollama/ollama 一次性容器,首启拉取默认模型 tinyllama

几个与源码可互相印证的关键设计:

  • Valkey 以单节点 cluster 模式运行docker-compose.yml 的注释解释:Puter 的 ioredis 客户端只说 Cluster 协议,所以首启时把全部 16384 个槽位分配给自己(CLUSTER ADDSLOTSRANGE 0 16383),后续启动检测到 nodes.conf 已有槽位即跳过;--cluster-require-full-coverage no 保证即使出现部分槽位,读操作仍可用。这与 config.template.jsoncredis.startupNodes 的注释“单容器请跑 cluster 模式(单节点、全槽位)”一致。
  • 依赖编排基于健康检查puter 服务声明 depends_on 等待 valkey/mariadb 达到 service_healthy、dynamo service_started、s3-init service_completed_successfullydocker-compose.yml);mariadb 的健康检查用 healthcheck.sh --connect --innodb_initialized 确认不仅监听端口、而且已接受认证。
  • DynamoDB 表由 Puter 自举dynamo.bootstrapTables = true 时 Puter 启动时创建 store-kv-v1 表(docker-compose.yml 注释);该开关只对本地模拟器使用,指向真实 AWS 时应通过 IaC 建表(config.template.jsonc 对同一事项有相同的“NEVER set against real AWS”警告)。
  • Ollama 藏在 ai profile 后。不带 --profile ai 时两个 ollama 容器完全不起;同时要求 config.json 里写 "providers": { "ollama": { "enabled": false } },否则 Puter 会反复探测 127.0.0.1:11434 并在每次启动打印 ECONNREFUSEDdocker-compose.yml 注释与 doc/self-hosting.md 的说明一致)。启用时用 docker compose --profile ai up -d,模型默认 tinyllama(约 640 MB 磁盘 / 700 MB 内存),可用 OLLAMA_DEFAULT_MODEL 替换;NVIDIA GPU 直通需宿主机安装 nvidia-container-toolkit 并解开 deploy: 块。
  • 状态目录集中在 ./puter/data/<service>/,一处备份即可;z 挂载后缀是为 Fedora/RHEL 的 SELinux 重标注(其他发行版上是 no-op),缺失时容器会循环 EACCESdocker-compose.yml 注释)。

更省事的入口:一键安装脚本

doc/self-hosting.md 推荐的其实是一行安装命令,其实现是仓库根目录的 install.sh。它的行为顺序在脚本头注释中写得很清楚(install.sh):

  1. 检查 docker(含 compose 插件)、curlopenssl
  2. 创建 ./puter-selfhosted/(可用 PUTER_DIR 覆盖),并预建 puter/configputer/data/*puter/tls,数据目录 chmod 0777 以规避各镜像非 root UID 差异导致的挂载写权限问题(install.sh);
  3. 从 OSS 仓库下载 docker-compose.ymlcaddy/Caddyfile(Caddyfile 缺失时 Docker 会把它当成目录自动创建,导致 “not a directory” 挂载失败,脚本对此做了清理);
  4. openssl rand -hex 生成一整套密钥(MARIADB_ROOT_PASSWORDMARIADB_PASSWORDS3_SECRET_KEYJWT_SECRETJWT_SECRET_V2URL_SIGNATURE_SECRET),写入 .envputer/config/config.json
  5. docker compose up -d,并提示从 puter 容器日志中提取首启管理密码。

可调环境变量(install.sh):

变量 默认值 说明
PUTER_DIR ./puter-selfhosted 安装目录
PUTER_URL GitHub raw(main 分支) compose 文件来源
PUTER_DOMAIN puter.localhost 服务域名
PUTER_PORT 80 Caddy HTTP 端口
PUTER_PROTOCOL http 公网协议;TLS 在 Puter 前终结时设为 https
PUTER_TRUST_PROXY 1 前置反代跳数(1=Caddy 单跳,2=CF→Caddy→Puter)
PUTER_ENV prod proddev
PUTER_FORCE 0 1 才会覆盖已有 .env/config.json

重复运行是安全的:已存在的 .env/config.json 不会被覆盖(install.sh)。另外脚本会检查:若 PUTER_PROTOCOL=https./puter/tls/fullchain.pem 缺失,会警告此时 Caddy 仍只提供 HTTP(install.sh)。

首启、登录与验证

docker compose up -d 之后首启约需 30 秒:MariaDB 完成初始化,Puter 应用 schema 与默认应用。观察方式:

docker compose logs -f puter

doc/self-hosting.md 给出健康启动日志样例:

[config] override from /etc/puter/config.json
[mysql] running migrations from /opt/puter/dist/src/backend/clients/database/migrations/mysql: 2 file(s)
[mysql] applied mysql_mig_1.sql (...)
[mysql] applied mysql_mig_2.sql (9 statements)

首启后管理员登录用户为 admin,临时密码只会在 puter 容器日志中打印一次:

docker compose logs puter | grep tmp_password

登录后在设置中修改密码。migrationPaths 指向的迁移文件是幂等的,跨版本升级重启可安全重放;数据卷在 docker compose down 后保留(docker compose down && rm -rf puter/data 才是不可逆的彻底重置)。

关键配置项:config.json 怎么选

配置模板见 config.template.jsonc(每个字段只存在于唯一规范键,无 fallback 别名),逐键的源码级文档在 src/backend/types.ts。自托管场景中最容易踩坑的几个键,doc/self-hosting.md 给出了完整论证,这里按重要性归纳:

  • jwt_secret_v2:Puter 签名与校验认证 token 的 HMAC 密钥(JWT 头 kid: 'v2')。v1 旧格式已退役——用旧 jwt_secret 签发的 token 不再能通过校验,持有者需要重新登录;从旧版本升级时旧 jwt_secret 键会被忽略,可直接删除。
  • env: "prod":内置 config.default.json 出厂为 env: "dev"(配合源码树的 webpack-dev-server 工作流)。Docker/自托管跑的是预构建静态包,必须设 prod,否则首页会等待一个不存在的 CSS manifest。
  • database.migrationPaths:首启自动应用内置 MySQL/MariaDB schema,幂等,跨重启保留即可。
  • dynamo.aws 是 snake_caseaccess_key / secret_key),与 S3 块的 camelCase(accessKeyId / secretAccessKey不通用;对 dynamodb-local 写占位值即可,AWS SDK 只是要求“有值”。写错大小写会得到 Error: DynamoDB aws config requires both access_key and secret_key
  • meteringEnforcement.subscriptions: false:Puter 对部分界面(OpenAI/Anthropic 兼容的 AI 端点)声明“仅付费计划”。自托管没有付费计划,所有账户都落在免费策略上,若不关闭计划门,这些路由会对所有人返回 402 subscription_required。更彻底的做法是 "unlimitedMetering": true:所有账户解析为不限额策略,用量仍被记录,但永远不会因预算不足被拒(config.template.jsonc 对两者的语义有完整说明)。
  • s3.s3Config.forcePathStyle: true:RustFS / MinIO 需要 path-style URL(<endpoint>/<bucket>);换成真实 AWS S3 时应去掉该开关(虚拟主机模式),且 publicEndpoint 可整体省略。
  • s3.s3Config.publicEndpointendpoint(如 http://s3:9000)只在 docker 网络内可解析,而下发给浏览器的预签名上传/下载 URL 需要主机可达地址。Caddy 把 s3.<domain> 子域内部路由到 RustFS 并端到端保留 Host 头(S3 签名校验的要求),因此浏览器无需暴露独立端口、TLS 后也不会有混合内容。启用 TLS 后应改为 https://s3.<your-domain>
  • trust_proxy:Caddy 终结 TLS 并转发 X-Forwarded-For。不设它,req.ip 就是 Caddy 容器的 docker 网络地址,限流与 IP 审计日志全部失真。取值为可信跳数(1=Caddy 单跳,前置 Cloudflare 则 2);绝不要设 true(信任所有跳,使 XFF 可伪造)——config.template.jsonc 对此有同样警告。

TLS 与反向代理要点

仓库自带 caddy/Caddyfile,其设计目标是“镜像生产 ALB 的行为”:

  • 接受任意 Host 头,把子域路由(api.*site.*app.*dev.* 及用户动态子域)全部交给 Puter 内部处理;
  • auto_https offcaddy/Caddyfile):Puter 在运行时为用户发明形如 <name>.site.<domain> 的子域,只有 DNS-01 通配符证书能覆盖,而标准 caddy:2.11-alpine 镜像不带 DNS provider 插件,因此证书由运维者提供(通配符 fullchain.pem + privkey.pem 放入 ./puter/tls/);
  • s3. 前缀的 Host 单独 reverse_proxy s3:9000,其余全部转发 puter:4100,且 flush_interval -1 无缓冲流式转发(Puter 使用 SSE 与 socket.io;WebSocket 升级由 Caddy 默认代理);请求体上限 1024MiB 与生产 ALB 默认一致(Puter 对大文件做分块上传)。

若你已有一套自己的边缘代理(Traefik、nginx、HAProxy、云 LB),doc/self-hosting.md 的“Running behind your own reverse proxy”一节给出六条硬性规则,任何一条出错都会表现为重定向循环或 Invalid Host header:不改写 Host;外部域名必须等于 config.jsondomain;代理终结 TLS 时 protocol 必须设为 https;转发 Host/X-Forwarded-Proto/X-Forwarded-For/Upgrade+Connection(漏掉 WebSocket 升级头会导致实时连接静默失败);trust_proxy 等于代理跳数;把 *.<domain>*.site.<domain> 的泛解析路由给 Puter。

DNS 侧需要为主域加 api.*site.*app.*dev.*s3.* 的通配符记录;dig 验证解析生效通常需要 5–60 分钟。

运行中的管理与常见故障

日常管理命令(来自 doc/self-hosting.md):

# 升级
docker compose pull
docker compose up -d

# 日志
docker compose logs -f puter

# 停止但保留数据
docker compose down

常见故障与对应原因:

  • Caddy 返回 502 / Bad Gateway:puter 容器没起来,docker compose logs puter 通常指向 .envconfig.json 之间的数据库密码不一致(两者必须来自同一次密钥生成,漂移会触发 ER_ACCESS_DENIED_ERROR);
  • 登录页提示 “admin password not set”:首启临时密码只打印一次,用 docker compose logs puter | grep tmp_password 找回;
  • 健康检查报 unhealthy 但站点正常:容器内健康检查固定打 puter.localhost:4100/test,改了 domain/port 后检查项仍用默认值,站点本身无碍;
  • compose up 卡在 “waiting for service to be healthy”docker compose ps 定位不健康的容器;MariaDB 冷启约 20–30 秒,其余服务均应在 5 秒内就绪。

支持渠道与许可证

入口文档与支持章节一致:Bug 或功能请求请开 issue;社区渠道包括 Discord、X (Twitter)、Reddit、Mastodon;安全问题联系 security@puter.com;一般邮件联系 hi@puter.com。

许可证方面,本仓库(含全部内容、子项目、模块与组件)遵循 AGPL-3.0,除非明确另作说明;仓库内第三方库可能受其自身许可约束。

多语言文档

Puter 的入门 README 维护了 30 余种语言版本,统一放在 doc/i18n/ 目录下,与根目录英文 README.md 并列,例如中文 README.zh.md、西班牙语 README.es.md、法语 README.fr.md、德语 README.de.md、日语 README.jp.md、俄语 README.ru.md、阿拉伯语 README.ar.md 等。各语言版内容随主 README 演进,但细节(如 Node 版本要求)可能与仓库当前清单存在时间差,以 package.jsondoc/self-hosting.md 为准。

小结

按本文路径走一遍,你可以在本地用三条命令进入开发模式(git clonenpm installnpm start,入口 tools/start.mjs),也可以用 install.sh 一条命令在单主机上拉起含 MariaDB、Valkey(cluster 模式)、DynamoDB-local、RustFS S3 与 Caddy 的完整栈,首启约 30 秒后通过 docker compose logs puter | grep tmp_password 拿到管理员密码。配置面则以 puter/config/config.json 为唯一注入点(深度合并于内置默认值之上),密钥、env: "prod"trust_proxy、S3 path-style 与计量开关这几项是自托管中最值得逐项核对的部分。

登录后查看全文
热门项目推荐
相关项目推荐