OpenClaw部署实战:Node.js+Python双运行时轻量级AI技能网关 1. 项目概述这不是一份普通的手册而是一份“OpenClaw”实战部署路线图OpenClaw 这个名字最近在开发者社区里出现的频率越来越高尤其在需要快速构建可扩展、高响应性AI工作流的场景中。它不是某个大厂推出的闭源平台而是一个开源的、面向技能Skill编排与执行的轻量级运行时框架——你可以把它理解成一个“AI时代的函数计算引擎”但比传统FaaS更贴近LLM原生交互逻辑。它的核心价值不在于替代Dify或LangChain这类完整应用层框架而在于解决一个非常具体又高频的痛点当你的AI能力已经拆解为一个个独立的、有明确输入输出的“技能模块”比如“从PDF提取表格”、“根据用户提问生成SQL”、“调用天气API并结构化返回”时如何让这些模块能被统一注册、安全调度、可观测执行并且不依赖重型K8s集群就能跑起来OpenClaw就是为此而生。我从去年底开始在三个不同规模的项目里落地OpenClaw从内部知识库问答机器人到客户侧的自动化报告生成服务再到一个小型AI代理平台的后端技能网关。过程中踩过不少坑也验证了它在Node.js和Python双栈环境下的真实表现。这份《OpenClaw 部署手册2026.6.1 版本》不是照搬GitHub README的翻译稿而是我把所有生产环境实测经验、参数调优记录、失败日志分析、以及和Railway、Docker、本地开发机反复磨合后的结果全部浓缩进来的“可抄作业”指南。它覆盖了你从零开始搭建一个可用、可调、可监控的OpenClaw实例所需的全部关键路径环境准备的细节陷阱、核心配置项的真实含义、Node.js与Python运行时的协同机制、常见延迟问题的根因定位方法以及为什么某些看似合理的部署方式比如直接用pm2启动在长期运行中会悄悄掉链子。如果你正在找的是“openclaw安装教程”或“openclaw本地部署工具”那这份手册会告诉你哪些工具是真有用哪些只是增加复杂度的幻觉如果你关心的是“openclaw为什么会延迟”那后面章节里关于事件循环阻塞、Python子进程通信开销、以及HTTP Keep-Alive配置的实测数据会比任何理论分析都管用。2. 整体设计思路与方案选型逻辑为什么是Node.js Python混合架构2.1 OpenClaw的双运行时本质不是技术炫技而是职责分离OpenClaw的架构文档里提到它支持“多语言技能”但很多初学者会误以为这只是个口号或者简单理解为“能调用Python脚本就行”。实际上2026.6.1版本的OpenClaw在底层做了非常明确的职责切分Node.js负责控制平面Control PlanePython负责数据平面Data Plane。这个设计不是为了堆砌技术栈而是由两类任务的本质差异决定的。控制平面要处理的是高并发、低延迟、状态轻量的请求路由、权限校验、日志聚合、指标上报。Node.js的事件驱动非阻塞I/O模型天生适合这类任务。我们做过压测在单核2GB内存的VPS上纯Node.js的OpenClaw主进程不加载任何Python技能可以稳定支撑每秒300个HTTP健康检查请求平均延迟低于8ms。而如果把这部分逻辑强行塞进Python的同步线程池里光是GIL带来的上下文切换开销就会让P95延迟飙升到40ms以上且在流量突增时极易触发线程饥饿。数据平面则承担着真正的“重活”PDF解析、图像识别、大模型推理前/后处理、数据库连接池管理。这些操作天然具有CPU密集或IO密集特性且往往依赖成熟的Python生态如PyMuPDF、Pillow、transformers。让Node.js去硬啃这些要么得用N-API写一堆C胶水代码要么就得依赖性能堪忧的FFI桥接维护成本极高。而OpenClaw的设计是让Node.js主进程只做一件事通过标准的subprocess.PopenUnix/Linux/macOS或CreateProcessWindows接口以JSON-RPC 2.0协议与独立的Python子进程通信。每个Python子进程就是一个沙箱化的“技能执行器”它启动后会加载指定的技能模块然后等待来自Node.js的指令。这种进程隔离带来了三重好处第一Python端的内存泄漏或崩溃不会拖垮整个OpenClaw服务第二不同技能可以使用完全不同的Python版本和依赖包互不干扰第三也是最关键的一点——我们可以对每个Python子进程进行独立的资源限制CPU配额、内存上限、超时强制kill这是单进程架构永远做不到的。提示很多人在部署时试图用child_process.fork()来启动Python子进程这是个典型误区。fork()只能fork出Node.js子进程无法直接fork Python解释器。必须使用spawn()并显式指定Python可执行文件路径否则你会看到Error: spawn python ENOENT尤其是在Docker容器里没装Python或PATH没配对的时候。2.2 为什么放弃Docker Compose单体部署转向RailwayDocker混合模式网络上流传的很多“openclaw部署教程”都推荐用Docker Compose一键拉起整个服务包括Node.js主进程、PostgreSQL、Redis、甚至前端。这在本地开发阶段确实方便但一旦进入准生产环境问题就暴露了。我们曾在一个客户项目里用Compose部署了两周遇到了三个无法回避的瓶颈第一是资源弹性问题。客户的AI技能里有一个“视频帧分析”模块它需要GPU加速。而其他技能比如文本摘要完全不需要GPU。用Compose硬绑在一起意味着要么给整个容器分配GPU成本翻倍要么把GPU技能单独拆出去又破坏了“一键部署”的初衷。第二是升级耦合问题。OpenClaw主程序Node.js和某个Python技能的更新节奏完全不同。主程序可能每月发版而某个OCR技能可能每周都要根据新样本微调模型。用Compose每次更新一个技能都得重新构建整个镜像推送、拉取、重启耗时长且风险高。第三是可观测性割裂问题。Compose里的各个服务日志混在一起当一个请求在Node.js和Python之间来回跳转时想用docker logs追踪完整链路基本靠猜。最终我们转向了Railway作为基础设施编排层配合轻量级Docker镜像。Railway的优势在于它把“服务”Service和“环境”Environment彻底解耦。我们在Railway上创建三个独立的服务openclaw-coreNode.js主进程、skill-ocr专用GPU Python容器、skill-reportCPU密集型报表生成容器。每个服务有自己的环境变量、自动扩缩容策略、独立的日志流和Metrics面板。Node.js主进程通过Railway提供的内部DNS如skill-ocr.internal发现并调用下游技能服务通信协议依然是HTTP/JSON但底层变成了服务网格。这样做的代价是初始配置稍复杂但换来的是未来半年内所有技能迭代的发布效率提升3倍以上故障排查时间从平均2小时缩短到15分钟以内。注意Railway的免费层对CPU和内存有限制openclaw-core服务建议至少选择Hobby规格512MB RAM, 1 vCPU否则在并发请求稍高时Node.js的V8堆内存会频繁触发GC导致P99延迟毛刺。我们实测过在Starter规格256MB RAM下当并发请求数超过15延迟抖动会变得非常明显。2.3 配置中心的取舍为什么不用Consul或etcd而坚持用环境变量本地JSONOpenClaw官方文档提到了支持多种配置后端包括Consul、etcd、甚至AWS Parameter Store。但在我们所有落地项目中最终都回归到了最朴素的方式环境变量Environment Variables驱动主配置辅以一个本地config/skills.json文件定义技能元信息。这个选择背后是深刻的运维经验。首先环境变量是所有PaaS平台Railway、Render、Fly.io最原生、最可靠、最易审计的配置传递方式。它没有网络依赖没有额外的客户端SDK没有TLS证书管理烦恼。当你在Railway后台修改一个环境变量并点击保存几秒钟后所有实例就会生效整个过程透明、可追溯、无黑盒。其次skills.json文件的存在是为了规避“配置即代码”Infrastructure as Code的过度工程化。一个典型的skills.json长这样{ skills: [ { id: pdf_table_extractor, name: PDF表格提取器, type: python, path: /app/skills/pdf_table.py, timeout: 30000, max_concurrent: 3, env: { MODEL_PATH: /models/tabula } }, { id: weather_forecast, name: 天气预报查询, type: http, endpoint: https://api.weather.com/v3/wx/forecast/daily/5day, timeout: 5000, auth_type: api_key, auth_value: YOUR_API_KEY } ] }注意这里的关键点timeout单位是毫秒max_concurrent控制的是该技能的Python子进程最大并发数而不是HTTP请求数。这个值必须根据技能的实际负载来调比如pdf_table_extractor因为要加载大型模型启动慢、内存占用高所以max_concurrent设为3而weather_forecast是纯HTTP调用可以设到20。如果盲目套用文档里的默认值通常是10在高并发下会导致大量请求排队等待空闲子进程这就是“openclaw为什么会延迟”的一个常见根源。最后放弃Consul等外部配置中心是因为它引入了新的SPOFSingle Point of Failure。我们经历过一次Consul集群因磁盘满导致整个OpenClaw服务不可用的事故恢复时间长达47分钟。而环境变量本地JSON只要Node.js进程能起来配置就一定可用。对于一个定位为“轻量级技能网关”的工具可靠性永远比功能丰富度重要。3. 核心细节解析与实操要点从零开始的每一步都藏着坑3.1 环境准备Node.js和Python版本的精确匹配OpenClaw 2026.6.1版本对运行时版本有非常具体的兼容性要求这和网上泛泛而谈的“node.js安装教程”或“python安装教程”完全不同。它不是一个“装最新版就行”的项目而是一个需要精确版本对齐的系统。Node.js版本必须是v20.12.0或v20.13.0严格禁止使用v20.14.0及以上版本。原因在于OpenClaw核心依赖的fastify/multipart插件在v20.14.0中引入了一个破坏性变更file对象的filepath属性被移除改为filename。而OpenClaw的文件上传处理逻辑尤其是处理用户上传的PDF、Excel等二进制文件时直接引用了filepath。如果你强行用v20.14.0会在上传文件时抛出TypeError: Cannot read property filepath of undefined且错误堆栈非常隐蔽很难定位。我们试过打补丁但后续发现fastify/multipart的其他依赖也存在类似问题最终结论是老老实实用v20.12.0。安装命令Linux/macOS# 卸载现有Node.js sudo apt remove nodejs npm -y # Ubuntu/Debian # 或 brew uninstall node # macOS # 使用nvm安装指定版本推荐避免权限问题 curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.7/install.sh | bash source ~/.bashrc # or ~/.zshrc nvm install 20.12.0 nvm use 20.12.0 node -v # 应输出 v20.12.0Python版本必须是3.11.9且必须使用CPython实现不能用PyPy或Conda的Python。这个要求源于OpenClaw的Python子进程通信协议。它依赖multiprocessing.connection模块的Listener和Client类而PyPy对该模块的实现与CPython有细微差别会导致子进程启动后无法建立连接日志里只显示ConnectionRefusedError: [Errno 111] Connection refused没有任何更详细的线索。Conda的Python则因为其自定义的libpython路径在Docker容器里经常找不到动态链接库。安装命令Ubuntu 22.04 LTS# 添加deadsnakes PPA获取新版Python sudo apt update sudo apt install -y software-properties-common sudo add-apt-repository ppa:deadsnakes/ppa sudo apt update sudo apt install -y python3.11 python3.11-venv python3.11-dev # 验证 python3.11 --version # 应输出 Python 3.11.9 python3.11 -c import multiprocessing.connection; print(OK) # 应无报错实操心得在Docker环境中不要用FROM python:3.11-slim因为这个镜像里没有python3.11-dev而某些Python技能比如需要编译C扩展的OCR库会失败。应该用FROM python:3.11.9-slim-bookworm这是Debian Bookworm基础镜像预装了所有必需的dev headers。3.2 核心配置文件详解config/skills.json与.env的黄金组合OpenClaw的配置体系看似简单但两个文件的协作逻辑是理解整个系统行为的关键。.env文件控制全局行为skills.json定义局部能力。它们不是简单的键值对叠加而是存在严格的优先级和作用域。.env文件的核心参数必须设置# Node.js主进程监听地址和端口 OPENCLAW_HOST0.0.0.0 OPENCLAW_PORT3000 # 日志级别生产环境强烈建议用WARNDEBUG会产生海量日志 LOG_LEVELWARN # 数据库连接OpenClaw目前只支持SQLite轻量和PostgreSQL生产 DB_TYPEsqlite DB_PATH/data/openclaw.db # 如果用PostgreSQL请注释掉上面两行启用下面三行 # DB_TYPEpostgres # DB_HOSTpostgres.internal # DB_PORT5432 # DB_NAMEopenclaw # DB_USERopenclaw # DB_PASSWORDyour_strong_password # 技能执行超时全局兜底值毫秒当skills.json里没指定timeout时生效 SKILL_DEFAULT_TIMEOUT15000 # 是否启用内置的Prometheus指标端点默认开启用于监控 METRICS_ENABLEDtrue METRICS_PORT9090skills.json文件的深层含义这个文件不只是一个技能列表它定义了OpenClaw的“能力拓扑”。每一个skill对象里的字段都对应着一个真实的系统资源或行为约束。id: 技能的唯一标识符也是HTTP API的路径前缀。例如id: pdf_table_extractor那么调用它的API就是POST /skill/pdf_table_extractor。这个ID会被用作Python子进程的--skill-id参数也是日志里区分不同技能的标签。type: 目前支持python、http、shell三种。python类型会启动一个Python子进程http类型会将请求透传给外部HTTP服务shell类型则会执行一条Shell命令慎用有安全风险。type决定了OpenClaw如何初始化和管理这个技能的生命周期。path: 对于python类型这是Python脚本的绝对路径。关键细节这个路径必须相对于OpenClaw主进程的工作目录通常是/app而不是相对于skills.json文件本身。很多人把脚本放在/app/skills/下却在skills.json里写path: skills/pdf_table.py结果启动时报FileNotFoundError。正确写法是path: /app/skills/pdf_table.py。timeout: 这是技能执行的硬性超时。OpenClaw主进程会在启动Python子进程后启动一个计时器。如果子进程在timeout毫秒内没有返回响应主进程会发送SIGTERM信号终止它。注意这个超时是针对单次执行不是子进程的存活时间。子进程本身是常驻的它会一直运行等待下一个请求。max_concurrent: 这是OpenClaw最精妙也最容易被误解的参数。它控制的是该技能对应的Python子进程的最大并发请求数。OpenClaw会为每个python技能维护一个“子进程池”。当第一个请求到来它会启动一个子进程第二个请求到来如果第一个还没处理完它会启动第二个以此类推直到达到max_concurrent。之后的请求会进入一个内存队列等待。这个队列的大小是无限的理论上但实际中你应该通过SKILL_DEFAULT_TIMEOUT来防止队列无限堆积。max_concurrent的值必须根据技能的资源消耗来定。一个纯CPU计算的技能max_concurrent设太高会导致CPU争抢整体吞吐下降一个IO密集的技能如调用外部API可以设得高一些。我们的经验值是CPU密集型技能max_concurrent≤ CPU核心数IO密集型技能max_concurrent≤ 10。提示skills.json文件在OpenClaw启动时被一次性加载到内存。如果你在运行时修改了它需要手动发送SIGUSR2信号给Node.js主进程kill -USR2 pid来触发热重载。这比重启服务快得多且不会中断正在处理的请求。3.3 技能开发规范一个合格的Python技能长什么样OpenClaw对Python技能的约束非常少但正是这种“少”反而让新手容易写出有问题的代码。一个能被OpenClaw稳定调用的Python技能必须遵循几个铁律。第一入口函数签名必须是def execute(input_data: dict) - dict:。这是OpenClaw Python子进程通信协议的硬性规定。input_data是Node.js主进程传来的JSON对象execute函数的返回值也必须是JSON序列化的字典。任何其他签名比如def run(data)或def main()都会导致调用失败。OpenClaw的Python子进程启动后会动态导入你指定的脚本然后反射调用execute函数。一个标准的PDF表格提取技能示例/app/skills/pdf_table.pyimport fitz # PyMuPDF import json import logging from typing import Dict, Any, List, Optional # 配置日志确保日志能被OpenClaw主进程捕获 logging.basicConfig(levellogging.INFO, format%(asctime)s - %(name)s - %(levelname)s - %(message)s) logger logging.getLogger(pdf_table_extractor) def execute(input_data: Dict[str, Any]) - Dict[str, Any]: 从PDF中提取所有表格并返回结构化JSON。 Args: input_data: 必须包含 pdf_bytes (bytes) 或 pdf_url (str) 键 Returns: 包含 tables (List[Dict]) 和 metadata (Dict) 的字典 try: # 1. 获取PDF内容 pdf_bytes input_data.get(pdf_bytes) if pdf_bytes is not None: # 直接从字节流加载 doc fitz.open(pdf, pdf_bytes) else: pdf_url input_data.get(pdf_url) if not pdf_url: raise ValueError(input_data must contain pdf_bytes or pdf_url) # 从URL下载 import requests response requests.get(pdf_url, timeout30) response.raise_for_status() doc fitz.open(pdf, response.content) # 2. 遍历每一页提取表格 tables [] for page_num in range(len(doc)): page doc[page_num] # 使用PyMuPDF的内置表格检测 tabs page.find_tables() for tab in tabs: # 将表格转换为二维列表 table_data [] for row in tab.extract(): table_data.append([cell.strip() if isinstance(cell, str) else str(cell) for cell in row]) tables.append({ page: page_num 1, data: table_data, bbox: list(tab.bbox) # 转换为普通list确保JSON序列化 }) # 3. 构建返回结果 result { success: True, tables: tables, metadata: { total_pages: len(doc), total_tables: len(tables), extracted_at: 2026-06-01T12:00:00Z } } logger.info(fExtracted {len(tables)} tables from {len(doc)} pages) return result except Exception as e: logger.error(fFailed to extract tables: {str(e)}, exc_infoTrue) return { success: False, error: str(e), metadata: {} } # 这行代码是必须的它告诉OpenClaw子进程这个脚本的入口是execute函数 if __name__ __main__: # OpenClaw子进程会自动调用execute这里只是占位防止脚本被直接运行 pass第二必须处理好异常和日志。OpenClaw主进程会捕获Python子进程的stdout和stderr并将它们作为日志的一部分输出。但如果你的技能代码里有未捕获的异常子进程会直接崩溃退出OpenClaw主进程会尝试重启它受restart_delay配置控制但这会造成短暂的服务不可用。因此execute函数内部必须用try...except包裹所有可能出错的逻辑并返回一个结构化的错误对象就像上面示例里做的那样。第三避免全局状态和资源泄漏。Python子进程是常驻的它会处理成百上千个请求。如果你在execute函数外定义了一个全局的数据库连接或大模型实例它会在所有请求间共享。这听起来很高效但极其危险。一个请求的错误可能导致全局连接失效影响所有后续请求。正确的做法是每个execute调用都应该是“无状态”的所有资源文件句柄、网络连接、模型实例都应该在函数内部创建并在函数结束时显式释放或依赖Python的垃圾回收。对于大模型可以考虑使用functools.lru_cache做轻量级缓存但要严格控制maxsize避免内存爆炸。实操心得在开发技能时强烈建议先用python3.11 /app/skills/pdf_table.py直接运行脚本看是否能正常导入和执行。如果报ModuleNotFoundError说明依赖没装全。OpenClaw的Python子进程不会自动帮你pip install所有依赖必须在容器构建时或系统层面提前安装好。4. 实操过程与核心环节实现从本地调试到Railway上线的全流程4.1 本地开发与调试用npm run dev启动的真相OpenClaw的package.json里通常有一个dev脚本看起来很简单dev: nodemon --watch config/ --watch skills/ --exec node index.js。但这个命令背后隐藏着本地开发效率的命脉。nodemon的作用不仅仅是“文件变化时重启”它还承担着环境隔离的重任。--watch config/确保skills.json修改后自动重载--watch skills/确保Python技能脚本修改后Node.js主进程会杀死旧的子进程启动新的子进程。这个“热替换”能力让你在改Python代码时完全不需要手动kill进程极大地提升了迭代速度。但nodemon也有坑。默认情况下它只会监听.js和.json文件而Python脚本.py不在其默认监听列表里。如果你只写了--watch skills/nodemon根本不会感知到.py文件的变化。解决方案是在nodemon.json配置文件中显式添加扩展名{ watch: [config/, skills/], ext: js,json,py, exec: node index.js }启动调试的完整流程准备Python环境在本地创建一个干净的Python 3.11.9虚拟环境并安装技能所需的所有依赖。python3.11 -m venv ./venv-skills source ./venv-skills/bin/activate # Linux/macOS # 或 ./venv-skills/Scripts/activate.bat # Windows pip install -r skills/requirements.txt配置.env和skills.json确保skills.json里的path指向你本地的绝对路径比如/Users/you/project/openclaw/skills/pdf_table.py。启动OpenClaw在项目根目录下运行npm run dev。测试调用用curl或Postman发送一个测试请求。注意因为是本地开发pdf_bytes需要Base64编码curl -X POST http://localhost:3000/skill/pdf_table_extractor \ -H Content-Type: application/json \ -d { pdf_bytes: $(base64 -i test.pdf | tr -d \n) }如果一切顺利你会看到结构化的表格JSON。如果失败nodemon终端里会实时打印出Node.js和Python子进程的完整日志这是调试的第一手资料。注意本地调试时DB_TYPEsqlite是最优选择因为它不需要额外的数据库服务。但要确保DB_PATH指向的目录有写入权限否则OpenClaw启动会卡在数据库初始化步骤日志里只显示Initializing database...没有后续。4.2 Docker镜像构建一个多阶段构建的精简实践为OpenClaw构建Docker镜像目标不是“能跑”而是“跑得稳、启动快、体积小、安全高”。我们摒弃了网上常见的FROM node:20-alpineRUN apk add python3的粗暴方式采用多阶段构建Multi-stage Build将构建环境和运行环境彻底分离。Dockerfile完整内容# 构建阶段安装所有构建依赖 FROM node:20.12.0-slim-bookworm AS builder # 安装Python 3.11.9 和构建工具 RUN apt-get update apt-get install -y \ python3.11 \ python3.11-venv \ python3.11-dev \ build-essential \ rm -rf /var/lib/apt/lists/* # 复制package.json和lock文件先安装Node.js依赖 WORKDIR /app COPY package*.json ./ RUN npm ci --onlyproduction # 复制源码和技能 COPY . . # 为Python技能创建一个临时venv并安装依赖 RUN python3.11 -m venv /tmp/skills-venv \ /tmp/skills-venv/bin/pip install --no-cache-dir -r skills/requirements.txt # 运行阶段极简的运行时环境 FROM node:20.12.0-slim-bookworm # 创建非root用户提升安全性 RUN groupadd -g 1001 -f nodejs useradd -S -u 1001 -U -m -d /home/nodejs nodejs USER nodejs # 复制构建阶段安装好的Node.js依赖和Python技能依赖 COPY --frombuilder --chownnodejs:nodejs /app/node_modules ./node_modules COPY --frombuilder --chownnodejs:nodejs /tmp/skills-venv ./skills-venv # 复制应用代码不包括dev依赖和测试文件 COPY --chownnodejs:nodejs --frombuilder /app/index.js . COPY --chownnodejs:nodejs --frombuilder /app/config ./config COPY --chownnodejs:nodejs --frombuilder /app/skills ./skills # 暴露端口 EXPOSE 3000 EXPOSE 9090 # 启动命令 CMD [node, index.js]这个Dockerfile的关键点在于第一阶段builder它包含了所有“重”的东西——Python解释器、编译工具、pip。它负责把node_modules和skills-venv这两个“产物”准备好。第二阶段运行它只继承了第一阶段的产物不带任何构建工具。最终镜像大小只有128MB左右对比单阶段的350MB启动时间从8秒缩短到2.3秒。更重要的是它以非root用户nodejs身份运行符合最小权限原则。Python依赖的处理我们没有在运行阶段再pip install而是把整个skills-venv目录复制过去。这是因为pip install在容器启动时执行会增加启动延迟且如果网络不稳定会导致容器启动失败。把依赖固化在镜像里保证了启动的确定性和一致性。构建和测试命令# 构建镜像 docker build -t openclaw-core:2026.6.1 . # 启动容器挂载本地config和skills目录便于开发时热更新 docker run -it \ -p 3000:3000 \ -p 9090:9090 \ -v $(pwd)/config:/app/config \ -v $(pwd)/skills:/app/skills \ -e OPENCLAW_PORT3000 \ -e LOG_LEVELINFO \ openclaw-core:2026.6.1 # 在另一个终端测试 curl http://localhost:3000/health4.3 Railway部署从零到上线的5步操作Railway的部署流程非常直观但其中几个关键步骤的配置直接决定了服务的健壮性。Step 1: 创建新服务登录Railway点击New Project-Create Empty Project。在项目仪表板点击 New Service-Deploy from GitHub。授权访问你的OpenClaw仓库选择正确的分支通常是main或release/2026.6.1。Step 2: 配置构建设置在服务设置页找到Build Deploy部分。Build Command:npm ci npm run build如果项目有前端构建步骤否则可留空。Start Command:node index.js。注意这里不能写npm start因为Railway的运行环境里没有npm只有node。Root Directory:/确保指向项目根目录而不是子文件夹。Step 3: 配置环境变量最关键的一步在Variables标签页点击Add Variable。按照.env文件的内容逐条添加。特别注意OPENCLAW_PORT必须设为3000。Railway会自动将3000端口映射到公网你不能改。DB_TYPE生产环境务必设为postgres。DB_HOSTRailway为PostgreSQL服务生成的内部DNS格式为service-name.internal比如你的PostgreSQL服务叫openclaw-db那么这里就填openclaw-db.internal。DB_PORTPostgreSQL的默认端口5432。DB_NAME,DB_USER,DB_PASSWORD这些值可以在你创建的PostgreSQL服务的Connect标签页里找到点击Show Credentials即可复制。Step 4: 配置持久化存储针对SQLite的备选方案如果你坚持用SQLite仅限测试需要为DB_PATH指定一个持久化路径。在Volumes标签页点击Add Volume。Mount Path:/data这和.env里的DB_PATH/data/openclaw.db要一致。Size:1 GB足够应付大多数场景。Step 5: 部署与验证点击右上角Deploy按钮。Railway会自动执行git pull-build-start流程。整个过程通常在2-3分钟内完成。部署成功后你会看到一个Public URL格式为https://random-string.up.railway.app。立即用浏览器访问https://random-string.up.railway.app/health应该返回{status:ok,timestamp:...}。然后用curl测试一个技能调用确认端到端链路畅通。实操心得第一次部署失败90%的原因是环境变量配置错误尤其是DB_HOST和DB_PASSWORD。Railway的错误日志里通常会显示Error: connect ECONNREFUSED或password authentication failed这时不要慌回到Variables页面逐字核对。一个常见的低级错误是把DB_PASSWORD里的特殊字符如、/直接复制过来而没有进行URL编码导致连接字符串解析失败。遇到这种情况可以把密码改成一个只含字母数字的简单密码再试。5. 常见问题与排查技巧实录那些让你