Kimai 2.71 配置参数详解
本文逐层拆解 Kimai 2.71.0 的配置体系:环境变量与 `.env` 文件的优先级实测、`local.yaml` 对 `kimai.yaml` 的覆盖机制验证、时间跟踪/费率规则/权限/用户认证/邮件/发票/日历/主题的全部配置段默认值,以及配置改动生效路径(`kimai:reload`)。全部参数值来自真实部署环境的 `bin/console debug:config` 与 `debug:dotenv` 回显,费率联动行为经 API 实测验证。产品认知见 [《Kimai 2.71 介绍与选型指南》](https://www.quzuo.net/document/sK41JUhCNu0x),部署过程见 [《Kimai 2.71 Docker Compose 部署实战》](https://www.quzuo.net/document/3CNq42eP7Z4D)。
Kimai 2.71 配置参数详解
本文逐层拆解 Kimai 2.71.0 的配置体系:环境变量与
.env文件的优先级实测、local.yaml对kimai.yaml的覆盖机制验证、时间跟踪/费率规则/权限/用户认证/邮件/发票/日历/主题的全部配置段默认值,以及配置改动生效路径(kimai:reload)。全部参数值来自真实部署环境的bin/console debug:config与debug:dotenv回显,费率联动行为经 API 实测验证。产品认知见 《Kimai 2.71 介绍与选型指南》,部署过程见 《Kimai 2.71 Docker Compose 部署实战》。
一、配置体系总览
1.1 三层配置来源与优先级
Kimai 基于 Symfony 6.4 的标准配置体系,实际生效值由三层来源叠加决定,优先级从高到低:
| 层级 | 位置 | 承载内容 | 生效方式 |
|---|---|---|---|
| ① 运行环境变量 | 进程环境(Compose environment:、shell export) | 数据库连接、密钥、可信主机、邮件等部署相关项 | 立即生效(重启服务后) |
② .env 文件 | 安装目录根 /opt/kimai/.env | 同上的文件形态兜底值 | 被层级 ① 覆盖 |
| ③ YAML 配置 | config/packages/kimai.yaml(默认)与 config/packages/local.yaml(用户覆盖) | 应用行为(时间跟踪规则、权限、费率、日历、主题等) | 需 kimai:reload 重建缓存 |
用 bin/console debug:dotenv 可以直观看到三个层级谁说了算:
Dotenv Variables & Files
========================
[WARNING] The file .env.dist gets skipped due to the existence of .env.
Scanned Files (in descending priority)
--------------------------------------
* ⨯ .env.local.php
* ⨯ .env.prod.local
* ⨯ .env.prod
* ⨯ .env.local
* ✓ .env
Variables
---------
------------------- -------------------------------------------------------------------------------------- -----------------------------------
Variable Value .env
------------------- -------------------------------------------------------------------------------------- -----------------------------------
APP_ENV prod prod
APP_SECRET 5f0a1c9e2b7d4a8f3e6c1d9b0a7f4e2c
CORS_ALLOW_ORIGIN ^https?://localhost(:[0-9]+)?$ ^https?://localhost(:[0-9]+)?$...
DATABASE_URL mysql://kimaiuser:q1w2e3r4@kimai-2.71.0-db/kimai?charset=utf8mb4&serverVersion=8.3.0 mysql://user:password@127.0.0....
DEFAULT_URI https://127.0.0.1:8000 https://127.0.0.1:8000
MAILER_FROM kimai@example.com kimai@example.com
MAILER_URL null://localhost null://null
------------------- -------------------------------------------------------------------------------------- -----------------------------------
这张表的读法:Value 列是最终生效值,.env 列是文件里的值。APP_SECRET 与 DATABASE_URL 两行的 .env 列为空——实际值来自运行环境变量(容器编排注入),.env 文件中的同名占位(mysql://user:password@127.0.0.1)被环境变量覆盖。这正是 Compose 部署的标准形态:敏感凭据走编排环境变量,.env 只留非敏感默认值。
1.2 查看生效配置的两把尺子
bin/console debug:config kimai:输出kimai:配置树合并后的最终值(本地.yaml 已叠加);bin/console debug:dotenv:输出环境变量三层的叠加结果。
修改配置后的生效路径:YAML 层改动必须执行 bin/console kimai:reload(重建 Symfony 生产缓存),环境变量层改动必须重启服务进程。本文所有覆盖实测均按此流程验证。
二、环境变量详解
2.1 全量变量与默认值
以下为 2.71.0 安装目录 .env 文件原文(本文实测环境逐行拷贝),注释保留官方原意:
# Configure your database connection and the correct server version - find it with: SELECT VERSION();
DATABASE_URL=mysql://user:password@127.0.0.1:3306/database?charset=utf8mb4&serverVersion=10.5.8-MariaDB
# You MUST change this to a long and random character sequence, e.g.: php -r 'echo bin2hex(random_bytes(32));'
APP_SECRET=
# The base URL of your Kimai installation
DEFAULT_URI=https://127.0.0.1:8000
# SECURITY WARNING: you SHOULD configure the allowed domain name(s) for your installation
# TRUSTED_HOSTS='^www\.kimai\.cloud$'
# Outgoing emails use this email-address as sender
MAILER_FROM=kimai@example.com
# Email connection - see https://www.kimai.org/documentation/emails.html
MAILER_URL=null://null
# Running behind reverse proxies? Try these:
# TRUSTED_PROXIES=127.0.0.1,127.0.0.2
# regexp to define allowed frontend origins for API requests
CORS_ALLOW_ORIGIN=^https?://localhost(:[0-9]+)?$
# only change to "dev" if you are developing with/for Kimai
APP_ENV=prod
逐项说明:
| 变量 | 必填 | 作用与实测要点 |
|---|---|---|
DATABASE_URL | ✅ | Doctrine 连接串:mysql://用户:密码@主机:端口/库?charset=utf8mb4&serverVersion=<真实版本>。serverVersion 必须与真实版本一致(本文实测 8.3.0)——Doctrine 按它选择 SQL 方言与迁移平台策略;charset=utf8mb4 官方默认串自带,不可省(中文/emoji 存储依赖它) |
APP_SECRET | 官方要求 | Symfony 会话/CSRF/记住我令牌的加密熵源。官方建议 php -r 'echo bin2hex(random_bytes(32));' 生成 64 位十六进制。实测发现:Compose 部署漏配时官方镜像会自动生成随机密钥并持久化到 var/data 卷(部署篇排障案例一),但生产环境应显式配置以保证可审计可迁移 |
DEFAULT_URI | 建议 | 系统发邮件/生成绝对链接用的基地址。默认 https://127.0.0.1:8000,生产必须改成对外域名(否则密码重置邮件里的链接不可用) |
TRUSTED_HOSTS | 建议 | Symfony 可信主机正则,如 ^kimai\.example\.com$。不配置时 Symfony 接受任意 Host 头;配置后白名单外的主机名直接拒绝。多个主机用 ` |
TRUSTED_PROXIES | 可选 | 反向代理场景下,声明哪些来源 IP 的 X-Forwarded-* 头可信;默认注释(不信任任何代理头) |
MAILER_FROM | 可选 | 外发邮件的发件人地址,默认 kimai@example.com |
MAILER_URL | 可选 | 邮件传输器 DSN,默认 null://null(空传输器:邮件不真正发出,实测见 8.2 节) |
CORS_ALLOW_ORIGIN | 可选 | API 跨域白名单正则,默认只放行 localhost;前端域名与 API 不同源时必须改 |
APP_ENV | 固定 | prod(默认)/dev/test。官方注释明确:除非为 Kimai 做开发,否则不要改 dev |
2.2 容器部署的变量注入点
官方镜像读取的变量与 .env 同名,但来源是容器环境变量(优先级高于文件)。部署篇编排文件里注入的正是这几个:
environment:
- APP_SECRET=5f0a1c9e2b7d4a8f3e6c1d9b0a7f4e2c
- 'TRUSTED_HOSTS=^(localhost|127\.0\.0\.1|kimai-2\.71\.0-web)$'
- ADMINMAIL=admin@example.com
- ADMINPASS=q1w2e3r4
- "DATABASE_URL=mysql://kimaiuser:q1w2e3r4@kimai-2.71.0-db/kimai?charset=utf8mb4&serverVersion=8.3.0"
其中 ADMINMAIL/ADMINPASS 是官方镜像特有的首启建号变量(不属于 Symfony 层,只被镜像入口脚本消费);其余变量进入 1.1 节的层级 ①。TRUSTED_PROXIES 在镜像中默认注入 127.0.0.1(实测 docker inspect 可见),反代场景按需调整。
三、YAML 配置与 local.yaml 覆盖机制
3.1 文件布局
/opt/kimai/
├── .env # 环境变量兜底值(层级 ②)
└── config/packages/
├── kimai.yaml # Kimai 主配置(官方默认,头部声明 DO NOT EDIT)
├── local.yaml # 用户覆盖文件(默认不存在,需自行创建)
├── security.yaml / framework.yaml # Symfony 框架级配置
├── doctrine.yaml / monolog.yaml ... # Doctrine、日志等
└── tabler.yaml # 前端 Tabler 主题配置
kimai.yaml 文件头部官方原话:
# DO NOT EDIT THIS FILE, INSTEAD CREATE THE FILE "local.yaml" AND ADD YOUR SETTINGS IN THERE.
# See https://www.kimai.org/documentation/local-yaml.html
#
# Be aware that this file is YAML format and the indentation is important.
# Each config level needs to be indented with 4 additional spaces.
即:kimai.yaml 是默认值清单(每次升级会被镜像覆盖),用户改动一律写进 local.yaml,键路径与 kimai.yaml 一致即可按「后加载者胜」逐键覆盖(YAML 深合并,未写的键保持默认)。
3.2 覆盖实测
在 config/packages/local.yaml 写入两项覆盖(markdown_content 开启 Markdown 渲染、duration_increment 改 30 分钟步进):
kimai:
timesheet:
markdown_content: true
duration_increment: 30
执行 bin/console debug:config kimai 核验合并结果:
timesheet:
markdown_content: true # 覆盖生效(默认 false)
duration_increment: 30 # 覆盖生效(默认 15)
default_begin: now # 未覆盖 → 保持默认
mode: default # 未覆盖 → 保持默认
time_increment: 15 # 未覆盖 → 保持默认
改动对运行中服务生效还需重建缓存:
bin/console kimai:reload
实测回显:
// Warming up the cache for the prod environment with debug false
[OK] Cache for the "prod" environment (debug=false) was successfully warmed.
[OK] Kimai config was reloaded
💡 技术拓展:为什么 YAML 改动不能「热生效」 Symfony 生产模式把 YAML 配置编译进
var/cache/prod的 PHP 类里,请求路径上不存在「每次读 YAML」的动作——这是它性能的来源。因此配置文件的任何改动都需要一次「重新编译」:kimai:reload内部就是清空缓存再预热(其输出先 clear 后 warm)。部署篇重启实验也印证了这一点:容器每次启动都会重跑一次缓存重建。
四、时间跟踪参数(kimai.timesheet)
以下为 debug:config kimai 输出的 timesheet 段完整默认值(2.71.0 实测):
kimai:
timesheet:
default_begin: now # 新建时间记录的预填开始时间
mode: default # 记录模式
markdown_content: false # 描述字段是否渲染 Markdown
duration_increment: 15 # 时长下拉步进(分钟);null=跟随取整规则,0=禁用下拉
time_increment: 15 # 开始/结束时间分钟选择步进
rounding: # 取整规则(按下标命名,可自定义多条)
default:
days: [monday, tuesday, wednesday, thursday, friday, saturday, sunday]
begin: 1 # 开始时间向下取整到 1 分钟
end: 1 # 结束时间向上取整到 1 分钟
duration: 0 # 时长取整增量(0=不取整)
mode: default
rates: { } # 分时段费率规则(如周末上浮),见 5.2
active_entries:
hard_limit: 1 # 每用户同时运行中的记录上限
rules:
allow_future_times: true # 允许记录未来时间
allow_zero_duration: true # 允许零时长记录
allow_overbooking_budget: true # 允许超预算继续记账
allow_overlapping_records: true # 允许时间重叠的多条记录
lockdown_period_start: null # 锁定期开始(配合项目锁定日期)
lockdown_period_end: null
lockdown_period_timezone: null
lockdown_grace_period: null # 锁定期宽限(天数)
break_warning_duration: 0 # 休息时长警告阈值(分钟)
long_running_duration: 0 # 长时间未停止警告阈值(分钟)
require_activity: true # 记录必须选择活动
break_time_active: false # 是否启用休息时间扣减
要点解读:
mode三种取值:default(自由补录,默认)、duration(只填时长不填起止)、punch(打卡模式——禁止任意补录,只能开始/停止,对应「时钟模式」)。当前环境 API 实测GET /api/config/timesheet回显{"trackingMode":"default","defaultBeginTime":"now","activeEntriesHardLimit":1,"isAllowFutureTimes":true,"isAllowOverlapping":true},与 YAML 值一一对应(trackingMode即mode);hard_limit: 1的实际语义:用户再次开始记录时,前一条自动停止(不是拒绝);调大后超过 1 条时才要求手工停止。实测中连续创建两条运行中记录,第一条被自动结束,与该默认值一致;rounding的mode支持default(四舍五入口径见上)/nearest/earliest等策略,days数组限定规则生效的星期——可实现「工作日取整、周末不取整」类差异化;lockdown_*系列是审计场景的核心:设定锁定期后,用户不能再修改/新增落在锁定期内的历史记录(配合宽限期给管理员留缓冲)。
五、费率规则实战
5.1 三级费率来源与优先级
Kimai 的金额计算取费率的优先级为:时间记录自身指定 > 活动级费率 > 项目级费率 > 客户级费率 > 用户时薪 > 系统默认(0)。费率分两种形态:hourly_rate(按小时折算)与 fixed_rate(每条记录一口价)。三种实体(活动/项目/客户)都有 rates 子资源(数据库表 kimai2_activities_rates / projects_rates / customers_rates),每条费率可带 user 字段做「针对特定用户」的定向费率。
实测过程(全程 API 操作,认证方式见命令篇):
# 给活动 2 设置小时费率 350 / 内部成本价 280
curl -s -X POST -H "Authorization: Bearer $TOK" -H 'Content-Type: application/json' \
-d '{"rate":350,"internalRate":280}' http://127.0.0.1:8136/api/activities/2/rates
回显:
{"id":1,"user":null,"rate":350.0,"internalRate":280.0,"isFixed":false}
isFixed: false 表示小时费率(true 则为固定费率)。创建一条 1.5 小时记录验证折算:
curl -s -X POST -H "Authorization: Bearer $TOK" -H 'Content-Type: application/json' \
-d '{"project":2,"activity":2,"begin":"2026-10-27T14:00:00","end":"2026-10-27T15:30:00"}' \
http://127.0.0.1:8136/api/timesheets
回显(rate = 1.5 × 350 = 525,internalRate = 1.5 × 280 = 420,hourlyRate 回显取到的费率来源):
{"id":4,"begin":"2026-10-27T14:00:00+0800","end":"2026-10-27T15:30:00+0800","duration":5400,
"rate":525.0,"internalRate":420.0,"fixedRate":null,"hourlyRate":350.0,...}
固定费率形态实测(创建时直接指定 fixedRate,与时长无关):
{"id":3,"begin":"2026-10-27T10:00:00+0800","end":"2026-10-27T12:30:00+0800","duration":9000,
"rate":1200.0,"fixedRate":1200.0,"hourlyRate":null,...}
两条记录、两种费率形态合计 1725,与后续 CLI 生成发票的总价(Total 1725 CNY)精确一致,费率链路端到端验证通过。
5.2 分时段费率(rates 规则)
kimai.yaml 注释给出的官方示例——周末上浮 50%:
kimai:
timesheet:
rates:
weekend:
days: ['saturday', 'sunday']
factor: 1.5
factor 乘在最终时薪上;days 之外的可用维度与 rounding 相同(按星期选择生效日)。该段默认 { }(空)。
六、权限体系参数(kimai.permissions)
6.1 sets / maps / roles 三段结构
Kimai 的权限体系在 YAML 里由三段组成,语义是「权限名 → 权限集 → 角色」:
kimai:
permissions:
sets: # 权限集:一组权限名的命名集合
ACTIVITIES: ['view_activity','create_activity','edit_activity','budget_activity','time_activity','delete_activity','permissions_activity']
TIMESHEET: ['view_own_timesheet','start_own_timesheet','stop_own_timesheet','create_own_timesheet','edit_own_timesheet','export_own_timesheet','delete_own_timesheet','weekly_own_timesheet']
TIMESHEET_OTHER: ['view_other_timesheet','start_other_timesheet','stop_other_timesheet','create_other_timesheet','edit_other_timesheet','export_other_timesheet','delete_other_timesheet']
# ……共 30 余个集合(ACTIVITIES/PROJECTS/CUSTOMERS 各分 _TEAMLEAD/_ALL_TEAM 变体、INVOICE、PROFILE、TAGS、USER、RATE、EXPORT、BILLABLE、TEAMS、LOCKDOWN、REPORTING、EVERYONE 等)
maps: # 角色映射:角色 → 权限集列表
ROLE_USER: ['TIMESHEET','PROFILE', 'EVERYONE']
ROLE_TEAMLEAD: ['ACTIVITIES_TEAMLEAD','PROJECTS_TEAMLEAD','CUSTOMERS_TEAMLEAD','TIMESHEET_OTHER','INVOICE','TIMESHEET','PROFILE','EXPORT','BILLABLE','TAGS','REPORTING', 'EVERYONE']
ROLE_ADMIN: ['ACTIVITIES','PROJECTS','CUSTOMERS','INVOICE','INVOICE_ADMIN','TIMESHEET','TIMESHEET_OTHER','PROFILE','TEAMS','RATE','RATE_OTHER','EXPORT','BILLABLE','TAGS','LOCKDOWN','REPORTING', 'EVERYONE']
ROLE_SUPER_ADMIN: ['ACTIVITIES','PROJECTS','CUSTOMERS','INVOICE','INVOICE_ADMIN','TIMESHEET','TIMESHEET_OTHER','PROFILE','PROFILE_OTHER','USER','TEAMS','RATE','RATE_OTHER','EXPORT','BILLABLE','TAGS','LOCKDOWN','REPORTING', 'EVERYONE']
roles: # 角色直授:不走集合的零散权限
ROLE_USER: ['view_team_member','time_team_project','create_tag','view_reporting']
ROLE_TEAMLEAD: ['view_rate_own_timesheet','view_rate_other_timesheet','hourly-rate_own_profile','view_team_member','hours_other_profile']
ROLE_ADMIN: ['hourly-rate_own_profile','edit_exported_timesheet','teams_own_profile','view_team_member','view_all_data','contract_other_profile','hours_other_profile','create_export_template']
ROLE_SUPER_ADMIN: ['hourly-rate_own_profile','hourly-rate_other_profile','roles_own_profile','supervisor_own_profile','system_information','system_configuration','plugins','edit_exported_timesheet','teams_own_profile','view_team_member','upload_invoice_template','view_all_data','contract_other_profile','hours_other_profile','create_export_template']
要点:
- 四个内置角色的能力分界线在
maps:ROLE_USER只能动自己的时间表和个人配置;ROLE_TEAMLEAD增加「看/管他人的时间表 + 开发票 + 导出」;ROLE_ADMIN增加客户/项目/活动全套管理、费率管理、锁定期操作;ROLE_SUPER_ADMIN独占用户管理(USER)、系统配置(system_configuration)、插件管理(plugins)、发票模板上传(upload_invoice_template)与全局数据可见(view_all_data); - 命名规律:
*_own_*/*_other_*区分本人与他人的数据;_teamlead_后缀是团队领导对团队数据的变体权限;集合名EVERYONE是全员基线(含api_access——API 访问本身就是一项权限); - 扩展角色:在
local.yaml中给新角色名(如ROLE_PM)组合sets即可,无需改代码;自定义角色经命令篇的kimai:user:promote授予。
七、用户与认证参数
7.1 user 段默认值
kimai:
user:
login: true # 是否开放登录页
theme: auto # 用户默认主题(auto=跟随系统深浅色)
registration: false # 开放自助注册(默认关闭,默认仅管理员建号)
wizard: true # 首次登录引导向导
password_reset: true # 允许自助密码重置(邮件)
password_reset_retry_ttl: 3600 # 重置请求重试间隔(秒)
password_reset_token_ttl: 86400# 重置令牌有效期(秒,24 小时)
password_strength: 0 # 密码强度校验级别(0=不强制)
auth_change: false # 是否允许在认证方式间切换
password_strength 是 2.70.0 引入的新参数(0–4,数值越高越严格)。
7.2 LDAP 配置(local.yaml 覆盖形态)
LDAP 在 debug:config 中的默认结构如下(activate: false 即未启用;字段默认值实测):
kimai:
ldap:
activate: false
connection:
host: null
port: 389
useStartTls: false
useSsl: false
bindRequiresDn: true
accountFilterFormat: null
user:
baseDn: null
filter: ''
attributesFilter: '(objectClass=*)'
usernameAttribute: uid
attributes: { }
role:
baseDn: null
usernameAttribute: dn
nameAttribute: cn
userDnAttribute: member
groups: { }
启用形态(引自官方文档 kimai.org/documentation/ldap.html 的 local.yaml 样例,2026-10-27 取证,完整字段以官方页为准):
kimai:
ldap:
activate: true
connection:
# 可用空格分隔多台:'ldap://ldap1.local ldap://ldap2.local',ldaps:// 同理
host: ldap://ldap.example.local
# port: 389 # useSsl 与 useStartTls 互斥,官方建议优先 StartTLS
# useSsl: false
# useStartTls: false
# username: ... # 服务账号(部分服务器要求 DN 形式)
# password: ...
user:
baseDn: ou=people,dc=example,dc=local
filter: '(objectClass=inetOrgPerson)'
role:
baseDn: ou=groups,dc=example,dc=local
LDAP 用户登录后本地自动建号(authenticator 显示为 ldap,kimai:user:list 可见),2.69.0 起支持把 LDAP/SAML 账号转回内部账号(auth_change 相关)。本环境无 LDAP 服务器,以上启用样例为官方文档转述,字段结构为 2.71.0 实测默认值。
7.3 SAML 配置结构
SAML 段默认结构(activate: false):
kimai:
saml:
activate: false
title: 'Login with SAML'
provider: default
usernameAttribute: null
roles:
resetOnLogin: true # 登录时以 IdP 返回的角色重置本地角色
attribute: null
mapping: { }
mapping: { } # IdP 属性 → Kimai 用户字段映射
connection:
cleanupLongRelayState: false
organization: { }
官方文档站提供 Azure AD、Google、Keycloak、Authentik、Microsoft ADFS 五个 SAML 对接专页;元数据与证书字段在 connection.organization 与 mapping 中按 IdP 填写。
八、邮件参数
8.1 MAILER_URL 传输器 DSN
MAILER_URL 使用 Symfony Mailer 的 DSN 语法,常见取值:
| DSN | 行为 |
|---|---|
null://null(.env 默认)/ null://localhost(镜像默认) | 空传输器:邮件「发送」成功但不落地、不投递 |
smtp://用户:密码@smtp.example.com:465 | SMTPS 直连 |
smtp://smtp.example.com:25 | 明文 SMTP |
smtp://smtp.example.com:587?encryption=tls&auth_mode=login | STARTTLS 提交端口 |
8.2 发信实测
bin/console kimai:mail:test admin@example.com
echo "exit=$?"
实测回显(null://localhost 下命令静默成功,无任何输出、退出码 0):
exit=0
解读:kimai:mail:test <收件人> 用 MAILER_URL/MAILER_FROM 配置发一封测试邮件;在默认空传输器下它只走完 Symfony 邮件管道即返回成功——「命令成功」不代表邮件真的发出。生产切换 SMTP 后执行同一条命令,失败时会打印传输异常(认证失败/连接拒绝等),才是有效的发信验证。同理,依赖邮件的「密码重置」功能在默认配置下不可用(用户收不到邮件但系统不报错),上线前必须配置真实 SMTP。
九、发票、导出、日历、主题与其他参数
9.1 invoice 段(发票)
kimai:
invoice:
defaults: # 发票文档(模板)搜索目录
- 'var/invoices/'
- 'templates/invoice/renderer/'
documents: { } # 用户上传模板追加目录
number_format: '{Y}/{cy,3}' # 发票编号格式(默认 2026/001)
upload_twig: false # 是否允许上传 Twig 模板(有代码执行面,默认关)
rounding_mode: classic # 金额舍入(classic/ceiling/floor)
编号格式占位符:{Y} 年、{cy,3} 客户维度计数器(3 位补零);可组合 {D}、{M}、{date}、{cc,4}(客户编号)等。实测环境当前编号 2026/001 与默认格式吻合。
9.2 export 段(导出)
kimai:
export:
defaults:
- 'var/export/'
- 'templates/export/renderer/'
documents: { }
timeout: 60 # 导出渲染超时(秒)
9.3 calendar 段(日历)
kimai:
calendar:
week_numbers: true # 显示周数
day_limit: 4 # 日视图最多显示条数
slot_duration: '00:30:00' # 时间槽粒度
businessHours: {begin: '08:00', end: '20:00'}
visibleHours: {begin: '00:00', end: '23:59'}
weekends: true
dragdrop_amount: 5 # 拖拽创建的条目数
title_pattern: '{activity}' # 日历条目标题模板
google: {api_key: null, sources: { }} # Google 日历聚合(节假日)
9.4 theme 段(主题与品牌)
kimai:
theme:
show_about: true
colors_limited: true # 限制可选颜色为内置色板
color_choices: 'Silver|#c0c0c0,Gray|#808080,...' # 内置 28 色板(实测完整串)
branding:
logo: null # 品牌定制:登录页 logo
mini: null # 侧栏收起 logo
company: null # 公司名
title: null # 浏览器标题
avatar_url: false # 允许用户填外部头像 URL
品牌定制四件套(logo/mini/company/title)支持相对路径(放 var/data)或绝对 URL;color_choices 可整体替换为组织自定义色板。
9.5 customer / project / activity 段(实体编号与选择器)
kimai:
customer:
language: en
choice_pattern: '{name}' # 下拉选择器显示模板
number_format: '{cc,4}' # 客户编号格式(实测 Acme = 0002、Beta = 0003)
rules: {allow_duplicate_number: false}
project:
choice_pattern: '{name}'
copy_teams_on_create: false
number_format: '{pc,4}'
allow_duplicate_number: false
activity:
choice_pattern: '{name}'
allow_inline_create: false
number_format: '{ac,4}'
allow_duplicate_number: false
choice_pattern 支持 {name}、{number}、{comment} 等占位组合,大型实例常用 {name} ({number}) 改善下拉可读性。编号格式占位({cc,4}/{pc,4}/{ac,4})为「对象类别 + 4 位计数」,与 5.1 节 API 实测回显(客户/项目/活动 number 全为 0003 起跳)一致。
9.6 defaults 段(新对象默认值)
kimai:
defaults:
customer: {timezone: null, country: DE, currency: EUR} # 新客户默认国家/币种
user: {timezone: null, language: en, locale: null, theme: auto}
国内部署建议在 local.yaml 中改为 country: CN、currency: CNY、language: zh_CN,减少每次建客户的手工修正(实测环境中已通过 Web 建号时的表单默认值体现为 CNY/Asia/Shanghai)。
十、排障实录
10.1 改了 local.yaml 没生效
现象:local.yaml 写入覆盖项后刷新页面,行为未变。
根因:YAML 改动需要缓存重建(见 3.2 节原理)。
处置:bin/console kimai:reload,输出两个 [OK](cache warmed + config reloaded)后生效;确认手段是 debug:config kimai 看合并值而非看文件。
10.2 .env 里改了 DATABASE_URL 不生效
现象:编辑 .env 的 DATABASE_URL 后重启,应用仍连旧库。
根因:层级 ① 的运行环境变量优先于 .env(1.1 节 debug:dotenv 实测:.env 列的占位值被环境变量覆盖)。
处置:Compose 部署下改编排 environment: 段再 up -d;.env 只在无同名环境变量时才被读取。
10.3 duration_increment 配 null 被按默认处理
现象:按旧文档把 duration_increment 配成 ~(YAML null)期望「禁用时长下拉」,实际下拉仍出现且步进 15。
根因:2.71.0 的合并默认值中 duration_increment: 15(debug:config 实测),null 语义为「跟随取整规则」而非禁用;配置 0 才是禁用。
处置:明确意图——0 禁用、正整数作步进、null 跟随 rounding 规则。
10.4 密码重置邮件「成功」但收不到
现象:用户触发密码重置提示已发送,实际收不到邮件;kimai:mail:test 又显示成功。
根因:默认 MAILER_URL=null://null 是空传输器(8.2 节实测:静默 exit=0),邮件从未离开服务器。
处置:配置真实 SMTP DSN 后重测 kimai:mail:test;DEFAULT_URI 同步改为对外域名,否则重置链接不可点。
10.5 中文/emoji 存库报错或乱码
现象:客户名等含中文的字段写入报编码错误或显示 ?。
根因:连接串丢了 charset=utf8mb4(官方默认串自带),或手工建的库默认字符集不是 utf8mb4。
处置:核对 DATABASE_URL 含 charset=utf8mb4&serverVersion=<真实版本>;本文实测环境全链路(建客户「Beta 咨询公司」→ 发票 PDF → CSV 导出)中文往返无乱码。
10.6 配置速查:改什么、在哪里、怎么生效
| 需求 | 配置位置 | 生效方式 |
|---|---|---|
| 换数据库 | 编排 environment.DATABASE_URL | 重启服务 |
| 换域名/收紧可信主机 | TRUSTED_HOSTS(编排或 .env) | 重启服务 |
| 时间表改打卡模式 | local.yaml → kimai.timesheet.mode: punch | kimai:reload |
| 周末费率上浮 | local.yaml → kimai.timesheet.rates | kimai:reload |
| 新增自定义角色 | local.yaml → kimai.permissions.maps/roles | kimai:reload |
| 开放自助注册 | local.yaml → kimai.user.registration: true | kimai:reload |
| 接 LDAP/SAML | local.yaml → kimai.ldap/saml.activate: true | kimai:reload |
| 发票编号规则 | local.yaml → kimai.invoice.number_format | kimai:reload |
| 品牌 logo/标题 | local.yaml → kimai.theme.branding | kimai:reload |
| 新客户默认 CN/CNY | local.yaml → kimai.defaults.customer | kimai:reload |
CLI 侧的命令行工具链(含 kimai:reload、kimai:install、发票与导出命令的全量实测)见 《Kimai 2.71 常用命令与运维实战》。