Puter:开源「互联网计算机」的本地开发、自托管与架构解析
2026/9/6 18:58:39 网站建设 项目流程

Puter:开源「互联网计算机」的本地开发、自托管与架构解析

【免费下载链接】puter🌐 The Internet Computer! Free, Open-Source, and Self-Hostable.项目地址: https://gitcode.com/GitHub_Trending/pu/puter

本文基于 Puter 仓库的 README.md 及其配套的安装脚本、Docker 编排与自托管文档展开,讲解 Puter 这一「浏览器里的桌面环境 / 个人云计算机」项目的三种落地方式:源码本地开发、Docker Compose 全栈自托管(含一行式安装器)、以及生产托管服务。读完你可以独立完成 Puter 的克隆构建、单主机多容器部署、DNS 通配符与 TLS 配置,并能看懂 Caddy 反向代理、MariaDB/Valkey/DynamoDB-local/RustFS 各组件在整体栈中的角色。

一、Puter 是什么

Puter 的自我定位是一个先进、开源、可自托管的「互联网计算机(Internet Computer)」:功能丰富、速度快、高度可扩展(README.md)。它把文件、应用、游戏收纳在一处,从任何设备都能访问。

README 把读者分成两类:

  • 面向用户:Puter 的目标是让所有工作、创作、娱乐所需的应用与功能集中在一个入口下——从记事本、录音机到电子表格、摄像头,一站式覆盖数字生活。
  • 面向开发者:Puter 提供构建与发布 Web 应用、游戏所需的一切能力:AI、云存储(对象存储)、数据库(KV)、Serverless Workers;应用发布到 App Store 后还能触达并变现用户。

仓库本身是一个 npm workspaces 单仓库(monorepo),package.json 中"workspaces": ["src/*"]声明了子项目结构:

目录职责
src/gui浏览器桌面环境 GUI(webpack 打包、nodemon 热重载)
src/backendNode.js/TypeScript 后端:controllers / services / stores / clients / drivers 分层架构
src/puter-js供第三方网页嵌入的 SDK(puter.js),附带 API/e2e 测试
src/workerServerless Worker 运行时库构建
src/docs开发者文档站
src/mcp-connectorMCP 连接器
extensions后端扩展(thumbnails、metering、whoami、serverInfo 等)
tools启动器、类型检查、覆盖率报告等工程脚本

后端的分层与依赖注入细节(Controllers → Drivers → Services → Stores → Clients → Config,由PuterServer依次实例化)在 doc/architecture.md 中有完整说明,本文只在自托管语境下引用其结论。

二、本地开发:npm start起一个完整后端

README 给出的本地开发流程是:

git clone https://gitcode.com/GitHub_Trending/pu/puter cd puter npm install npm start

成功后 Puter 会运行在http://puter.localhost:4100

结合仓库源码,这条流程的实际行为是:

  • Node 版本要求:package.json 声明"engines": { "node": ">=24.0.0" },且 Docker 镜像也基于node:24-slim,即 Node 24+ 是明确前提。
  • npm start的入口是 tools/start.mjs。默认路径(不带参数)依次执行setupExtensionsbuild:tstsc -p tsconfig.build.json编译后端),然后以node -r ./dist/src/backend/telemetry.js ./dist/src/backend/index.js启动自托管后端——也就是说本地开发起的是与生产自托管同一套代码路径。
  • --server参数npm start --server=puter.com(或任意完整 origin)会完全跳过本地后端,只在本机用 src/gui/dev-server.js 起 GUI,并对接远端 Puter 后端;裸域名会按https://api.<domain>约定解析。这是前端开发者调试远程环境的方式。
  • --extensions参数npm start --server=puter.com --extensions=<dir>;<dir>可把仓库外的 GUI 扩展目录捆绑进本地 GUI(等价于PUTER_GUI_EXTENSION_PATHS),仅限 GUI-only 模式使用。

测试方面,package.json 提供了test:backend(vitest,默认引擎)、test:backend:postgresPUTER_TEST_DB_ENGINE=postgres)、test:puterjs(node / browser / workerd 三种 runner)等脚本,可作为验证本地环境是否搭对的依据。

三、自托管:一行式安装器

README 给出的自托管命令:

  • Linux/macOS:
curl -fsSL https://puter.com/selfhost | sh
  • Windows(PowerShell):
irm https://puter.com/selfhost?os=windows | iex

这两个 URL 最终执行的逻辑与仓库内的 install.sh / install.ps1 一致(也可从仓库 raw 地址直接拉取脚本)。脚本的执行步骤(install.sh 头注释即为权威说明):

  1. 检查依赖:docker(含 compose 插件)、curlopenssl
  2. 创建安装目录./puter-selfhosted/(可用PUTER_DIR覆盖),预建puter/data/{valkey,mariadb,dynamo,s3,puter,caddy}等目录并放宽权限,规避非 root 容器在原生 Linux 上的 EACCES 问题;
  3. 从 OSS 仓库下载 docker-compose.yml 与 caddy/Caddyfile;
  4. openssl rand生成全套随机密钥,写入.envputer/config/config.json
  5. docker compose up -d启动,并提示用docker compose logs puter | grep password抓取首次启动打印的一次性 admin 临时密码。

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

变量作用默认值
PUTER_DIR安装目录./puter-selfhosted
PUTER_URL拉取 compose 文件的基础 URLGitHub raw(main 分支)
PUTER_DOMAINPuter 服务的域名puter.localhost
PUTER_PORTCaddy 的 HTTP 端口80
PUTER_PROTOCOL对外协议http/https(有 TLS 终结时设 https)http
PUTER_TRUST_PROXY前置反向代理跳数(1=内置 Caddy;2=Cloudflare→Caddy→Puter)1
PUTER_ENVprod/devprod
PUTER_FORCE设为1时覆盖已有.env/config.json0

生成的密钥包括MARIADB_ROOT_PASSWORD/MARIADB_PASSWORD/S3_SECRET_KEY(各 32 字节 hex),以及jwt_secret(仅用于验证存量 v1 旧 token)、jwt_secret_v2(签发所有新 token)、URL_SIGNATURE_SECRET(各 64 字节 hex)。重复执行脚本是安全的——已存在的.envconfig.json不会被覆盖,只会刷新 compose 文件并拉起栈。

自托管的完整手动步骤(.envconfig.json全量示例、DNS、TLS、反向代理规则、故障排查)在 doc/self-hosting.md,以下各节将其与仓库文件结合展开。

四、Docker 全栈:七个容器各司其职

doc/self-hosting.md 对 docker-compose.yml 的概括是:拉起 Puter 及其所需的全部外部服务,是单主机上最接近生产形态的部署。容器清单:

容器镜像角色
puter-caddycaddy:2.11-alpine80/443 反向代理,转发到 Puter
puterghcr.io/heyputer/puter应用本体(端口 4100)
puter-mariadbmariadb:11SQL 数据库,首次启动自动应用 schema
puter-valkeyvalkey/valkey:8-alpineRedis 兼容缓存 + 限流后端
puter-dynamoamazon/dynamodb-localKV 存储,KV 表首次启动自动创建
puter-s3rustfs/rustfsS3 兼容对象存储(注释中注明 MinIO 可平替)
puter-s3-initamazon/aws-cli一次性容器,首启创建puter-localbucket 后退出

所有状态统一落在./puter/data/<service>/,一个目录就是全部备份范围。

几个值得注意的实现细节(均来自 docker-compose.yml 注释与编排内容):

  • Valkey 以单节点集群模式运行:Puter 的 ioredis 客户端只支持 Cluster 模式,所以编排里用--cluster-enabled yes启动,并在首启时把 16384 个槽位全部分配给自己,--cluster-require-full-coverage no保证部分槽位缺失时读仍可用。
  • DynamoDB-localbootstrapTables: true让 Puter 在启动时自建store-kv-v1表;容器固定user: "1000:1000",与 install.sh 中把 bind-mount 数据目录设为0777的处理相呼应,都是为了让不同 UID 的容器能写入宿主机目录。
  • RustFS(S3):默认不发布宿主机端口,浏览器侧的预签名上传/下载全部经 Caddy 的s3.<domain>子域名转发(保持 Host 头以通过 S3 签名校验);需要aws-cli调试时可手动解开9000:9000映射。
  • 启动依赖链puter服务depends_on中等待 valkey/mariadb 健康、dynamo 启动、s3-init成功退出;caddy再依赖 puter 启动。首次启动约 30 秒,主要是 MariaDB 初始化。

五、手动自托管:.envconfig.json逐键解析

如果不用一行式安装器,doc/self-hosting.md 的 Step 1 要求在同一个 shell 会话中完成密钥生成与两份配置写入——.env(docker compose 读取)和puter/config/config.json(Puter 读取)必须对 MariaDB 密码与 S3 密钥保持一致,否则会出现ER_ACCESS_DENIED_ERROR

.env内容(安装器生成的同形):

MARIADB_ROOT_PASSWORD=$(openssl rand -hex 32) MARIADB_PASSWORD=$(openssl rand -hex 32) S3_SECRET_KEY=$(openssl rand -hex 32) JWT_SECRET_V2=$(openssl rand -hex 64) URL_SIGNATURE_SECRET=$(openssl rand -hex 64) # .env HTTP_PORT=80 # HTTPS_PORT=443 # 启用 TLS 后再取消注释 MARIADB_ROOT_PASSWORD=$MARIADB_ROOT_PASSWORD MARIADB_DATABASE=puter MARIADB_USER=puter MARIADB_PASSWORD=$MARIADB_PASSWORD S3_ACCESS_KEY=puter S3_SECRET_KEY=$S3_SECRET_KEY S3_BUCKET=puter-local

puter/config/config.json骨架(一行式安装器按 install.sh 模板写入的版本,含全部默认项):

{ "domain": "puter.localhost", "protocol": "http", "pub_port": 80, "env": "prod", "static_hosting_domain": "site.puter.localhost", "static_hosting_domain_alt": "host.puter.localhost", "private_app_hosting_domain": "app.puter.localhost", "private_app_hosting_domain_alt": "dev.puter.localhost", "jwt_secret": "...", "jwt_secret_v2": "...", "url_signature_secret": "...", "database": { "engine": "mysql", "host": "mariadb", "port": 3306, "user": "puter", "password": "...", "database": "puter", "migrationPaths": ["/opt/puter/dist/src/backend/clients/database/migrations/mysql"] }, "redis": { "startupNodes": [{ "host": "valkey", "port": 6379 }], "tls": false }, "dynamo": { "endpoint": "http://dynamo:8000", "bootstrapTables": true, "aws": { "access_key": "fake", "secret_key": "fake", "region": "us-east-1" } }, "s3": { "s3Config": { "endpoint": "http://s3:9000", "publicEndpoint": "http://s3.puter.localhost", "accessKeyId": "puter", "secretAccessKey": "...", "region": "us-east-1", "forcePathStyle": true } }, "s3_bucket": "puter-local", "s3_region": "us-east-1", "providers": { "ollama": { "enabled": false } }, "meteringEnforcement": { "subscriptions": false }, "trust_proxy": 1 }

关键配置项的设计动机(doc/self-hosting.md 的 "Why these knobs" 一节):

  • jwt_secret_v2:Puter 签发/验证认证 token 的 HMAC 密钥(JWT headerkid: 'v2');旧版jwt_secret只用于验证存量 v1 token,若从老版本升级应直接删除旧字段。
  • env: "prod":内置config.default.json默认env: "dev"(配合 webpack-dev-server 的 CSS manifest 工作流);自托管跑的是预构建静态包,须设prod让首页输出/dist/bundle.min.css
  • database.migrationPaths:启动时应用内置 MySQL/MariaDB schema;迁移文件幂等,重启安全。
  • dynamo.bootstrapTables: true:只用于本地模拟器,绝不可指向真实 AWS。
  • dynamo.awss3.s3Config的命名风格不同:前者 snake_case(access_key/secret_key),后者 camelCase(accessKeyId/secretAccessKey),两者不可互换——写错正是排障清单里的经典错误。
  • meteringEnforcement.subscriptions: false:OpenAI/Anthropic 兼容的 AI 端点被声明为付费计划专属;自托管没有付费计划,若保留该门禁所有人都会收到402 subscription_required
  • providers.ollama.enabled: false:默认 Puter 会探测127.0.0.1:11434的本地 Ollama,没有它会每次启动刷ECONNREFUSED
  • s3.s3Config.forcePathStyle: true:RustFS/MinIO 需要 path-style URL;换真实 AWS S3 时去掉此标志,publicEndpoint也可整体删除。
  • trust_proxy: 1:Caddy 终结 TLS 并转发X-Forwarded-For,不设它req.ip会是 Caddy 容器地址,限流与 IP 审计即失效。前置第二层代理(如 Cloudflare)时改为2永远不要设true(信任所有跳,XFF 可伪造)。该键的完整语义见 config.template.jsonc 中trust_proxy的注释。
  • 密码轮换注意:首启后改MARIADB_PASSWORD,仅改.env不会更新 MariaDB(凭据已固化进./puter/data/mariadb/),需在库内手动改密或清空该数据目录重来。

全部可配置键的权威清单在 config.template.jsonc(每个键带注释,未设置的键回落到文档化默认值),逐字段类型定义在 src/backend/types.ts。

六、DNS 通配符与 TLS

Puter 按子域名路由:api.<domain>site.<domain>app.<domain>dev.<domain>及每个用户的<name>.site.<domain>等动态子域名,因此 doc/self-hosting.md 要求为每个托管域配通配符 A 记录(*.<domain>*.site.<domain>*.host.<domain>*.app.<domain>*.dev.<domain>),外加s3.<domain>

TLS 方面,官方建议用 Let's Encrypt DNS-01 申请通配符证书certbot certonly --manual --preferred-challenges dns -d <domain> -d *.<domain> …),把fullchain.pem+privkey.pem放进./puter/tls/,然后:

  1. 解开 caddy/Caddyfile 底部的:443块,并把:80块换成强制跳转的redir版本;
  2. 解开 docker-compose.yml 中 caddy 服务的443:443端口映射;
  3. .env解开HTTPS_PORT=443
  4. config.json设为"protocol": "https", "pub_port": 443,并把 S3publicEndpoint改为https://s3.<domain>

为什么不用 Caddy 的自动 HTTPS:标准caddy:2.11-alpine镜像只支持对已知主机名做 HTTP-01 签发,而 Puter 会在运行时发明<name>.site.<domain>这类子域名,只能靠 DNS-01 通配符证书覆盖;DNS-01 需要的 DNS 提供商插件不在标准镜像中。所以 caddy/Caddyfile 里显式写了auto_https off,证书完全由运维方提供。

七、Caddy 反向代理配置解析

caddy/Caddyfile 镜像了生产环境 ALB 的行为:接受任意 Host 头、原样转发给 Puter,子域名路由全部由 Puter 内部完成。核心逻辑封装在共享片段(puter_routes)中:

  • request_body max_size 1024MiB:粗略对齐生产 ALB 的请求体上限;Puter 大文件上传是分块的,单请求 1 GiB 足够。
  • @s3 header_regexp Host ^s3\.reverse_proxy s3:9000:只匹配s3.开头的 Host,转发到 RustFS;Caddy 全程保留原始 Host 头,S3 签名校验因此成立,且与 Puter 共享同一 TLS 终结,避免 9000 端口直连带来的混合内容问题。
  • 其余一切 Host →reverse_proxy puter:4100,并设flush_interval -1让 SSE/socket.io 流式响应直通;WebSocket 升级 Caddy 默认代理,无需额外配置。
  • :80 { import puter_routes }:无主机名的 site 地址是 catch-all,对每个 Host 应答——这正是 Puter 子域名路由所需。

若把 Puter 放到自己的边缘代理(Traefik/nginx/云 LB)后面,doc/self-hosting.md 列出六条硬性规则,违反任何一条都会造成重定向循环或Invalid Host header:不改写 Host 头、外部域名必须等于config.jsondomainprotocol与对外协议一致、转发X-Forwarded-Proto/X-Forwarded-For/Upgrade/Connectiontrust_proxy等于实际代理跳数、通配符子域名流量全部路由到 Puter。

八、Docker 镜像与配置注入机制

自托管默认拉取ghcr.io/heyputer/puter:mainpull_policy: always);Dockerfile 是 multistage 构建:node:24-slim构建阶段先拷 package 清单再npm ci(利用层缓存),然后npm run build:ts编译后端、并行构建 GUI 与 puter-js 的 webpack bundle;运行阶段是无构建工具的 slim 镜像,以node用户运行,EXPOSE 4100,健康检查为wget --spider http://puter.localhost:4100/test

配置注入的关键约定(Dockerfile 注释):自托管者把config.json挂载到/etc/puter/config.json(对应 compose 里的./puter/config:/etc/puter),配置加载器会将其深度合并在内置config.default.json之上,所以部分覆盖是允许的,文件不存在则全用默认值。

想从源码构建本地镜像:解开 docker-compose.yml 中puter服务的build:块(可加platforms: [linux/amd64, linux/arm64]交叉编译),并把pull_policy改为never,然后docker compose up -d --build

九、可选本地 LLM 与其他进阶配置

doc/self-hosting.md 的「Additional configuration」各块均可直接并入puter/config/config.jsondocker compose restart puter

  • PostgreSQL:社区贡献、非生产默认(生产自托管推荐 MariaDB/MySQL 与 SQLite)。把database.engine改为postgres并把migrationPaths指向/opt/puter/dist/src/backend/clients/database/migrations/postgres,也可改用connectionString
  • 邮件(SMTP):用于密码重置、邮箱确认与通知;不配置时这些流程会静默失败。示例块含from/host/port/secure/auth;可配"strict_email_verification_required": true强制登录前验证邮箱;本地调试可用 MailHog 容器(docker run -d -p 1025:1025 -p 8025:8025 mailhog/mailhog)作 SMTP 汇。
  • OIDC 登录:Google(client_id/client_secret/scopes,走 OIDC 发现)、Apple(client_id/team_id/key_id/private_key)、Microsoft(client_id/client_secret/tenant_id)三类示例配置,回调地址统一为https://puter.<domain>/auth/oidc/callback/login(及/signup);自定义 OIDC 供应商需显式给出authorization_endpoint/token_endpoint/userinfo_endpoint
  • AI 供应商:任何设置了apiKey的供应商自动启用,如claudeopenai-completiongeminiopenai-image-generation;完整供应商清单(chat/image/video/TTS/OCR)见 config.template.jsonc。
  • 存储配额storage_capacity(默认 100 MB)+is_storage_limited(设false为无限,受宿主机磁盘限制)。
  • 计量与预算:Puter 按每月预算计量下行流量、对象存储请求、KV 容量与 AI token,超限返回402 insufficient_funds(只读与删除操作始终可用)。自托管没有购买渠道,建议"unlimitedMetering": true(账户全解析为无限策略,用量仍记录);或"meteringEnforcement": { "enabled": false }只记录不拦截;worker 发起的调用默认豁免执行,"meteringEnforcement": { "workers": true }可纳入。
  • Captcha:内置 proof-of-work 验证码,"captcha": { "enabled": true, "difficulty": "medium" },难度easy/medium/hard
  • 关闭注册"disable_user_signup": true强制访客用已有账户登录。
  • 一次性邮箱 TLD 屏蔽blockedEmailDomains列表,仅在env: "prod"时生效。
  • 密码策略"min_pass_length": 12
  • 联系表收件人"support_email",默认support@puter.com

本地 LLM(Ollama)ollamaollama-init两个服务在 compose 的aiprofile 之后,不会默认启动。启用步骤:在config.json"providers": { "ollama": { "apiBaseUrl": "http://ollama:11434" } }(不启用时保持"enabled": false,否则启动刷ECONNREFUSED);在.env可选设置OLLAMA_DEFAULT_MODEL(默认tinyllama,约 640 MB 磁盘 / 700 MB 内存);然后docker compose --profile ai up -dollama-init是一次性拉模型容器,模型已在盘上时 pull 是快速 no-op。NVIDIA GPU 直通需宿主机装nvidia-container-toolkit并解开 compose 中deploy:块。

十、日常运维与故障排查

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

# 更新 docker compose pull docker compose up -d # 日志 docker compose logs -f puter # 停止但保留数据 docker compose down # 停止并清除全部状态(不可逆) docker compose down rm -rf puter/data

迁移在 pull 更新后幂等重放,卷会保留。

现象原因与处置
Caddy 502 / Bad Gatewayputer 容器没起来;docker compose logs puter看是哪个依赖拒绝(最常见是.envconfig.json的 DB 密码不一致)
登录页提示 admin 密码未设置首启临时密码只打印一次:docker compose logs puter | grep "tmp_password",登录后到 Settings 修改
健康检查不健康但站点正常镜像内 HEALTHCHECK 打的是默认域/端点;若改过domain/port需留意,站点本身无碍
DNS 改完不解析等待传播(5–60 分钟),dig <domain>dig api.<domain>都应返回服务器 IP
compose up卡在等健康docker compose ps看谁不健康;MariaDB 冷启约 20–30s,其余均 <5s
DynamoDB aws config requires both access_key and secret_keydynamo.aws里用了 camelCase;该块必须是 snake_case(access_key/secret_key),只有s3.s3Config用 camelCase

十一、社区、许可与多语言

README 的 Support 一节给出与社区联系的渠道:Bug 报告与功能请求走 issue;X (Twitter)@HeyPuter;安全问题/滥用举报security@puter.com;维护者邮箱hi@puter.com

许可方面:本仓库及其全部内容、子项目、模块与组件均按AGPL-3.0许可(LICENSE.txt),除非另有明确声明;仓库内第三方库可能适用各自许可。

README 还维护了 30+ 语言的多语言版本索引(阿拉伯语、孟加拉语、中文、丹麦语、英语、波斯语、芬兰语、法语、德语、希伯来语、印地语、匈牙利语、印尼语、意大利语、日语、韩语、马来语、荷兰语、波兰语、葡萄牙语、罗马尼亚语、俄语、西班牙语、瑞典语、泰语、土耳其语、乌克兰语、乌尔都语、越南语等),对应文件位于 doc/i18n/ 目录(如 doc/i18n/README.zh.md)。

小结

Puter 的仓库结构让「开发者体验」与「自托管体验」走同一条代码路径:本地npm start与 Docker 栈启动的是同一套后端(dist/src/backend/index.js),配置都收敛到config.json的深度合并语义上。掌握本文的三个抓手——tools/start.mjs 的开发启动链路、docker-compose.yml + caddy/Caddyfile 的单主机全栈、以及 doc/self-hosting.md 的子域名路由与 TLS 规则——即可在一个主机上完成从克隆到生产形态自托管的完整闭环。

【免费下载链接】puter🌐 The Internet Computer! Free, Open-Source, and Self-Hostable.项目地址: https://gitcode.com/GitHub_Trending/pu/puter

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询