docker compose 配置解析失败速查:YAML 语法与版本字段排障
更新时间:2026-10-24。FAQ 速查页,面向「
docker compose up还没碰到容器就死在读文件这一步」的同学——报一行yaml: line 12: mapping values are not allowed,或者刷一排The "XX" variable is not set警告然后起出奇怪的服务。docker compose 配置解析失败的本质一句话:compose 先把 yaml 文本变成数据模型再校验语义,任何一层不过关都起不来——缩进错位、冒号引号误用是文本层,环境变量未定义、env_file 找不到是插值层,version 字段、键名拼错是语义层。病灶收敛在六根轴上。YAML 语法之外的 Dockerfile 语法问题看 docker 构建语法速查;起起来后的冲突看 docker 容器名与端口冲突速查。
本文要回答的问题
yaml: line N: mapping values are not allowed到底错在哪一格?The "DB_HOST" variable is not set. Defaulting to a blank string是警告还是错误?- 顶部的
version: "3.8"为什么被提示the attribute 'version' is obsolete? env_file明明写了相对路径,为什么报 no such file?- 改了 compose 文件,
docker compose config怎么提前发现错误?
一、报错原文与典型现象
解析失败的报错都发生在容器创建之前,指纹清晰——「命令一敲就炸,不碰镜像不碰网络」(路径以 /proj/demo 占位,镜像源用 registry.example.com 占位):
# 指纹一:YAML 文本层 —— 缩进或冒号把一行解析成了两种结构
$ docker compose -f /proj/demo/docker-compose.yml up
yaml: line 12: mapping values are not allowed in this context# 指纹二:插值层 —— 变量没定义,compose 用空串顶替并警告
$ docker compose up
WARN[0000] The "DB_HOST" variable is not set. Defaulting to a blank string.
WARN[0000] The "DB_PORT" variable is not set. Defaulting to a blank string.# 指纹三:语义层 —— version 字段已弃用,Compose v2 不再读它
$ docker compose config
WARN[0000] /proj/demo/docker-compose.yml: the attribute `version` is obsolete,
it will be ignored, please remove it to avoid potential confusion# 指纹四:env_file 路径 —— 相对 compose 文件还是当前目录搞反了
$ docker compose up
env file /proj/demo/config/.env.prod not found: stat /proj/demo/config/.env.prod:
no such file or directory读现象抓三条判据:凡是没拉镜像、没建网络就退出的,一律先怀疑解析层;yaml: 开头的报错带行号,直接跳到该行看缩进与冒号;WARN 级别的「variable is not set」「version is obsolete」不挡启动但会埋雷——变量空串拼出的连接串、镜像名会以更奇怪的方式在运行期炸。
二、病因处置速查表
| 报错现象 / 场景 | 可能原因 | 快速验证 | 处置 |
|---|---|---|---|
| mapping values are not allowed | 值里裸冒号未加引号 | 跳到报错行看冒号 | 含冒号值整体加引号 |
| did not find expected key | 同级键缩进不齐 | 对齐报错行前后缩进 | 统一用空格、级差 2 |
| variable is not set 警告 | shell 与 .env 都没该变量 | echo 验证 + 看 .env | 写进 .env 或导出环境变量 |
| version is obsolete 警告 | Compose v2 弃用 version 字段 | docker compose version | 删除 version 行 |
| env_file not found | 路径相对基准理解错误 | compose config 看解析结果 | 路径相对 compose 文件写 |
| services 下键不生效 | 顶节键名拼错(如 service) | compose config 输出缺该节 | 对照官方文档修正键名 |
| 端口映射成怪数字 | "8080:80" 写成八进制误读 | compose config 看端口 | 端口映射统一加引号 |
| 多文件 -f 合并结果不对 | 合并顺序与覆盖规则误解 | compose config 看合并后 | 调整 -f 顺序,先通用后覆盖 |
三、排查决策图
这张图是标准排查路线:红是解析失败的现象与根因归类,蓝是排查动作,绿是验证通过,橙是修复,紫是防复发沉淀。
读这张图抓住关键一步:docker compose config 是唯一权威视角——它输出的是插值、合并、默认值全部生效后的最终模型,报错行号对不上源文件时,看 config 的输出能立刻分辨是文本层还是插值层的问题。养成「改 compose 文件先 config 再 up」的习惯,九成的解析失败在本地五秒内现形。
四、逐条排查展开
裸冒号是 mapping values 报错的第一嫌疑。 YAML 里 key: value 的冒号后必须跟空格;值里再出现「冒号+空格」会被当成新的映射。重灾区是命令行与时间格式:
# 错:值里的 ": " 触发二次解析
command: echo time: now
# 对:整值加引号
command: "echo time: now"同理,URL、Windows 盘符、cron 表达式都建议养成加引号的习惯。报错行号有时指向下一行——冒号错位会让解析器「以为」映射在该行结束。
缩进不齐是 did not find expected key 的根因。 YAML 只用空格不用 Tab(Tab 在某些解析器直接报错),同级键必须列对齐。最常见事故是从网上抄配置混进了 Tab,或 environment: 下的列表用了两种缩进级差。编辑器开「显示空白字符」,把 Tab 全部换成空格,级差统一 2 格。
变量插值失败不挡启动但最坑人。 compose 的 ${VAR} 查找顺序:shell 环境变量 > compose 文件同目录的 .env 文件 > 空串(同时 WARN)。空串拼出的 image: registry.example.com/app:${TAG} 变成 app:——标签为空,行为未定义。处置两选一:变量写进 .env(注意 .env 只供插值,不会自动进容器,进容器要用 environment: 或 env_file:);或启动前 export。验证永远用 docker compose config | grep 看插值结果,别看源文件猜。
version 字段在 Compose v2 是死信。 version: "3.8" 是 Compose v1(Python 版)时代的字段,v2(Go 版,docker compose 子命令)按最新规范解析、完全忽略它,只刷一条 obsolete 警告。处置就是删掉这一行;留着不炸但会让读者误以为「受 3.8 规范约束」,掩盖真实行为。顺带确认本机是 v2:docker compose version(有空格、是子命令),而不是 docker-compose --version(带横杠、是 v1)。
env_file 与构建 context 的路径基准不同。 env_file: 的相对路径相对 compose 文件所在目录;而 build: context: 也相对 compose 文件,但 Dockerfile 里的 COPY 又相对 context——三套基准混用是「文件明明在」却 not found 的根源。团队约定:路径一律相对 compose 文件写,跨目录引用用 ../ 显式表达,并在 README 写明基准。
键名拼错被静默忽略最阴险。 把 services: 写成 service:、deploy: 在旧版被忽略、mem_limit 与 memory 混用——compose 对不认识的键多数情况只是警告或直接忽略,服务起得来但配置没生效。防线就是 docker compose config:它输出的是「compose 真正理解的模型」,源文件写了但输出里没有的键,就是拼错或不支持的。
五、规范做法与预防
- config 预检进 CI 与 pre-commit:任何 compose 文件变更先
docker compose config -q,非零退出即拦截。 - 删光 version 字段:存量文件批量清理,杜绝「以为受旧规范约束」的误读。
- .env 模板入库:
.env.example列全所有变量与示例值,真.envgitignore;新人复制即用,variable is not set 归零。 - 含冒号/特殊字符的值一律引号:端口映射、command、环境变量值,养成默认加引号的手感。
- 编辑器装 YAML 插件:缩进与 Tab 问题在写入瞬间标红,比报错后找行号快一个量级。
- 路径基准写进团队约定:compose 相对路径的解析基准一句话写进 README,消除三套基准混用。
这一篇在知识体系里的位置
本篇是 docker 排错速查簇的 compose 配置篇,定位「起容器之前就失败」:Dockerfile 文本层的同类问题看 docker 构建语法速查;配置过了但服务互踩看 docker 容器名与端口冲突速查;构建期行为不符合预期看 docker 构建缓存陈旧速查;时好时坏的网络层问题看 docker 构建拉取偶发失败速查。config 预校验、变量模板化、路径基准约定,是 compose 文件写得稳的前提。
一句话总结
docker compose 配置解析失败的规律是「三层各管一段」:文本层(缩进、Tab、裸冒号)报 yaml: line N 错,插值层(变量未定义、env_file 路径错)刷 WARN 或 not found,语义层(version 弃用、键名拼错)警告或静默忽略。排查的通式是一条命令——docker compose config 看插值合并后的最终模型,输出与源文件对不上的地方就是病灶。治本靠 pre-commit 挂 config 校验、.env.example 模板化变量、删光 version 字段、含冒号的值默认加引号,把解析失败挡在提交之前。