unit 4 · Playbooks

YAML pitfalls & the quoting rule

The parser gets your file before Ansible does

essential · 6 min · linux + windows

you may want to read 12 · Playbook anatomy first — but nothing is locked

tl;dr

  • Indentation is structure, and it is spaces only — a tab is a parse error, not a style choice.
  • A value that STARTS with {{ }} must be quoted, because a bare { opens YAML inline-dict syntax.
  • Unquoted yes/no/true/false all become booleans — quote them when you mean the literal word.

why it matters YAML slips are among the most common beginner failures — tabs, unquoted {{ }}, surprise booleans and stray colons all kill a playbook before Ansible reads a single task.

Lesson 12 gave you a working playbook. This lesson covers the four ways YAML quietly wrecks the next one. None are Ansible bugs: a YAML parser reads your file before Ansible sees a single task, and applies YAML’s rules — not Ansible’s. Learn where those rules bite and you’ve pre-empted most beginner syntax errors.

1. Spaces, never tabs

In YAML, indentation is the structure — no braces mark where a block starts and ends, only depth. Two-space indent is the convention, and it must be spaces: one tab anywhere is a hard parse error. Configure your editor now (spaces for .yml, render whitespace) and this trap disappears.

2. The quoting rule

Ansible’s placeholder for “a variable’s value goes here” is {{ var }}. The full templating machinery is lesson 23; today it’s just a placeholder. Here’s the trap every newcomer hits:

- name: Deploy the config
  ansible.builtin.copy:
    src: {{ config_file }} # bare { — YAML chokes here
    dest: /etc/app.conf
BROKEN — fails at parse time, before any task runs

Why: in YAML, { opens an inline flow mapping — {key: value}, the same brace syntax JSON uses. The parser reads {{ config_file }} as a dict inside a dict, finds no key: value shape, and dies with an error that never mentions quoting. The fix costs two characters:

    src: '{{ config_file }}'
    # mid-string needs no quotes — the value doesn't START with {
    dest: /etc/{{ app_name }}.conf
FIXED — quotes make it a string; Ansible substitutes later

The rule is precise: a value that starts with {{ must be quoted. Braces mid-string open as plain text and parse fine. One preview so the rule doesn’t overreach: when: takes a bare expression with no {{ }} at all — that’s lesson 18.

3. The yes that became True

Unquoted yes, no, true and false all parse as booleans — not strings:

vars:
  enable_firewall: true # boolean — what you meant
  cleanup: yes # ALSO a boolean — same as true
  country_code: 'no' # quoted — stays the string "no"
Booleans parse silently — no warning, no error

The surprise cuts both ways. Older playbooks are full of become: yes — it works and means exactly true. The nastier direction is silent data loss: an unquoted no meant as a country code becomes false, with no error at all.

4. Colons and braces inside strings

The last trap: a colon-plus-space inside a value looks like a new key: value pair to the parser.

- name: Deploy: production release # second ": " = parse error
- name: 'Deploy: production release' # quoted — one string
BROKEN then FIXED — quote any value containing ': '

Same family as the quoting rule: :, { and [ are live YAML syntax, so a string containing them needs quotes. When in doubt, quote — it is never wrong. Single quotes are fully literal; double quotes process escapes like \n, occasionally what you want and usually not.

That’s the whole minefield: tabs, leading {{, bare booleans, embedded colons. Each fails (or worse, succeeds) before a single module runs. That is why ansible-playbook --syntax-check is worth running the moment a playbook looks done. Next lesson: what separates a module from a plugin from a collection.

practice — this task fails to parse. why? (1/4)

- name: Deploy the config
  ansible.builtin.copy:
    src: {{ config_file }}
    dest: /etc/app.conf

practice, not progress — the four traps from this lesson

self-check — recall, not a test

  1. 1 Why does src: {{ config_file }} fail before Ansible even runs?

  2. 2 When does a value containing {{ }} NOT need quotes?

  3. 3 You write country_code: no and nothing errors. What did you actually store?

lab (optional — never required)

Write a task with an unquoted {{ }} value, run ansible-playbook --syntax-check to read the real error, then quote the value and watch the same command pass.

needs: The control node from lesson 5.

next up 14 · Modules, plugins & collections 5 min · unit 4