Why Ansible Uses YAML
YAML — short for YAML Ain't Markup Language — is a human-friendly data serialisation format. Ansible playbooks are YAML files because YAML is easier to read and write by hand than JSON or XML, and because indentation-based structure forces readable layout.
💡 Tip: Every YAML file is also valid JSON when fully expanded. Ansible parses your
.yml file into a Python dict — exactly what JSON would produce — then walks that dict to execute tasks. The Three Things YAML Cares About
- Indentation — uses spaces only (never tabs), and indentation level must be consistent within a block.
- Type of value — scalar (string, int, bool), list, or map.
- The starting marker —
---at the top is optional but conventional. It says "a new YAML document begins here."
Scalars — Strings, Numbers, Booleans
YAML — scalar types
# strings — quotes optional, but required if the value contains: # : { } [ ] , & * ! |
name: deploy # no quotes
greeting: "Hello world" # double-quoted: \n, \t escape sequences active
literal: 'C:\\path' # single-quoted: NO escape sequences (literal backslash)
# numbers — recognised as int / float by default
port: 3306
ratio: 0.75
big: 1_000_000 # underscores allowed for readability
# booleans — many spellings, all valid
enabled: true
debug: yes # also: True, On
silent: false # also: False, no, off
# null
unset: null # also: ~ or just empty
⚠ Warning: Norway problem: the country code
NO in YAML 1.1 evaluates to false. Always quote two-letter country codes and version strings: country: "NO", version: "3.10" (otherwise 3.10 becomes the float 3.1). Lists and Maps — The Two Container Types
YAML — lists and maps
# A list (sequence) — each item starts with a hyphen and a space
fruits:
- apple
- banana
- cherry
# Same list, inline (flow) form
fruits: [apple, banana, cherry]
# A map (dictionary) — key: value pairs
user:
name: deploy
uid: 1000
shell: /bin/bash
# Same map, inline form
user: {name: deploy, uid: 1000, shell: /bin/bash}
# A list of maps — the shape Ansible tasks use
tasks:
- name: Install nginx
ansible.builtin.dnf:
name: nginx
state: present
- name: Start nginx
ansible.builtin.systemd:
name: nginx
state: started
Multi-Line Strings — | and >
YAML — multi-line strings
# Literal block scalar (|) — preserves newlines exactly as written
welcome: |
Welcome to db1.
Authorised users only.
All activity is logged.
# value is: "Welcome to db1.\nAuthorised users only.\nAll activity is logged.\n"
# Folded block scalar (>) — replaces single newlines with spaces
prose: >
This is one long sentence
written across multiple lines
for readability.
# value is: "This is one long sentence written across multiple lines for readability.\n"
# Strip-trailing-newline variant (|-)
script: |-
#!/usr/bin/env bash
echo "hello"
# value ends without a trailing \n
How a Playbook is Just YAML
An Ansible playbook is a list of plays. Each play is a map. Each play has a key
called tasks whose value is a list of maps. Once you see it that way, the structure
clicks:
YAML — playbook structure annotated
--- # optional doc start
- name: Configure database servers # a play (a map)
hosts: databases # play-level key
become: true
vars:
mysql_root_password: secret
tasks: # list of tasks
- name: Install MySQL # one task (a map)
ansible.builtin.dnf: # module name
name: mysql-server # module args (also a map)
state: present
- name: Start MySQL
ansible.builtin.systemd:
name: mysqld
state: started
enabled: true
- name: Configure web servers # second play in same file
hosts: webservers
tasks:
- name: Install nginx
ansible.builtin.dnf:
name: nginx
state: present
The Seven Beginner Gotchas
| Gotcha | Symptom | Fix |
|---|---|---|
| Tabs instead of spaces | YAML parser error | Set editor to "spaces, 2-wide" for .yml |
| Inconsistent indent within a block | did not find expected key | Pick 2 spaces and stay consistent |
Unquoted version 3.10 | Becomes 3.1 (float) | Quote it: "3.10" |
Unquoted NO / YES / ON | Becomes a boolean | Quote two-letter codes: "NO" |
| Colon in unquoted string | mapping values are not allowed | Quote: msg: "Hello: world" |
Forgetting - for list items | Item parses as a key | Add hyphen + space at start of each item |
Mixing flow (Ellipsis) and block | Subtle parse errors | Pick one style per file |
✅ Tip: Lint your YAML before running playbooks:
pip install yamllint, then yamllint -d default *.yml. It catches every gotcha above in under a second. Validate Just Your Ansible Playbook
BASH — syntax-check a playbook without running it
ansible-playbook --syntax-check site.yml
# parses YAML, resolves imports, validates every task name and module
# does NOT connect to any host and does NOT run any task
# even better: ansible-lint checks for best-practice violations
pip install ansible-lint
ansible-lint site.yml