OpnForm v2.8.1 配置参数详解
OpnForm v2.8.1 双 .env + compose 三层配置机制实测详解:URL 四元组/密钥/驱动/上传四层链路/TRUSTED_PROXIES/遥测/MCP/品牌化逐项参数、restart 不加载 env 的 down-up 改参闭环与生产配置模板
OpnForm v2.8.1 配置参数详解
目标:把 OpnForm v2.8.1 自托管实例的两份
.env、compose 注入层与 nginx 模板层配置讲透——每个核心参数的含义、默认值、修改方法与生效条件,全部结论来自真实环境实测(2026-11-01/02,含改参→重建→验证闭环)。
一、环境信息
- 宿主:Linux x86_64(内网 IP
192.168.1.172),Docker29.9.2,Docker Composev5.6.1 - OpnForm:
v2.8.1(容器内实测APP_VERSION_DOCKER=2.8.1),Laravel 12.70.1 / PHP 8.3.36 - 部署目录:
/opt/data/quzuo/tmp/opnform/OpnForm,对外端口8161 - 配置文件:
api/.env(后端)、client/.env(前端)、docker-compose.yml+docker-compose.override.yml(注入层)、docker/nginx.conf(入口模板) - 7 服务容器:
opnform-api/opnform-api-worker/opnform-api-scheduler/opnform-client/opnform-db/opnform-redis/opnform-ingress - 演示账号:
g25admin@example.com/G25test@12345 - 本文全部回显取自已按 《OpnForm v2.8.1 Docker Compose 部署指南》 部署完成的实例
二、前置条件
- 一个已完成 setup 的实例(管理员已创建)。验证配置读取通道可用:
cd /opt/data/quzuo/tmp/opnform/OpnForm
docker compose -f docker-compose.yml -f docker-compose.override.yml exec -T api php artisan about
实测输出(节选):Environment=production、Debug Mode=OFF、URL=192.168.1.172:8161、Cache/Queue/Session=redis、Database=pgsql、Logs=errorlog、Mail=log、Config/Events/Routes/Views=CACHED、public/storage=LINKED。
- 理解 OpnForm 的三层配置来源(实测确认,这是本文所有"改了不生效"问题的根源):
| 层 | 来源 | 内容 | 解析时机 |
|---|---|---|---|
① compose ${VAR:-default} | 宿主 shell 环境变量 / compose 同目录根 .env | DB_DATABASE/DB_USERNAME/DB_PASSWORD(默认 forge)、DB_CONNECTION(默认 pgsql) | docker compose 解析 YAML 时 |
② compose environment: 静态块 | docker-compose.yml | DB_HOST=db、REDIS_HOST=redis、PHP_* 四变量 | 容器创建时注入 |
③ env_file | api/.env、client/.env | 应用密钥、URL 四元组、驱动、限流等 | 容器创建时注入 |
实测证据:api/.env 里没有任何 DB_* 行(setup-env.sh 生成的 .env.docker 模板不含),但容器内 printenv DB_HOST 返回 db、DB_DATABASE 返回 forge——全部来自 ①②层。官方文档同时注明:env_file 条目只在 Compose 文件解析完成后向容器传值,${...} 的解析用的是 shell/根 .env,两层机制不同。
三、参数查看与修改方式
3.1 查看当前生效值
# 文件层
cat api/.env
cat client/.env
# 容器实际生效层(推荐,能暴露"改了没生效")
docker compose -f docker-compose.yml -f docker-compose.override.yml exec -T api printenv JWT_TTL
# 1440
docker compose -f docker-compose.yml -f docker-compose.override.yml exec -T api printenv DB_HOST
# db
💡 说明:
printenv查的是容器进程真实环境;.env文件只是它的输入之一。两者不一致 = 你改完还没重建。
3.2 修改与生效:restart 不加载 env,必须 down/up(实测闭环)
官方文档明确"docker compose restart 不会重新加载 env 值",本环境做了逐变量实测闭环验证:
# 基线:变量未设置
docker compose -f docker-compose.yml -f docker-compose.override.yml exec -T api printenv OPNFORM_ANONYMOUS_TELEMETRY_DISABLED
# (无输出,rc=1)
echo 'OPNFORM_ANONYMOUS_TELEMETRY_DISABLED=true' >> api/.env
# 纠偏实验:restart 后仍读不到
docker compose -f docker-compose.yml -f docker-compose.override.yml restart api
docker compose -f docker-compose.yml -f docker-compose.override.yml exec -T api printenv OPNFORM_ANONYMOUS_TELEMETRY_DISABLED
# (无输出,rc=1)——实测证实 restart 不加载 env_file 新值
# 正确姿势:down 单服务 + up 重建(Compose v5.6.1 实测 down 可带服务名)
docker compose -f docker-compose.yml -f docker-compose.override.yml down api
docker compose -f docker-compose.yml -f docker-compose.override.yml up -d
docker compose -f docker-compose.yml -f docker-compose.override.yml exec -T api printenv OPNFORM_ANONYMOUS_TELEMETRY_DISABLED
# true
三个连带要点(均为实测):
down api伴随的Network opnform_default Resource is still in use警告无害(网络仍被其他服务使用,Compose 尝试删除失败而已);- api/api-worker/api-scheduler 共享同一 env_file 锚点,改一次 api/.env 三个服务都要重建才能消除配置漂移——实测
up -d后三者printenv同步变为true; - 重建 api 或 ui 后必须
restart ingress:nginx 启动时解析并缓存上游容器 IP,重建换 IP 后POST /api/login实测 502 Bad Gateway;docker compose -f docker-compose.yml -f docker-compose.override.yml restart ingress后恢复 200。
3.3 密钥的生成与保留
setup-env.sh 生成规则(读脚本实测):APP_KEY=base64: + openssl rand -base64 32;JWT_SECRET、FRONT_API_SECRET/NUXT_API_SECRET 为 tr -dc A-Za-z0-9 </dev/urandom | head -c 40 的 40 位字母数字串;FRONT_API_SECRET 与 NUXT_API_SECRET 写入同一共享密钥。重跑 docker-setup.sh 时已有 .env 的密钥保留不重新生成(脚本按"值非空则跳过"处理),只更新 URL 四元组。
四、参数总览
api/.env.example 共约 120 个变量、client/.env.example 14 个,按用途分组如下(★=自托管生产常需触碰):
| 组 | 代表变量 | 默认值(实测/文档) |
|---|---|---|
| URL 四元组★ | APP_URL FRONT_URL NUXT_PUBLIC_APP_URL NUXT_PUBLIC_API_BASE | http://localhost,setup 用 --public-url 一条写齐 |
| 密钥★ | APP_KEY JWT_SECRET FRONT_API_SECRET/NUXT_API_SECRET | 自动生成,重跑保留 |
| 驱动★ | CACHE_DRIVER QUEUE_CONNECTION SESSION_DRIVER DB_CONNECTION | docker 模板=redis×3、pgsql |
| 数据库注入★ | DB_HOST DB_PORT DB_DATABASE DB_USERNAME DB_PASSWORD | compose 层:db/5432/forge×3 |
| 上传链★ | PUBLIC_UPLOADS_MAX_FILE_SIZE PUBLIC_UPLOADS_RATE_LIMIT_PER_MINUTE/HOUR PHP_UPLOAD_MAX_FILESIZE PHP_POST_MAX_SIZE PHP_MEMORY_LIMIT PHP_MAX_EXECUTION_TIME NGINX_MAX_BODY_SIZE | 50000000 / 30 / 300 / 64M / 1G / 600 / 64m |
| 认证令牌★ | JWT_TTL JWT_REMEMBER_TTL JWT_SKIP_IP_UA_VALIDATION OAUTH_ENABLED OAUTH_ACCESS_TOKEN_TTL OAUTH_REFRESH_TOKEN_TTL_DAYS | 1440 / 43200 / false / true / 10080 / 30 |
| 反代★ | TRUSTED_PROXIES | 空(禁 *) |
| 遥测★ | OPNFORM_ANONYMOUS_TELEMETRY_DISABLED | 未设置=遥测开 |
| MCP/AI | MCP_ENABLED MCP_RATE_LIMIT_* MCP_OAUTH_REDIRECT_DOMAINS OPEN_AI_API_KEY | false / 120,3000 / 官方 allowlist / 空 |
| 品牌化 | NUXT_PUBLIC_SELF_HOSTED_NAME/LOGO_URL/PRIVACY_POLICY_URL/TERMS_URL NUXT_PUBLIC_ROOT_REDIRECT_URL | 名称默认 OpnForm |
| 用户与许可 | ADMIN_EMAILS MODERATOR_EMAILS TEMPLATE_EDITOR_EMAILS SHOW_OFFICIAL_TEMPLATES LICENSE_API_ENDPOINT | 空 / 空 / 空 / true / https://api.opnform.com |
| 验证码/集成 | H_CAPTCHA_* RE_CAPTCHA_* GOOGLE_CLIENT_ID/SECRET SLACK_BOT_TOKEN TELEGRAM_BOT_* | 空 |
| 邮件 | MAIL_MAILER MAIL_HOST MAIL_FROM_ADDRESS | docker 模板=log(不发真邮件) |
解读:真正需要人工决策的只有 ★ 组;其余留空即安全默认。MAIL_MAILER=log 意味着表单通知邮件只写日志不外发,上生产要么配 SMTP 要么接受通知静默。
五、核心参数分类详解
5.1 公开 URL 四元组
必配且必须一致(生产禁 localhost):
| 变量 | 文件 | 实测值 | 作用对象 |
|---|---|---|---|
APP_URL | api/.env | http://192.168.1.172:8161 | Laravel 生成链接/回调 |
FRONT_URL | api/.env | http://192.168.1.172:8161 | 后端拼前端跳转 |
NUXT_PUBLIC_APP_URL | client/.env | http://192.168.1.172:8161 | 浏览器侧应用 origin |
NUXT_PUBLIC_API_BASE | client/.env | http://192.168.1.172:8161/api | 浏览器侧 API base |
artisan about 的 URL 行实测回显 192.168.1.172:8161,即 APP_URL 去协议后的呈现,可作为验证点。另有一个模板自带、setup 不写的变量:NUXT_PRIVATE_API_BASE=http://ingress/api——Nuxt SSR 在服务端发起的 API 调用走 compose 内部网络直达 ingress,不绕公网;改公网 URL 时它保持不动。
5.2 密钥与共享密钥
APP_KEY:Laravel 加密键(base64:前缀 + 32 字节随机)。丢失则已加密数据不可解、全部会话失效,必须随.env一起备份。JWT_SECRET:登录令牌签名键。实测登录响应{"token":"eyJ0eXAi…","token_type":"bearer","expires_in":86400}。FRONT_API_SECRET(api 侧)=NUXT_API_SECRET(client 侧):前后端内部调用共享密钥,setup-env.sh 保证同值。JWT_SKIP_IP_UA_VALIDATION:把令牌与客户端 IP/UA 绑定校验关掉。官方脚本对生产配置直接拒绝:docker-setup.sh检测到true即exit 1(读脚本实测);仅docker-compose.dev.yml开发场景自动设 true。
💡 技术拓展:OpnForm 有两套令牌体系。第一方登录用 JWT(tymon/jwt-auth 风格,
JWT_TTL控制);MCP/OAuth 开放集成用 Laravel Passport OAuth 2.1(OAUTH_*变量、oauth_*数据表实测存在)。对比——
体系 载体 适用 JWT 自签名令牌,无状态 自家前后端会话 Passport OAuth 2.1 授权码+访问/刷新令牌,有状态 第三方客户端、MCP agent 本文环境实测:
OAUTH_ENABLED默认 true(为 MCP 预留),但MCP_ENABLED=false,对外 OAuth 面未激活。
5.3 数据库与 Redis(compose 注入层)
docker-compose.yml 实测注入:api 系 DB_HOST=db、REDIS_HOST=redis、DB_CONNECTION=${DB_CONNECTION:-pgsql}、DB_DATABASE/DB_USERNAME/DB_PASSWORD=${…:-forge};db 服务 POSTGRES_DB/USER/PASSWORD=${…:-forge} 与 api 侧同源——改库口令只需在宿主设一次 DB_PASSWORD 环境变量(或根 .env),两侧同时生效。
artisan db:show 实测确认连接参数:PostgreSQL 16.15 (Debian 16.15-1.pgdg13+2)、Database=forge、Host=db、Port=5432、Username=forge、Tables=44(该命令打印完摘要后会因镜像缺 intl 扩展而崩溃,属官方镜像已知限制,不影响配置生效)。
💡 技术拓展:
DB_CONNECTION存在多后端可选。官方 compose 钉死postgres:16,但 tech-stack 文档声明支持 MySQL/MariaDB,README 迁移说明提及 MySQL/SQLite 走标准索引迁移。对比——
驱动 支持度 说明 pgsql 官方 compose 默认,实测通过 唯一开箱即用路径 mysql/mariadb 框架支持 需自建 compose 换 db 服务 sqlite 仅开发/测试 生产不建议 本方案采用默认 pgsql:与官方镜像、迁移脚本(
CREATE INDEX CONCURRENTLY为 PG 专属写法)一致性最高。
💡 技术拓展:
QUEUE_CONNECTION/CACHE_DRIVER/SESSION_DRIVER三驱动在 Laravel 里可选多种实现。对比——
驱动 特点 适用 sync 进程内同步执行,无 worker 开发调试(api/.env.example 默认值) redis 独立队列+高速缓存,需 redis 与 worker 进程 生产标配(docker 模板默认) database 队列表落库,零额外中间件 低量任务、不想跑 redis 本环境实测三驱动全
redis:提交落库因此是异步的(answer 返回submission_id:null,约 5-8 秒后可见),依赖api-worker存活。
5.4 认证与令牌生命周期
JWT_TTL=1440(分钟,docker 模板值;.env.example通用模板为 10080):实测登录响应expires_in: 86400秒与之吻合;Set-Cookie OpnForm_sessionMax-Age=7200 为会话 cookie 独立参数。JWT_REMEMBER_TTL=43200(30 天):勾选 remember 时的令牌寿命。OAUTH_ACCESS_TOKEN_TTL=10080(7 天)、OAUTH_REFRESH_TOKEN_TTL_DAYS=30:Passport 令牌,不影响第一方 JWT。
5.5 上传大小与限流(四层链路)
匿名附件从浏览器到落盘要过四道闸,实测取值:
| 层 | 变量 | 值 | 位置 |
|---|---|---|---|
| nginx | NGINX_MAX_BODY_SIZE | 64m | compose ingress environment → 模板 client_max_body_size |
| PHP | PHP_UPLOAD_MAX_FILESIZE / PHP_POST_MAX_SIZE | 64M / 64M | compose api 系 environment(另 PHP_MEMORY_LIMIT=1G、PHP_MAX_EXECUTION_TIME=600) |
| 应用 | PUBLIC_UPLOADS_MAX_FILE_SIZE | 50000000(50 MB) | api/.env.example 默认 |
| 限流 | PUBLIC_UPLOADS_RATE_LIMIT_PER_MINUTE/HOUR | 30 / 300 | setup 写入 api/.env(实测在文件内) |
解读:实际匿名上传上限取最小者 50 MB(应用层);要放大需三层同时调并重建容器。表单素材(logo/封面)另有独立 5 MB 限制(官方文档)。
5.6 TRUSTED_PROXIES(反代真实 IP)
前置 Caddy/Traefik/云 LB 时,在 api/.env 设反代连接来源的 IP 或 CIDR(官方示例形态:TRUSTED_PROXIES=192.0.2.10,2001:db8:1234::/48)。nginx 模板注释与官方文档双重强调:禁 *——否则直连客户端伪造 X-Forwarded-For 即可绕过按 IP 的限流(OIDC、公开上传等)。直连 ingress 部署则留空。修改后按 3.2 流程 down/up 重建 api 系。
5.7 匿名遥测
OPNFORM_ANONYMOUS_TELEMETRY_DISABLED=true 关闭。激活条件(官方文档+实测核对):APP_ENV=production 且自托管(SELF_HOSTED=true),开发环境自动禁用。采集面:功能使用计数、匿名实例 UUID、表单类型分布;不含表单内容、提交数据、用户邮箱。3.2 节已实测验证该变量从"未设置"到"true"的完整改参闭环。
5.8 MCP 与 AI 代理
自托管默认 MCP_ENABLED=false(.env.example 注释与官方文档一致)。启用后限流参数实测默认:MCP_RATE_LIMIT_PER_MINUTE=120、MCP_RATE_LIMIT_PER_HOUR=3000、MCP_SUBMISSION_EXPORTS_PER_MINUTE=5/HOUR=30、FORM_SUMMARY_RATE_LIMIT_PER_MINUTE=30;回调白名单 MCP_OAUTH_REDIRECT_DOMAINS(chatgpt.com/claude.ai/loopback 等)、原生 scheme 白名单 MCP_OAUTH_CUSTOM_SCHEMES=chatgpt,claude,cursor,vscode。启用前置:公网 HTTPS + Passport 密钥(PASSPORT_PRIVATE_KEY/PUBLIC_KEY,serverless 部署必须钉住)。AI 建表单独依赖 OPEN_AI_API_KEY(默认空=功能不可用)。
5.9 品牌化与前端变量
NUXT_PUBLIC_SELF_HOSTED_NAME(默认 OpnForm)、NUXT_PUBLIC_SELF_HOSTED_LOGO_URL、NUXT_PUBLIC_SELF_HOSTED_PRIVACY_POLICY_URL、NUXT_PUBLIC_SELF_HOSTED_TERMS_URL、NUXT_PUBLIC_ROOT_REDIRECT_URL(把欢迎页/未知路径 301 到你的站点,表单与应用页不受影响)。
品牌名生效位置做了实测:client/.env 追加 NUXT_PUBLIC_SELF_HOSTED_NAME=G25Brand → down ui + up -d ui 后容器内 printenv 返回 G25Brand;/login 页 HTML 出现 G25Brand(grep 命中 1 处),但公开表单页 <title> 不变(仍 G25 反馈表单 - OpnForm)——品牌名只作用于欢迎页/导航,表单页标题走表单自身 title。回退实验:删除该行重建后变量消失,7 服务恢复 healthy。
5.10 用户、许可与模板
ADMIN_EMAILS/MODERATOR_EMAILS/TEMPLATE_EDITOR_EMAILS:逗号分隔邮箱直接授予对应角色;EXTRA_PRO_USERS_EMAILS追加 Pro 配额。SHOW_OFFICIAL_TEMPLATES(默认 true):设 false 隐藏官方模板库(纯内网合规场景常用)。LICENSE_API_ENDPOINT(默认https://api.opnform.com):Enterprise 许可校验云端点;自托管免费版上限 2 用户(实测 setup 后注册关闭,plan_tier=pro但受实例上限约束),更多用户与高级特性需激活 Enterprise license(Settings → License路由实测存在于 api/routes.php)。
六、生产配置模板
适用画像:公网域名 forms.example.com、前置反代终结 TLS、8 GB 内存、50 GB SSD、单实例自托管(≤2 用户免费档或已购 Enterprise)。
api/.env(密钥行由 setup 自动生成,此处占位并给出注入方式):
APP_NAME="OpnForm"
APP_ENV=production
APP_KEY=${APP_KEY} # 由 ./scripts/docker-setup.sh 生成; 备份后原样保留
APP_DEBUG=false
APP_URL=https://forms.example.com
FRONT_URL=https://forms.example.com
LOG_CHANNEL=errorlog
CACHE_DRIVER=redis
QUEUE_CONNECTION=redis
SESSION_DRIVER=redis
MAIL_MAILER=smtp # 生产改 smtp, 配套 MAIL_HOST/PORT/USERNAME/PASSWORD/FROM_ADDRESS
MAIL_HOST=smtp.example.com
MAIL_PORT=587
MAIL_USERNAME=${SMTP_USER} # 从密管系统注入: export SMTP_USER=...
MAIL_PASSWORD=${SMTP_PASS} # export SMTP_PASS=...
MAIL_ENCRYPTION=tls
MAIL_FROM_ADDRESS=form@example.com
MAIL_FROM_NAME="OpnForm"
JWT_TTL=1440
JWT_SECRET=${JWT_SECRET} # setup 生成, 勿手填弱值
JWT_SKIP_IP_UA_VALIDATION=false
FRONT_API_SECRET=${FRONT_API_SECRET}
PUBLIC_UPLOADS_MAX_FILE_SIZE=50000000
PUBLIC_UPLOADS_RATE_LIMIT_PER_MINUTE=30
PUBLIC_UPLOADS_RATE_LIMIT_PER_HOUR=300
TRUSTED_PROXIES=10.0.0.5 # 你的反代真实来源 IP/CIDR, 严禁 *
OPNFORM_ANONYMOUS_TELEMETRY_DISABLED=true
MCP_ENABLED=false
SHOW_OFFICIAL_TEMPLATES=false
client/.env:
NUXT_PUBLIC_APP_URL=https://forms.example.com
NUXT_PUBLIC_API_BASE=https://forms.example.com/api
NUXT_PRIVATE_API_BASE=http://ingress/api
NUXT_API_SECRET=${FRONT_API_SECRET} # 与 api 侧同值, setup 自动对齐
NUXT_PUBLIC_SELF_HOSTED_NAME=MyCorp Forms
NUXT_PUBLIC_ENV=production
数据库口令(宿主根 .env,与 compose 同目录,供 ①层解析):
DB_DATABASE=opnform
DB_USERNAME=opnform
DB_PASSWORD=${DB_PASS} # export DB_PASS=... 或写入该文件实际值
⚠️ 注意:
${VAR}仅出现在系统级真实密钥的书写规范中(同值注入方式已注明);三文件写完后按 3.2 流程down && up -d全量重建,并restart ingress。
七、常见问题
7.1 改了 .env 没生效
restart 不加载 env_file 新值(3.2 实测 rc=1),必须 down <svc> && up -d;涉及 api/.env 时 api/api-worker/api-scheduler 三个服务一起重建。
7.2 重建 api 后整站 502
nginx 缓存旧上游 IP,docker compose -f docker-compose.yml -f docker-compose.override.yml restart ingress 恢复(实测闭环)。
7.3 想改数据库口令却改了 api/.env
api/.env 里没有 DB_* 行(实测);口令在 compose ①层 ${DB_PASSWORD:-forge},应在宿主 shell 或 compose 同目录根 .env 设置,同时重建 db 与 api 系(已有数据卷内口令不会自动变更,需进 psql ALTER USER)。
7.4 表单通知邮件没收到
docker 模板默认 MAIL_MAILER=log,邮件只写日志。按第六节模板改 smtp 并重建。
7.5 品牌名改了但表单页标题没变
NUXT_PUBLIC_SELF_HOSTED_NAME 只影响欢迎页/导航(5.9 实测);表单页 <title> 由表单 title 决定,属预期行为。
7.6 注册新用户被拒
setup 完成后公开注册关闭(实测 Registration is not allowed.),用管理员邀请;免费实例上限 2 用户,属许可约束而非配置项。
7.7 TRUSTED_PROXIES 能设 * 吗
不能。官方文档与 nginx 模板注释双重禁止:* 让任意直连客户端伪造 IP、绕过按 IP 限流。留空=直连部署,填具体反代 IP/CIDR。