How to Fix Docker Compose 'yaml: unmarshal errors' Invalid Indentation in compose.yaml
Threat/Impact Level: HIGH | Downtime Risk: HIGH | Time to Fix: 5–15 mins
TL;DR
- What broke:
docker-compose config(ordocker compose config) is aborting withyaml: unmarshal errorsbecause the YAML parser hit structurally invalid indentation — mixed tabs/spaces, wrong nesting depth, or a key at an illegal indent level. - How to fix it: Enforce 2-space indentation throughout, eliminate all tab characters, and validate nesting hierarchy against the Compose spec.
- Fast path: Use our Client-Side Sandbox above to auto-refactor this — paste your broken compose.yaml and it will pinpoint the offending lines and emit corrected output without sending your config anywhere.
▼ 🔍 Root Cause Breakdown & Kernel Diagnostics [Click to Expand Deep Dive]
The Incident (What Does the Error Mean?)
Raw error output from a broken compose.yaml:
$ docker compose config
yaml: unmarshal errors:
line 14: cannot unmarshal !!str `ports` into compose.ServiceConfig
line 22: field volumes not found in type compose.ServiceConfig
ERROR: The Compose file './compose.yaml' is invalid because:
services.api.image must be a stringThe Go YAML parser (gopkg.in/yaml.v3) is strict. When it encounters a key at an indentation level it doesn't expect — because a tab was used instead of spaces, or a block was indented 3 spaces instead of 2 — it misidentifies the key's parent node. ports gets parsed as a string value of the previous key instead of a sibling mapping. Everything downstream of that line is misread. docker compose up, docker compose run, and any CI pipeline calling docker compose config as a lint gate will hard-fail.
▼ 🔍 Detailed Impact & Blast Radius Analysis [Click to Expand Deep Dive]
The Attack Vector / Blast Radius
This is not a security vulnerability in the traditional sense, but the blast radius in a CI/CD pipeline is severe:
- Full deployment gate failure. Any pipeline using
docker compose configas a validation step will exit non-zero. Deployments stop. - Silent misconfiguration risk. If your editor auto-corrects the YAML just enough to parse without error but the nesting is semantically wrong (e.g., an
environmentblock attaches to the wrong service), you deploy with incorrect env vars — wrong DB hosts, wrong secrets references, wrong resource limits — without a parse error ever being thrown. - Volume mount misreads are the worst case: a malformed
volumesblock that parses as a string instead of a mapping silently drops the mount definition. Your container starts with no persistent storage. Data loss on first write. - Multi-developer drift. One engineer uses VSCode with tab expansion, another uses vim with
noexpandtab. The file becomes a mix of tabs and spaces. YAML spec (section 6.1) forbids tabs as indentation. The parser will reject the file on any strict-mode toolchain.
How to Fix It (The Solution)
Basic Fix: Strip Tabs, Enforce 2-Space Indentation
Run this before anything else:
# Detect tab characters in your compose file
cat -A compose.yaml | grep -P "^\t"
# Convert all tabs to 2 spaces (GNU sed)
sed -i 's/\t/ /g' compose.yaml
# Validate immediately
docker compose configRoot Cause Example — Broken vs. Fixed
services:
api:
image: myapp:latest
ports:
- - "8080:8080" # TAB-indented — parser reads this as string scalar
+ - "8080:8080" # 6 spaces: 2 (services) + 2 (api) + 2 (list item)
environment:
- DATABASE_URL: postgres://db:5432/app # 6 spaces — correct
- SECRET_KEY: supersecret # 8 spaces — WRONG, misaligned sibling
+ DATABASE_URL: postgres://db://db:5432/app
+ SECRET_KEY: supersecret # 6 spaces — correct sibling
volumes:
- - ./data:/var/lib/app/data # 4 spaces — wrong nesting under service
+ - ./data:/var/lib/app/data # 6 spaces — correctEnterprise Best Practice: Schema Validation + Editor Enforcement
1. Use docker compose config as a hard lint gate — never skip it.
# Makefile
- deploy:
- docker compose up -d
+ lint:
+ docker compose config > /dev/null
+ deploy: lint
+ docker compose up -d2. Enforce YAML formatting via .editorconfig at the repo root:
+ [*.yaml]
+ indent_style = space
+ indent_size = 2
+ tab_width = 2
+ trim_trailing_whitespace = true
+ insert_final_newline = true3. Add yamllint with a strict config:
# .yamllint.yml
extends: default
rules:
indentation:
spaces: 2
indent-sequences: true
check-multi-line-strings: true
truthy:
allowed-values: ['true', 'false']pip install yamllint
yamllint -c .yamllint.yml compose.yaml💡 Tired of pasting proprietary infrastructure configs into public LLMs? Generic AI interfaces log your company's ARNs, connection strings, and private keys. StackEngine enforces client-side zero-trust boundaries—the sandbox above executes 100% locally in your browser. Drop your broken manifest above to redact secrets locally and synthesize deterministic patch drafts with your own API key.
(For CI/CD pipeline automation, install via
npm i -g stackengine).
▼ 🔍 Continuous Pipeline Governance & CI/CD Guardrails [Click to Expand Deep Dive]
Prevention in CI/CD
GitHub Actions — Gate on Every PR
# .github/workflows/compose-lint.yml
name: Validate Compose
on: [pull_request]
jobs:
lint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: yamllint
run: |
pip install yamllint
yamllint -c .yamllint.yml compose.yaml
- name: docker compose config
run: docker compose config > /dev/nullPre-commit Hook (Local Enforcement)
# .pre-commit-config.yaml
repos:
- repo: https://github.com/adrienverge/yamllint
rev: v1.35.1
hooks:
- id: yamllint
args: ['-c', '.yamllint.yml']
- repo: https://github.com/IamTheFij/docker-pre-commit
rev: v3.0.1
hooks:
- id: docker-compose-checkCheckov for Compose Security + Syntax
pip install checkov
checkov -f compose.yaml --framework dockerfileCheckov will catch indentation-caused misconfigurations and flag security issues like privileged: true or missing read-only root filesystems in the same pass.
The rule: docker compose config must return exit code 0 before any image build or deployment step is permitted to run. No exceptions.
Official Grounding & Normative Specifications
The technical diagnostics, error recovery protocols, and prevention patterns in this specification are grounded in official vendor documentation and standards:
Federal security guidelines for secret exfiltration prevention, automated integrity checks, and peer sign-offs.
Core memory subsystem controls, pressure stall information (PSI), and kernel OOM invocation mechanics.
Industry-standard specification for container execution boundaries and POSIX namespace isolation.