HeadlessX v2.1.2 配置参数详解
HeadlessX v2.1.2 三层配置体系拆解:.env 环境变量全量分组、安全强制键的启动校验与 AES-256-GCM 加密机制、/api/config 运行态配置实时修改实测,附 8-16 GB 内存档生产模板。
HeadlessX v2.1.2 配置参数详解
HeadlessX 的配置分三层:环境变量(进程启动时读入,重启生效)、运行态配置(存数据库,改了即时生效)、安全强制校验(缺配置直接拒绝启动)。本文基于 《HeadlessX v2.1.2 Docker Compose 部署指南》 完成的标准部署,对每类参数做实测验证——所有输出均来自真实服务,参数行为均经重启或调用验证。
一、环境信息
| 项目 | 说明 |
|---|---|
| 软件版本 | HeadlessX v2.1.2(仓库 main 分支,commit e3d938b) |
| 部署形态 | 标准部署(部署方法见本专栏部署篇,7 个服务全部运行) |
| 服务器 | Ubuntu 22.04.4 LTS,12 核 32 GB,内网地址 192.168.146.128 |
| 服务端口 | API 38473,控制台 34872(官方默认值) |
二、前置条件
- 已按部署篇完成 HeadlessX 部署并跑通
/api/health; - 手上有一把 API Key:部署期的内部管理键(.env 中
DASHBOARD_INTERNAL_API_KEY的值),或用它在/api/keys创建的用户键; - 了解 .env 文件位置与编辑方法(见部署篇第 2 步;下文统一称"部署 .env")。
三、配置体系与查看方式
3.1 三层配置载体
| 层 | 载体 | 生效方式 | 适用参数 |
|---|---|---|---|
| 环境变量 | 部署 .env | 进程启动时读入,需重启服务进程 | 端口、数据库、队列、外部服务地址、安全键 |
| 运行态配置 | PostgreSQL(控制台设置页 / PATCH /api/config) | 保存即生效,无需重启 | 浏览器并发、stealth 档位、代理、浏览器窗口等 |
| 强制校验 | 进程启动断言 | 启动时检查,不满足拒绝启动 | 两把安全键 |
3.2 环境变量的加载机制
API 进程按以下顺序寻找 .env 文件(源码 apps/api/src/env.ts):
apps/api/.env.local(应用级,优先);- 仓库根目录
.env; - 两者都不存在时,直接使用进程注入的环境变量(标准部署即此形态,部署层已把部署 .env 注入进程环境)。
实测启动日志中的两处证据:
✅ Prisma migrations applied.
🚀 HeadlessX Backend running on http://0.0.0.0:8000
📂 Environment: production
进程内 dotenv 的注入行(标准部署下文件侧注入 0 个变量,值全部来自编排层注入):
[dotenv@17.2.3] injecting env (0) from .env
结论:标准部署改参数只改部署 .env,然后重启对应服务进程(启停命令见部署篇第十章);给应用目录另放 .env.local 属于源码开发形态,部署态不建议。
3.3 参数查看方式
| 看什么 | 命令 | 说明 |
|---|---|---|
| 运行快照(引擎/能力/并发) | curl -s http://localhost:38473/api/health | 免认证 |
| 运行态配置全量 | curl -s http://localhost:38473/api/config -H "x-api-key: <APIKey>" | 13 项,可改 |
| 队列引擎状态 | curl -s http://localhost:38473/api/jobs/metrics -H "x-api-key: <APIKey>" | 队列可达性 |
| 各模块状态 | curl -s http://localhost:38473/api/operators/status -H "x-api-key: <APIKey>" | 搜索/YouTube/浏览器 |
环境变量属于进程启动参数,运行中无法通过 API 查询,以部署 .env 文件内容为准。
四、环境变量总览
部署 .env 的全部变量按用途分 7 组(★ 为强制必填,缺则拒绝启动):
① 安全强制类
| 变量 | 默认值 | 说明 |
|---|---|---|
| DASHBOARD_INTERNAL_API_KEY ★ | (空) | 内部管理键:控制台服务端代理、/api/keys 管理命令的认证凭据;MCP 端点显式拒绝它 |
| CREDENTIAL_ENCRYPTION_KEY ★ | (空) | 凭证加密键:派生 AES-256-GCM 密钥,加密存储的各平台登录态 |
② 端口类
| 变量 | 默认值 | 说明 |
|---|---|---|
| POSTGRES_HOST_PORT | 35432 | 数据库宿主端口 |
| REDIS_HOST_PORT | 36379 | 队列宿主端口 |
| HTML_TO_MARKDOWN_HOST_PORT | 38081 | HTML→Markdown 微服务宿主端口 |
| YT_ENGINE_HOST_PORT | 38090 | YouTube 引擎宿主端口 |
| WEB_HOST_PORT | 34872 | 控制台宿主端口 |
| API_HOST_PORT | 38473 | API 宿主端口 |
| PORT | 8000 | API 进程监听端口(内部,勿动) |
| WEB_PORT | 3000 | 控制台进程监听端口(内部,勿动) |
| HOST | 0.0.0.0 | API 监听地址 |
③ 数据库类
| 变量 | 默认值 | 说明 |
|---|---|---|
| POSTGRES_USER | postgres | 数据库用户 |
| POSTGRES_PASSWORD | postgres | 数据库密码 |
| POSTGRES_DB | headlessx | 数据库名 |
| DATABASE_URL | postgresql://postgres:postgres@postgres:5432/headlessx | API 进程使用的连接串 |
| REDIS_URL | redis://redis:6379 | 队列连接串 |
④ 队列类
| 变量 | 默认值 | 说明 |
|---|---|---|
| BULLMQ_QUEUE_NAME | headlessx-jobs | BullMQ 队列名 |
| QUEUE_WORKER_CONCURRENCY | 2 | Worker 同时处理的作业数 |
| QUEUE_JOB_ATTEMPTS | 3 | 作业失败重试次数 |
| QUEUE_JOB_BACKOFF_MS | 5000 | 重试退避间隔(毫秒) |
| QUEUE_STREAM_POLL_MS | 1000 | 事件流轮询间隔(毫秒) |
⑤ 搜索引擎类
| 变量 | 默认值 | 说明 |
|---|---|---|
| TAVILY_API_KEY | (空) | Tavily 云搜索键 |
| EXA_API_KEY | (空) | Exa 云搜索键 |
⑥ YouTube 引擎类
| 变量 | 默认值 | 说明 |
|---|---|---|
| YT_ENGINE_URL | http://yt-engine:8090 | 引擎服务地址 |
| YT_ENGINE_TIMEOUT_MS | 45000 | 引擎调用超时(毫秒) |
| YT_ENGINE_PORT | 8090 | 引擎监听端口(内部) |
| YT_ENGINE_TEMP_DIR | /app/tmp/yt-engine | 媒体暂存目录 |
| YT_ENGINE_JOB_TTL_HOURS | 12 | 产物保留时长(超时自动清理) |
⑦ HTML→Markdown 与控制台类
| 变量 | 默认值 | 说明 |
|---|---|---|
| HTML_TO_MARKDOWN_SERVICE_URL | http://html-to-md:8080 | 微服务地址 |
| HTML_TO_MARKDOWN_TIMEOUT_MS | 60000 | 微服务超时(毫秒) |
| NEXT_PUBLIC_API_URL | http://localhost:38473 | 控制台回连 API 的地址,远程访问改为 http://<服务器IP>:38473 |
| INTERNAL_API_URL | http://api:8000 | 控制台内部回环地址(部署模板内服务名,勿改) |
| FRONTEND_URL | http://localhost:34872 | API 侧记录的前端回调地址,远程访问同步改 |
| BROWSER_WINDOW_WIDTH / HEIGHT | (空,默认 1366×768) | 可选,覆盖浏览器窗口尺寸,非法值会告警忽略 |
服务间地址(REDIS_URL、YT_ENGINE_URL、INTERNAL_API_URL 等)在标准部署下保持模板值即可,它们指向部署网络内的固定服务名。
五、安全强制参数实测
5.1 校验逻辑
进程启动时执行 assertSecurityConfiguration()(源码 apps/api/src/utils/security.ts):检查两把安全键 trim() 后非空,任一为空即抛错终止,不设长度门槛。但 CREDENTIAL_ENCRYPTION_KEY 的实际用途是 sha256 派生 AES-256-GCM 密钥去加密凭证(密文带 enc-v1: 前缀),从强度出发务必用完整随机值:
openssl rand -hex 16
5.2 缺键拒绝启动(实测)
把部署 .env 中 DASHBOARD_INTERNAL_API_KEY 置空,重启 API 进程,服务立即失败,日志:
🚀 Starting HeadlessX API Server...
[dotenv@17.2.3] injecting env (0) from .env
❌ Failed to start server: Error: Missing required security environment variables: DASHBOARD_INTERNAL_API_KEY
at assertSecurityConfiguration (/app/apps/api/src/utils/security.ts:29:15)
at startServer (/app/apps/api/src/server_entry.ts:14:9)
把键值填回(本例 hx-dash-internal-b5b8f2e2453a5ab9519c57d006e446ff)并重启,恢复正常序列:
✅ Prisma migrations applied.
🚀 HeadlessX Backend running on http://0.0.0.0:8000
📂 Environment: production
⚠️ 两把键的对应关系:DASHBOARD_INTERNAL_API_KEY 泄露等于交出管理面(可创建/吊销用户键、读任务日志),CREDENTIAL_ENCRYPTION_KEY 泄露等于交出所有已保存的平台登录态,且更换加密键后旧密文无法解密——已存的登录态会失效,换键前请评估。
六、运行态配置详解(/api/config)
运行态配置存数据库,控制台"设置"页与 API 读写同一份。查询:
curl -s http://localhost:38473/api/config -H "x-api-key: hx_e9a271e7c086cb0f3ff3c2f9c49c0bbefc31fd02441f7c96"
实测输出(部署初值):
{"success":true,"config":{"browserHeadless":true,"browserTimeout":60000,"maxConcurrency":5,"stealthMode":"advanced","proxyEnabled":false,"proxyProtocol":"http","profileRotation":false,"profileRotationInterval":3600000,"camoufoxGeoip":true,"camoufoxBlockWebrtc":true,"camoufoxBlockImages":false,"camoufoxEnableCache":true,"camoufoxHumanize":2.5}}
13 项逐条解释:
| 配置项 | 初值 | 说明 |
|---|---|---|
| browserHeadless | true | 无头模式;排障时改 false 观察浏览器窗口 |
| browserTimeout | 60000 | 单页操作超时(毫秒) |
| maxConcurrency | 5 | 浏览器最大并发实例数 |
| stealthMode | advanced | 隐匿档位(配合 Camoufox 指纹浏览器) |
| proxyEnabled | false | 出口代理总开关 |
| proxyProtocol | http | 代理协议(http/socks5) |
| profileRotation | false | 浏览器用户目录轮换开关 |
| profileRotationInterval | 3600000 | 轮换间隔(毫秒) |
| camoufoxGeoip | true | Camoufox 按 IP 一致性调整地理/区域指纹 |
| camoufoxBlockWebrtc | true | 屏蔽 WebRTC 泄露真实 IP |
| camoufoxBlockImages | false | 屏蔽图片加载(省流提速,可能影响截图) |
| camoufoxEnableCache | true | 浏览器缓存开关 |
| camoufoxHumanize | 2.5 | 拟人化鼠标/交互强度 |
修改实测——把并发从 5 降到 3:
curl -s -X PATCH http://localhost:38473/api/config \
-H "x-api-key: hx_e9a271e7c086cb0f3ff3c2f9c49c0bbefc31fd02441f7c96" \
-H "Content-Type: application/json" \
-d '{"maxConcurrency":3}'
实测返回(maxConcurrency 已变为 3,保存即生效,无需重启):
{"success":true,"config":{...,"maxConcurrency":3,"stealthMode":"advanced",...}}
GET /api/health 的快照同步变化。测完恢复 {"maxConcurrency":5}。
与运行态相关的另一处入口是 /api/health,它反映的 maxConcurrency、stealthMode、proxy 与 /api/config 同源:
{
"status": "online",
"capabilities": { "website": true, "search": false, "youtube": true },
"captchaModels": { "classification": false, "detection": false },
"searchProviders": { "tavily": false, "exa": false },
"maxConcurrency": 5,
"stealthMode": "advanced",
"proxy": { "enabled": false, "count": 0 }
}
(search: false、searchProviders 双 false 是未配置 Tavily/Exa 键的正常表现;captchaModels 双 false 是未放置可选 ONNX 模型。)
七、外部模块参数实测
队列参数:QUEUE_WORKER_CONCURRENCY=2 等队列参数随 Worker 进程启动生效。实测提交 crawl 作业后,Worker 日志出现完整生命周期:
👷 HeadlessX Queue Worker is running
📋 Job created: 36517871-50cb-4596-928d-eb83210e280f
📋 Job 36517871-50cb-4596-928d-eb83210e280f status: running
✅ Queue job completed: 36517871-50cb-4596-928d-eb83210e280f
GET /api/jobs/metrics 确认队列引擎可达:
{"success":true,"metrics":{"queueName":"headlessx-jobs","redisTarget":"redis://redis:6379","available":true,"counts":{"active":1,"waiting":0,"delayed":0,"completed":0,"failed":0,"paused":0}}}
搜索引擎参数:TAVILY_API_KEY、EXA_API_KEY 留空时模块状态实测(/api/operators/tavily/status):
{"success":true,"data":{"status":"online","configured":false,"service":"tavily-v1"}}
configured: false 表示服务在线但未配键,真正调用搜索面时会报未配置。填入有效键并重启 API 进程后此状态翻转为 true。
YouTube 引擎参数:/api/operators/youtube/status 实测:
{"success":true,"data":{"status":"online","configured":true,"service":"yt-dude-v1","ffmpeg_available":true,"temp_dir":"/app/tmp/yt-engine","job_ttl_hours":12}}
configured: true 表示引擎服务就绪(这与 TAVILY_API_KEY 的语义不同:引擎是自托管组件,配好即在线)。但引擎就绪 ≠ YouTube 可达:本例服务器出口无法访问 YouTube,/api/operators/youtube/info 45 秒后按 YT_ENGINE_TIMEOUT_MS 超时报 YOUTUBE_ENGINE_FAILED。这类"参数全对但出口不通"的情况需先解决服务器出网。
八、生产配置模板(8-16 GB 内存档)
在官方默认基础上建议改动的项(其余保持默认):
# ── 安全键:务必重新生成,不要照抄 ──
DASHBOARD_INTERNAL_API_KEY=<openssl rand -hex 16 输出>
CREDENTIAL_ENCRYPTION_KEY=<openssl rand -hex 16 输出>
# ── 数据库:改默认密码,并与 DATABASE_URL 同步 ──
POSTGRES_PASSWORD=<强密码>
DATABASE_URL=postgresql://postgres:<强密码>@postgres:5432/headlessx
# ── 远程访问:按服务器实际地址填写 ──
NEXT_PUBLIC_API_URL=http://<服务器IP>:38473
FRONTEND_URL=http://<服务器IP>:34872
# ── 搜索面:用到才填 ──
TAVILY_API_KEY=<你的Tavily键>
EXA_API_KEY=<你的Exa键>
# ── 队列:8 GB 内存档建议保守并发 ──
QUEUE_WORKER_CONCURRENCY=2
QUEUE_JOB_ATTEMPTS=3
资源取向:每个浏览器实例约占 0.5~1 GB 内存,16 GB 服务器建议运行态 maxConcurrency 不超过 6、QUEUE_WORKER_CONCURRENCY 保持 2;主服务部署产物内含指纹浏览器束(约 0.7 GB),磁盘预留 20 GB。
九、常见问题
Q1:改了部署 .env 不生效?
环境变量在进程启动时读入。改完必须重启所有引用该变量的服务进程(安全键、数据库、队列参数涉及 api 与 worker 两个进程;控制台类参数涉及 web 进程)。只改文件不重启是最常见的"配置失效"。
Q2:NEXT_PUBLIC_API_URL 改了之后要不要重新构建?
标准部署不需要。该值在控制台服务端被消费(源码中仅服务端引用),属于运行时注入,改部署 .env 后重启 web 进程即生效。只有源码开发形态(本地构建前端)才涉及构建期注入问题。
Q3:改了 POSTGRES_PASSWORD 后数据库连不上?
DATABASE_URL 里写死了密码,两处必须同步改。只改 POSTGRES_PASSWORD 不改 DATABASE_URL,API 进程会用旧密码连接新密码库,报认证失败。
Q4:运行态配置和环境变量重复的参数以谁为准?
maxConcurrency、stealthMode 等运行态项只认数据库配置(.env 中没有对应变量),两者不冲突;/api/health 展示的永远是运行态值。环境变量管"进程级"(端口、连接、超时),运行态管"行为级"(浏览器、代理、隐匿),职责不重叠。
Q5:安全键可以填短一点好记吗?
校验只要求非空,短键也能启动。但内部管理键等价于管理面凭据、加密键等价于凭证保险柜钥匙,强烈建议 openssl rand -hex 16 生成 32 位随机值。注意加密键一经使用就不要再换——换键后旧密文无法解密。
Q6:搜索键填错了怎么发现?
看 /api/operators/tavily/status(或 exa):服务在线返回 configured: true 只代表键存在,键无效会在实际调用时暴露,任务日志(GET /api/logs)里会留下失败记录。