Odoo 20 配置参数详解

Odoo 20 社区版 odoo.conf 逐节详解:参数加载与覆盖优先级、全参数默认值(config.py my_default 取证)、admin_passwd/list_db/dbfilter 实测语义(403/AccessDenied/锁库路由/CSRF 强制)、多进程 workers 与 limit_* 系列官方计算公式、日志与 SMTP 参数、环境变量对照与生产配置模板。

OdooOdooERPPostgreSQL2026/10/27

Odoo 20 配置参数详解

目标:把 Odoo 20 社区版服务端的 odoo.conf 配置体系逐项讲透——参数如何加载与覆盖、每个核心参数的真实默认值与实测行为、多进程模型与资源限制的计算依据、多数据库与 dbfilter 路由语义、环境变量对照与生产配置模板。全部参数默认值取自 Odoo 20 服务端源码 odoo/tools/config.py,全部行为结论来自 2026-10-27 在 Ubuntu 22.04 上的真实实测。部署过程见 《Odoo 20 Docker Compose 部署实战》,命令行操作见 《Odoo 20 常用命令与运维实战》。

一、环境信息

  • 操作系统:Ubuntu 22.04(x86_64,真实服务器视角)
  • Odoo:社区版 Odoo Server 20.0-20261015(odoo --version 实测回显),以 deb 包形态安装、systemd 服务 odoo.service 托管
  • 数据库:PostgreSQL 17(与 Odoo 同机部署,监听 5432)
  • 配置文件:/etc/odoo/odoo.conf(deb 包安装的默认配置路径)
  • 服务端口:HTTP 8069(默认)、WebSocket/长轮询 8072(默认)
  • 实测账号:PostgreSQL 角色 odoo / odoo20pg;Odoo 应用管理员 admin / q1w2e3r4(演示口令);数据库管理器主控口令实测值 q1w2e3r4
  • 实测数据库:odoo20main(空库)、odoo20demo(演示数据库,已安装 CRM/库存)、odoo20restore(备份恢复库)、odoo20clirestore(CLI 恢复库)

二、前置条件

  1. 服务器上已有一套可运行的 Odoo 20(deb 包安装或源码运行均可),且 odoo --version 能正常回显版本:

    odoo --version
    

    实测回显:

    Odoo Server 20.0-20261015
    
  2. 已知配置文件位置。Odoo 按以下顺序确定配置文件(找到即用,找不到则以内置默认值运行):

    • 命令行 --config=/path/to/odoo.conf(优先级最高);
    • 环境变量 ODOO_RC 指定的路径;
    • ~/.odoorc(用户级默认,老版本兼容 ~/.openerp_serverrc)。
  3. 服务重启方式(deb 安装形态):

    sudo systemctl restart odoo
    

    配置文件修改后必须重启服务才生效;验证服务状态:

    systemctl is-active odoo
    

    预期回显(正常运行时):

    active
    
  4. 数据库管理器的验证走 HTTP 接口(本篇所有接口验证均以服务监听 8069 为前提):

    curl -sI http://127.0.0.1:8069/web/database/selector | head -1
    

    实测回显:

    HTTP/1.1 200 OK
    

三、参数查看与修改方式

配置参数的「查看—修改—验证」闭环是配置运维的基本功。Odoo 20 的参数有四个来源层次:源码内置默认值 → 配置文件 odoo.conf → 命令行参数 → 环境变量(仅部分数据库参数)。本节先建立这个认知,再逐层展开。

3.1 查看全部参数与默认值

odoo --help 只输出子命令清单,服务端参数要通过 odoo server --help 查看:

odoo server --help | head -20

实测回显(节选,实测首尾):

Usage: odoo [options]

Odoo Server version 20.0-20261015

Options:
  --version             show program's version number and exit
  -h, --help            show this help message and exit

  Common options:
    -c PATH, --config=PATH
                        specify alternate config file
    --save              save configuration to ~/.odoorc (or to
                        ~/.openerp_serverrc if it exists)
    -i MODULE,..., --init=MODULE,...
                        install one or more modules (comma-separated list, use
                        "all" for all modules), requires -d
    -u MODULE,..., --update=MODULE,...

帮助文本不显示默认值。Odoo 20 的权威默认值在源码 odoo/tools/config.py 中,每个选项的 my_default 即真实默认值。检索方式(服务器上按 deb 包路径):

grep -n 'my_default' /usr/lib/python3/dist-packages/odoo/tools/config.py | head -20

实测回显(节选,实测首尾):

230:        parser.add_option(FileOnlyOption(dest='admin_passwd', my_default='admin'))
289:        group.add_option("--http-interface", dest="http_interface", my_default='127.0.0.1',
291:        group.add_option("-p", "--http-port", dest="http_port", my_default=8069,
293:        group.add_option("--gevent-port", dest="gevent_port", my_default=8072,
297:        group.add_option("--proxy-mode", dest="proxy_mode", action="store_true", my_default=False,
425:        group.add_option("--db_maxconn", dest="db_maxconn", type='int', my_default=64,
477:        group.add_option("--max-cron-threads", dest="max_cron_threads", my_default=2,
495:                         "--workers", dest="workers", my_default=0,

💡 技术拓展:服务端配置的四种载体对比——

载体生效范围优先级适用场景
源码默认值全局最低兜底行为
配置文件 odoo.conf全局持久中生产标准做法
命令行参数当次进程高一次性实验/调试
环境变量当次进程低于配置文件编排注入(仅 PG* 等少数项)

本文生产实践以配置文件为唯一事实源:配置文件可版本化管理、可审计,命令行参数仅用于临时验证,避免「改了配置不生效」或「命令行覆盖遗忘」两类排查陷阱。

3.2 配置文件格式与修改生效

odoo.conf 是 INI 格式,所有服务端参数都放在 [options] 节内,键名用下划线风格(与命令行 --db-host 对应为 db_host)。实测当前生效的配置:

cat /etc/odoo/odoo.conf

实测回显:

[options]
addons_path = /mnt/extra-addons
data_dir = /var/lib/odoo
http_interface = 0.0.0.0
db_host = db
db_port = 5432
db_user = odoo
db_password = odoo20pg
workers = 2
limit_time_cpu = 30
limit_time_real = 60
limit_memory_soft = 1073741824
limit_memory_hard = 1610612736
limit_request = 8192
max_cron_threads = 1
admin_passwd = q1w2e3r4

逐项说明:数据库连接四项(db_host/db_port/db_user/db_password)指向 PostgreSQL 实例;http_interface = 0.0.0.0 放开对外监听(Odoo 20 默认只听 127.0.0.1,这是本专栏部署篇实测确认过的 Odoo 20 关键变化);workers = 2 开启多进程模式(见 5.6 节);limit_* 系列限制单请求资源;admin_passwd 是数据库管理器主控口令(见 5.2 节)。

修改后重启服务生效,服务启动日志会回显加载的配置文件与解析出的关键值:

sudo systemctl restart odoo && sudo journalctl -u odoo -n 8 --no-pager

实测回显(日志关键行):

2026-10-27 15:25:12,448 1 INFO ? odoo: Using configuration file at /etc/odoo/odoo.conf
2026-10-27 15:25:12,448 1 INFO ? odoo: addons paths: _NamespacePath(['/usr/lib/python3/dist-packages/odoo/addons', '/var/lib/odoo/addons/20.0', '/mnt/extra-addons'])
2026-10-27 15:25:12,448 1 INFO ? odoo: database: odoo@db:5432
2026-10-27 15:25:12,671 1 INFO ? odoo.service.server: HTTP service running on 0.0.0.0:8069

这四行是配置生效的快速核对点:第一行确认加载的就是目标文件,第二行确认 addons_path 解析结果,第三行确认数据库连接(格式 用户@主机:端口),第四行确认监听地址端口。

3.3 命令行覆盖与 --save 回写

命令行参数优先于配置文件。例如配置文件已设 db_host = db,命令行临时改连另一实例:

odoo -c /etc/odoo/odoo.conf --db-host=192.168.1.50 --stop-after-init --no-http

--save 参数可把当前生效的全部参数(含命令行覆盖后的值)写回 ~/.odoorc,适合把调好的命令行组合固化成配置底稿再手工合并。

四、参数总览

Odoo 20 服务端参数按 server --help 的分组可归纳为九组,下表给出各组核心参数、真实默认值(源码 my_default 取证)与一句话作用。默认值列的布尔值以源码为准:

分组参数默认值作用
数据库连接db_host空(本机 socket)PostgreSQL 地址
db_port空(5432)PostgreSQL 端口
db_user / db_password空 / 空连接角色与口令
db_maxconn64单进程连接池上限
db_sslmodeprefer与 PostgreSQL 的 SSL 模式
db_templatetemplate0建库模板
数据库管理器admin_passwdadmin管理器主控口令(只能配置文件设置)
list_dbTrue是否允许列出数据库
dbfilter空(不过滤)数据库路由正则
HTTP 服务http_interface127.0.0.1监听网卡(Odoo 20 关键变化)
http_port8069HTTP 端口
gevent_port8072WebSocket/长轮询端口
proxy_modeFalse反向代理模式
x_sendfileFalseX-Sendfile 静态加速
多进程workers0(多线程模式)HTTP worker 进程数
max_cron_threads2cron 线程数
limit_memory_hard2684354560(2560MiB)worker 内存硬限(字节)
limit_memory_soft2147483648(2048MiB)worker 内存软限(字节)
limit_time_cpu60单请求 CPU 时间上限(秒)
limit_time_real120单请求真实时间上限(秒)
limit_time_real_cron-1(随 limit_time_real)cron 任务真实时间上限
limit_request65536worker 处理请求数后重生
模块加载addons_path内置路径模块搜索路径(逗号分隔)
server_wide_modulesbase,web常驻加载模块(--load)
data_dir系统 XDG 数据目录filestore/session 根目录
日志logfile空(stdout)日志文件路径
log_levelinfo日志级别
log_handler:INFO按模块定制级别
syslogFalse输出到 syslog
log_db / log_db_level空 / warning写入数据库的日志
邮件smtp_server / smtp_portlocalhost / 25SMTP 服务器
smtp_user / smtp_password空 / 空SMTP 认证
smtp_sslFalseSMTPS
email_from / from_filter空 / 空发件地址与过滤
翻译与导入overwrite_existing_translationsFalse覆盖既有翻译(--i18n-overwrite)
import_file_maxbytes10485760(10MiB)CSV 导入文件上限
import_partial空大导入断点文件路径
开发与杂项dev_mode空开发模式(--dev)
osv_memory_count_limit0(不限制)瞬态记录数上限
transient_age_limit1.0(小时)瞬态记录存活期
websocket_keep_alive_timeout3600WebSocket 保活(秒)
publisher_warranty_urlhttp://services.odoo.com/publisher-warranty/版本校验上报地址

表格解读:日常生产调参集中在「数据库连接、数据库管理器、HTTP 服务、多进程、日志」五组;邮件组在配置出站邮件时必碰;翻译与杂项组多数场景保持默认。下文按组逐项展开实测行为。

五、核心参数分类详解

5.1 数据库连接参数

Odoo 绑定 PostgreSQL(不支持 MySQL/MariaDB),连接参数六件套是所有部署的第一组必配项:

参数实测取值说明
db_hostdb空 = 本机 UNIX socket(deb 同机部署默认);远程/容器网络填主机名或 IP
db_port5432空 = 5432
db_userodoodeb 包安装时安装脚本自动创建的数据库角色同名
db_passwordodoo20pg该角色的口令
db_sslmode未设置(默认 prefer)可选 disable/allow/prefer/require/verify-ca/verify-full(Odoo 11.0 起支持)
db_maxconn未设置(默认 64)单进程 PostgreSQL 连接池上限

两个实测佐证:其一,连接池行为可见于 DEBUG 日志(log_handler = odoo.sql_db:DEBUG 时),连接池的 max=64 即 db_maxconn 默认值,连接串里的 sslmode=prefer 即 db_sslmode 默认值:

2026-10-27 15:24:08,781 331 DEBUG odoo20restore odoo.sql_db.connection: ConnectionPool(read/write;used=2/count=2/max=64) Give back connection to 'user=odoo password=xxx dbname=odoo20restore host=db port=5432 application_name=odoo-331 sslmode=prefer'

其二,application_name=odoo-{pid} 是 db_app_name 参数的默认格式(odoo-{pid}),PostgreSQL 侧 pg_stat_activity 可据此区分连接来自哪个 Odoo 进程——多进程模式下排查连接归属时非常实用。

容量关系:db_maxconn 是单进程上限,总连接数 ≈ db_maxconn × (workers + cron 线程 + 1 个 gevent)。PostgreSQL 默认 max_connections = 100,官方部署文档的同机示例将 PostgreSQL max_connections 调到 80 并以此约束 worker 数量——调大 workers 时务必同步核算连接总数,避免 PostgreSQL 拒连。

5.2 数据库管理器与 admin_passwd

/web/database/manager 提供建库、删库、备份、恢复、复制等全部破坏性操作,其防护就是 admin_passwd(主控口令)。官方文档明确:该参数只能通过配置文件设置("This setting can only be set using configuration files"),命令行参数无效,这是它与其他参数的重要差异。

默认值 admin 意味着「装完即裸奔」:任何能访问 8069 端口的人都能用默认口令建库删库。实测把主控口令改为 q1w2e3r4 后,旧口令的建库请求被拒绝:

curl -s -X POST http://127.0.0.1:8069/web/database/create \
  -d 'master_pwd=admin&name=badtest&login=admin&password=q1w2e3r4&phone=&lang=zh_CN&country_code=CN&demo=false' \
  -o /dev/null -w 'HTTP %{http_code}\n'

实测回显:

HTTP 403

403 页面为建库表单重新渲染并提示 Master Password 错误;改用正确口令 master_pwd=q1w2e3r4 则返回 303 建库成功(本专栏部署篇已实测)。生产环境的第一动作就是设置强 admin_passwd。

与 admin_passwd 配套的是 list_db(默认 True)。设为 False 后(等价命令行 --no-database-list)数据库列表接口被整体禁用,实测 JSON-RPC 调用返回明确的拒绝消息:

curl -s -X POST http://127.0.0.1:8069/web/database/list \
  -H 'Content-Type: application/json' \
  -d '{"jsonrpc":"2.0","method":"call","params":{"master_pwd":"q1w2e3r4"}}'

实测回显:

{"jsonrpc": "2.0", "id": null, "error": {"code": 0, "message": "Odoo Server Error", "data": {"name": "odoo.exceptions.AccessDenied", "message": "Database management functions blocked, admin disabled database listing.", "arguments": ["Database management functions blocked, admin disabled database listing."], "timestamp": 1793114793, "context": {}, "debug": "odoo.exceptions.AccessDenied: Database management functions blocked, admin disabled database listing.\n"}}

同时 /web/database/selector 页面返回 200 但选择器已被禁用(页面表单标记 disabled)。多库生产服务器官方推荐组合就是:dbfilter 锁库 + list_db = False 禁列库 + 屏蔽数据库管理界面(官方原话:"it is strongly recommended to block access to the database manager screens, and to use the --no-database-list startup parameter")。

顺带一个 Odoo 20 的接口形态实测发现:用表单方式(Content-Type: application/x-www-form-urlencoded)调 JSON-RPC 端点会命中新的类型路由检查:

curl -s -X POST http://127.0.0.1:8069/web/database/list -d 'master_pwd=q1w2e3r4' -w '\nHTTP %{http_code}\n' | head -5

实测回显:

<!doctype html>
<html lang=en>
<title>415 Unsupported Media Type</title>
<h1>Unsupported Media Type</h1>
<p>Request inferred type is compatible with ['http'] but '/web/database/list' is type='jsonrpc'.<br><br>Please verify the Content-Type request header and try again.</p>

写脚本对接数据库管理器时必须带 Content-Type: application/json,否则收到 415 而非业务结果。

5.3 多数据库与 dbfilter

一个 Odoo 服务可同时管理多个数据库(每个库是独立租户:独立应用清单、用户与业务数据),dbfilter 决定「当前请求路由到哪个库」。它支持两个动态占位符:%h = 请求域名(去端口、去 www.),%d = 域名第一段子域。

官方配置示例(照录自官方部署文档):

# 只显示 mycompany 开头的库
dbfilter = ^mycompany.*$

# 按第一段子域路由(如 www2.mycompany.com → www2 库)
dbfilter = ^%d$

官方还强调:使用 website 模块的多库生产服务器 必须 设置 dbfilter,否则未登录访客(门户/网站页)无法确定库,相关功能将不能正常工作。

实测语义验证(这是本篇最重要的实测结论):服务器上已有 4 个库(odoo20main/odoo20demo/odoo20restore/odoo20clirestore),设置 dbfilter = ^odoo20demo 并重启后——

  1. 匹配库登录正常(先 GET /web/login 建立会话并获取 CSRF token,再带 token POST):

    curl -s -c /tmp/odoo-cj.txt http://127.0.0.1:8069/web/login > /dev/null
    CSRF=$(grep -oE 'csrf_token: "[^"]+"' /tmp/odoo-cj.txt | head -1)
    curl -s -b /tmp/odoo-cj.txt -o /dev/null -D - -X POST http://127.0.0.1:8069/web/login \
      --data-urlencode "csrf_token=$(echo $CSRF | sed 's/.*"\(.*\)"/\1/')" \
      -d 'db=odoo20demo&login=admin&password=q1w2e3r4' | grep -iE '^HTTP|^location'
    

    实测回显:

    HTTP/1.1 303 SEE OTHER
    location: /odoo
    
  2. dbfilter 之外显式指定的库也会被路由进匹配库。用 db=odoo20main(不匹配 ^odoo20demo)重复上述登录,响应同为 303 → /odoo,而服务日志显示两次请求实际都落在 odoo20demo:

    2026-10-27 15:28:48,484 16 INFO odoo20demo odoo.http.server: 10.89.64.1 I3KgXUIM - [27/Oct/2026 15:28:48] "POST /web/login HTTP/1.1" 303 197 23 0.043 0.269 rw
    2026-10-27 15:29:06,328 16 INFO odoo20demo odoo.http.server: 10.89.64.1 oPb6qkoX - [27/Oct/2026 15:29:06] "POST /web/login HTTP/1.1" 303 197 14 0.006 0.253 rw
    

    日志第三列的库名均为 odoo20demo——dbfilter 的本质是请求级数据库路由器:源码 odoo/http/router.py 的 db_filter() 先按正则(含 %h/%d 展开)过滤可用库集合,路由取过滤结果的第一个;会话中已存的库若不匹配过滤也会被登出重选。它不是安全边界——库级访问控制要靠 PostgreSQL 角色权限与网络层。

  3. 锁库后登录 POST 强制要求 CSRF token。不带 token 直接 POST(部署篇部署时可行的写法)此时报 400:

    400 Bad Request
    Session expired (invalid CSRF token)
    

    原因:filter 锁定唯一库后,GET 登录页即绑定库并颁发 CSRF token,POST 必须携带。多库无 filter 时首跳 POST 可免 token(部署篇实测 303),锁库后该捷径失效——脚本对接多租户环境要先 GET 建 session。

💡 技术拓展:多租户库路由的常见模式对比——

模式dbfilter 写法适用
单库直连不设置(默认)服务器只有一个业务库
前缀匹配^customer.*$一台服务器多客户库、按名分流
子域路由^%d$SaaS 化:a.example.com → a 库
整域路由^%h$库名与完整域名一致的场景

本文实测采用前缀匹配 ^odoo20demo,因为它能最直观地暴露「过滤集合 + 取首个」的路由语义;生产 SaaS 场景选子域路由 %d,与域名规划天然对齐。

5.4 addons_path 与 data_dir

addons_path 是模块搜索路径(逗号分隔多个目录),data_dir 是 filestore(附件/图片二进制)、session、自动下载模块的根目录。二者是自定义模块开发与数据备份定位的基础。

实测要点:

  1. 内置路径自动存在。即使 addons_path 只写一个自定义目录,服务日志显示最终搜索路径是内置路径与自定义路径的合集:

    2026-10-27 15:25:12,448 1 INFO ? odoo: addons paths: _NamespacePath(['/usr/lib/python3/dist-packages/odoo/addons', '/var/lib/odoo/addons/20.0', '/mnt/extra-addons'])
    

    三段含义:核心框架模块(deb 包随 Python 库安装)、data_dir/addons/<版本>(网页端「应用」里下载的第三方模块落点)、自定义模块目录。

  2. 无效目录告警不阻断。目录存在但没有有效模块(缺 __manifest__.py)时,启动日志出现两行告警:

    2026-10-27 15:25:12,936 17 WARNING ? odoo.tools.config: option addons_path, invalid addons directory '/mnt/extra-addons', skipped
    

    服务照常启动运行;放入有效模块后告警消失。排查「自定义模块不出现」问题时先看这两行。

  3. filestore 与数据库必须成对备份。data_dir 下的 filestore/<库名>/ 存放该库全部附件二进制,只备份数据库 dump 不带 filestore 会丢附件(数据库管理器/CLI 的 zip 备份格式之所以「filestore-aware」正为此,见命令篇实测)。

5.5 HTTP 服务参数

参数默认值实测说明
http_interface127.0.0.1Odoo 20 关键变化:旧版本默认 0.0.0.0,20 改为仅本机回环;对外提供服务必须显式 0.0.0.0(源码 config.py 289 行 my_default='127.0.0.1',且配置留空时代码兜底强制回环,本专栏部署篇实测定位)
http_port8069改端口时注意 --http-port 同名覆盖
gevent_port8072多进程模式下 LiveChat/WebSocket 专用 worker 的监听端口,启动日志实测:GeventServer: Evented/WebSocket service running on 0.0.0.0:8072
proxy_modeFalse只应在反向代理之后启用:开启后 Odoo 信任 X-Forwarded-Host/X-Forwarded-Proto 等头,还原真实客户端 IP 与协议。无反代直开会导致 IP/协议失真
x_sendfileFalse反代支持 X-Sendfile/X-Accel-Redirect 时静态文件交给代理直传,减轻应用进程 IO
http_enableTrue--no-http 关闭 HTTP(CLI 管理命令场景,命令篇实测)

多进程模式下反代的分工(官方部署文档):HTTP 请求全部走 8069 常规 worker,路径以 /websocket/ 开头的请求需由反代单独转发到 8072 的 gevent worker,同时开启 proxy_mode。这套组合是 Odoo 生产 HTTPS 架构的标准形态。

5.6 多进程 workers 与 limit_* 系列

Odoo 服务端有两种运行模型,workers 参数是开关:

模型触发条件特征适用
多线程workers = 0(默认)单进程,每请求一线程,受 Python GIL 限制开发/演示/极小规模
多进程workers > 0prefork 模型:HTTP worker 进程池 + 独立 gevent 进程(8072)+ cron 进程,配有进程收割器(process reaper)监控资源、杀掉并重启失控 worker生产(Windows 不可用)

实测 workers = 2、max_cron_threads = 1 的多进程启动形态(日志 + 进程两个视角):

2026-10-27 15:25:12,671 1 INFO ? odoo.service.server: HTTP service running on 0.0.0.0:8069
2026-10-27 15:25:12,673 14 INFO ? odoo.service.server.WorkerHTTP: Alive
2026-10-27 15:25:12,675 16 INFO ? odoo.service.server.WorkerHTTP: Alive
2026-10-27 15:25:12,677 19 INFO ? odoo.service.server.WorkerCron: Alive
2026-10-27 15:25:13,183 17 INFO ? odoo.service.server.GeventServer: Evented/WebSocket service running on 0.0.0.0:8072
UID    PID  COMMAND
odoo     1  /usr/bin/python3 /usr/bin/odoo
odoo    14  /usr/bin/python3 /usr/bin/odoo
odoo    16  /usr/bin/python3 /usr/bin/odoo
odoo    17  /usr/bin/python3 /usr/bin/odoo gevent

进程清单与日志一一对应:PID 1 主进程(prefork 管理者),14/16 两个 HTTP worker(workers = 2),17 是 gevent 专用进程(gevent_workers 默认 1),19 为 cron 进程(内部跑 max_cron_threads = 1 个线程)。workers = 0 时这组 WorkerHTTP/WorkerCron 日志不会出现,全部请求在同一进程内——这是判断当前运行模型最直接的证据。

worker 数量计算(官方部署文档经验公式,照录):

  • 经验法则:workers = (CPU 核数 × 2) + 1;
  • cron worker 也要占 CPU(max_cron_threads 计入资源预算);
  • 1 worker ≈ 6 个并发用户。

内存计算(官方文档模型):按 20% 重请求/80% 轻请求假设,重 worker 峰值约 1GB、轻 worker 约 150MB:

Needed RAM = #worker × (light_worker_ratio × light_worker_ram_estimation
                     + heavy_worker_ratio × heavy_worker_ram_estimation)

官方给的标准算例(4 CPU / 8 线程服务器,60 并发用户):60 / 6 = 10 个 worker(并发需求)、(4 × 2) + 1 = 9 个 worker(CPU 上限),取 8 worker + 1 cron;内存 9 × (0.8 × 150 + 0.2 × 1024) ≈ 3GB。

limit_* 系列逐项(默认值取自源码,行为取自源码 help 文本与实测日志):

参数默认值行为
limit_memory_soft2147483648(2048MiB)worker 虚拟内存软限:超限后当前请求完成后 worker 自杀重生
limit_memory_hard2684354560(2560MiB)worker 虚拟内存硬限:超限后任何内存分配立即失败
limit_time_cpu60单请求 CPU 时间上限(秒),超时 worker 被收割器终止
limit_time_real120单请求墙钟时间上限(秒)
limit_time_real_cron-1(随 limit_time_real)cron 任务墙钟上限;0 = 不限时
limit_request65536worker 累计处理请求数达到后主动重生(防内存碎片累积)

实测佐证:把 limit_time_real = 60 后,一个长跑 cron 任务被收割器按 60 秒上限终止,日志留下明确的超时记录(真实回显):

2026-10-27 15:26:16,306 1 ERROR ? odoo.service.server.PreforkServer: WorkerCron (19) timeout after 60s

PreforkServer: WorkerXXX timeout 就是 limit 系列生效的标志行;HTTP worker 的对应形态是 WorkerHTTP (NN) timeout after NNs。生产上若日志频繁出现该行,说明 limit 配置低于业务真实耗时,应调参或优化慢请求,而不是一味加大。

5.7 日志体系

参数默认值说明
logfile空(输出到 stdout/stderr)指定文件后日志落盘,控制台输出停止(实测:配置后 journalctl 无新增行、文件持续增长,二者互斥)
log_levelinfo预设级别:debug/info/warn/error/critical,其中 debug_rpc/debug_sql 等是组合预设
log_handler:INFO精细化控制,格式 模块前缀:级别,可多行(如 odoo.sql_db:DEBUG 只把 SQL 连接池日志调到 DEBUG)
log_db空指定库名后日志写入该库 ir_logging 表
syslogFalse输出到系统 syslog

实测组合 logfile = /var/lib/odoo/odoo.log + log_handler = odoo.sql_db:DEBUG 的落盘内容(真实回显):

2026-10-27 15:24:08,781 331 DEBUG odoo20restore odoo.sql_db.connection: ConnectionPool(read/write;used=2/count=2/max=64) Put connection to 'user=odoo password=xxx dbname=odoo20restore host=db port=5432 application_name=odoo-331 sslmode=prefer' in pool
2026-10-27 15:24:08,781 331 INFO ? odoo.sql_db: ConnectionPool(read/write;used=1/count=1/max=64): Closed 1 connections to 'user=odoo password=xxx dbname=odoo20restore host=db port=5432 application_name=odoo-331 sslmode=prefer'

逐段解读:时间戳 进程号 级别 库名 模块:子模块 消息——第 2 列进程号在多进程模式下可定位具体 worker(331 是新 fork 的 HTTP worker),库名列为空表示与库无关的系统日志;password=xxx 表明连接口令在日志中自动脱敏。排障时最实用的两级组合就是:全局 info + odoo.sql_db:DEBUG 看连接池,或临时 log_level = debug_rpc_answer 看请求级 RPC 报文。

注意运行模型的日志入口差异:多线程模式日志从主进程输出;多进程模式各 worker 独立写同一文件(logfile)或各自输出 stdout,journalctl 聚合时按进程号区分。

5.8 SMTP 邮件参数

这组参数定义服务端级默认发件通道(数据库内还可按公司覆盖):

参数默认值说明
smtp_serverlocalhostSMTP 主机;配内网中继或公网 SMTP 服务地址
smtp_port25587(STARTTLS)/465(SMTPS)按服务商调整
smtp_user / smtp_password空服务端认证凭据
smtp_sslFalseTrue = 全程 TLS(465)
email_from空全局发件地址兜底
from_filter空限定哪些发件地址走此通道(逗号匹配规则)

验证方式:配置后重启服务,在应用内「设置 → 技术 → 邮件」触发测试邮件;若 SMTP 凭据错误,日志会出现 odoo.addons.mail 前缀的 SMTP 连接错误(smtplib.SMTPAuthenticationError 等),按报错对号入座。演示环境未接外网 SMTP,本篇以参数语义与默认值为准。

5.9 多进程之外的资源参数

参数默认值说明
max_cron_threads2多线程模式下的 cron 线程数;多进程模式归入 cron 进程。定时任务重的实例适当调大,纯接口型实例可设 0 停用定时调度
osv_memory_count_limit0(不限)瞬态模型(向导类)单表记录上限
transient_age_limit1.0瞬态记录存活小时数,过期由 vacuum 清理
websocket_keep_alive_timeout3600WebSocket 空闲保活秒数
publisher_warranty_urlhttp://services.odoo.com/publisher-warranty/版本/订阅校验上报地址。企业版数据库每周向 services.odoo.com 校验订阅;内网隔离环境官方给出两个选项:放行 80 端口出口,或改走 443 并配置 publisher_warranty_url = https://services.odoo.com/publisher-warranty/。社区版无订阅校验,该地址仅承载匿名版本遥测

5.10 环境变量对照

Odoo 服务端原生识别 PostgreSQL 客户端标准环境变量(源码 config.py 各选项的 env_name 实证),在配置文件缺省时生效:

环境变量对应配置项说明
PGHOSTdb_hostPostgreSQL 地址
PGPORTdb_port端口
PGUSERdb_user角色
PGPASSWORDdb_password口令
PGDATABASEdb_name默认连接库(-d)
PGDATABASE_TEMPLATEdb_template建库模板
PGSSLMODEdb_sslmodeSSL 模式
PGAPPNAMEdb_app_nameapplication_name 标识
ODOO_DEVdev_mode开发模式特性开关
ODOO_RC--config配置文件路径(deb 安装脚本已写入 systemd 单元)

优先级实测结论(命令篇 §命令形态将继续验证):配置文件已有值时,环境变量不再生效;两者都缺时回落源码默认。另有一组官方容器镜像专属变量(HOST/PORT/USER/PASSWORD/PASSWORD_FILE,由镜像 entrypoint 转换为命令行参数),仅存在于容器部署形态,其行为与优先级在 《Odoo 20 Docker Compose 部署实战》第六章已实测——裸机/包管理器部署不涉及。

六、生产配置模板

以下模板按官方部署文档的算例与本文实测参数组装,适用场景:8GB 内存、4 核 CPU、NVMe SSD,60 并发用户以内,CRM/销售/库存业务,Odoo 20 与 PostgreSQL 17 同机:

[options]
; ---- 数据库连接(同机 UNIX socket 可省 db_host/db_port)----
db_host = 127.0.0.1
db_port = 5432
db_user = odoo
db_password = odoo20pg
db_maxconn = 64
db_sslmode = prefer

; ---- 多进程与资源限制(官方 4C/8G 算例)----
workers = 8
max_cron_threads = 1
limit_memory_soft = 629145600
limit_memory_hard = 1677721600
limit_time_cpu = 600
limit_time_real = 1200
limit_request = 8192

; ---- HTTP 服务 ----
http_interface = 0.0.0.0
http_port = 8069
gevent_port = 8072
proxy_mode = True

; ---- 数据库管理器安全 ----
admin_passwd = q1w2e3r4
list_db = False
dbfilter = ^%d$

; ---- 模块与数据 ----
addons_path = /opt/odoo/extra-addons
data_dir = /var/lib/odoo

; ---- 日志 ----
logfile = /var/log/odoo/odoo.log
log_level = info
log_handler = odoo.sql_db:WARNING

逐项解读与调整指引:

  • workers = 8:(4 × 2) + 1 = 9 上限下留 1 核余量给 cron 与系统;并发用户增长按「每 worker ≈ 6 并发」扩容并同步核算 PostgreSQL max_connections(≈ db_maxconn × (workers + 2))。
  • limit_memory_soft/hard:官方算例的 600MB/1.6GB 组合,较源码默认(2GB/2.5GB)更适合小内存多 worker;内存充裕(16GB+)可放宽 soft 到 1GB 减少重生频率。
  • limit_time_cpu/real = 600/1200:官方示例值,比默认(60/120)宽松,适配报表导出等长请求;接口型业务可回调收紧。
  • proxy_mode = True + dbfilter = ^%d$ + list_db = False:三者是多租户 HTTPS 生产的配套组合——反代终结 TLS 并转发 /websocket/ 到 8072,按子域路由库,禁列库。
  • admin_passwd:上生产前必须改为强随机值(本文 q1w2e3r4 仅为演示环境的固定测试口令)。
  • 日志:log_handler = odoo.sql_db:WARNING 把连接池 INFO 降噪;排障期临时改为 odoo.sql_db:DEBUG,完毕改回。

轻量对照模板(2 核 4GB、20 并发以内):workers = 4、limit_memory_soft = 1073741824(1GB)、limit_memory_hard = 1610612736(1.5GB)、其余同上——本文实测环境即采用该档参数(workers = 2 为实测机保守值),多进程行为验证见 5.6 节。

七、常见问题

Q1:改了 odoo.conf 重启后行为没变化? 三步排查:确认服务加载的就是目标文件(启动日志 Using configuration file at ... 的路径);确认参数名是下划线风格且在 [options] 节内(写成 db-host 或放错节会被静默忽略);确认没有命令行参数(systemd 单元 ExecStart)覆盖配置文件。

Q2:admin_passwd 改完用 Web 界面验证报 403? admin_passwd 只能配置文件设置且重启生效;确认提交的 master_pwd 与配置值完全一致。403 页面是「口令错误后表单重渲染」的正常表现,不是服务故障。

Q3:开启 workers 后服务起不来或 Windows 上不可用? 多进程模型仅支持 Linux(官方明确 not available on Windows);Linux 下起不来看日志是否出现 PosixOnlyOption 相关参数冲突(workers 组参数在 Windows 被禁用)与端口占用(8069/8072)。

Q4:日志频繁出现 WorkerHTTP (NN) timeout after NNs? limit 时间上限低于真实业务耗时。先定位慢请求(log_level = debug_rpc 或数据库侧 pg_stat_activity),常见根因是未加索引的大表操作或报表聚合;确属业务常态再调大 limit_time_cpu/limit_time_real。

Q5:worker 频繁重生(Exiting cleanly. request_count: ... 高频出现)? 两种可能:limit_request 到数(默认 65536,属设计内正常重生);limit_memory_soft 频繁触发(调大 soft 或排查内存泄漏模块)。若为 limit_memory_hard 触发则伴随内存分配失败报错,需立即处理。

Q6:多库服务器未登录页面(网站/门户)报错? dbfilter 未设置时未登录请求无法定位库。按域名规划设置 %h/%d 过滤;确认后按 5.3 节实测语义验证路由结果(看日志第三列库名)。

Q7:list_db = False 后数据库管理器还能用吗? 管理器各操作端点与列表端点是分开的:list_db = False 禁掉库列表与选择器,备份/恢复/复制等操作仍可用正确 master_pwd 走对应端点(JSON-RPC 形态,注意 415 一节所述的 Content-Type 要求)。要彻底禁用管理器,官方建议在网络层屏蔽 /web/database/ 路径。

Q8:dbfilter 设置后脚本登录报 Session expired (invalid CSRF token)? 锁库后登录必须先 GET /web/login 建 session 拿 CSRF token 再 POST(5.3 节实测流程),或在脚本中复用带 cookie 的会话对象。

八、参考链接