AnsibleX · YAML for Reliable AutomationYAML for reliable automation
Multiline values that survive
What you'll learn
- Distinguish literal and folded block scalars by what they do to newlines
- Apply the strip, clip and keep chomping indicators correctly
- Choose the right form for a file body, a long command and a long description
- Predict the exact string a block scalar produces, including trailing newlines
- Recognise the indentation rule that makes a block scalar lose content
Prerequisites
Verified against ansible-core 2.21.x · ansible (community package) 14.x · Python (controller) 3.12+ · ansible-lint 26.x · Molecule 26.x · Ubuntu 24.04 LTS · Debian 12 (Bookworm) · RHEL / Rocky / AlmaLinux 9.x · 2026-08-11
Two characters decide what happens to the newlines in a multiline YAML value, and a third decides what happens at the end. Between them they produce five distinct results from identical-looking source, and the difference matters most for the two things people write multiline values for: file contents and shell commands.
| Indicator | Newlines inside | Trailing newlines |
|---|---|---|
| | kept | exactly one (clip) |
|- | kept | none (strip) |
|+ | kept | all of them (keep) |
> | folded to spaces | exactly one (clip) |
>- | folded to spaces | none (strip) |
The same content, five ways
Rather than describe the difference, here is the result. <NL> marks
each newline, and |END marks where the string stops.
vars:
literal_clip: |
line one
line two
literal_strip: |-
line one
line two
literal_keep: |+
line one
line two
folded_clip: >
line one
line two
folded_strip: >-
line one
line two$ ansible-playbook -i localhost, multiline.ymlok: [localhost] => (item=literal_clip (|) ) => {
"msg": "literal_clip (|) -> line one<NL>line two<NL>|END"
}
ok: [localhost] => (item=literal_strip (|-)) => {
"msg": "literal_strip (|-) -> line one<NL>line two|END"
}
ok: [localhost] => (item=literal_keep (|+)) => {
"msg": "literal_keep (|+) -> line one<NL>line two<NL><NL>|END"
}
ok: [localhost] => (item=folded_clip (>) ) => {
"msg": "folded_clip (>) -> line one line two<NL>|END"
}
ok: [localhost] => (item=folded_strip (>-)) => {
"msg": "folded_strip (>-) -> line one line two|END"
}Read the |+ result carefully: it kept two newlines, because the
source had a blank line after line two before the next key. That is
the “keep” behaviour doing exactly what it says, and it is why |+ is
rarely what you want — the value now depends on how many blank lines a
colleague left in the file.
Where each one is correct
| for a file body
A configuration file, a message of the day, an SSH key, a systemd unit
fragment. Files end with a newline; | gives you exactly one.
- name: Install the login banner
ansible.builtin.copy:
dest: /etc/motd
owner: root
group: root
mode: '0644'
content: |
This system is managed by Ansible.
Local changes will be overwritten on the next run.
Contact: ops@example.com|- for a value that must not end in a newline
A password, a token, a single-line value that happens to be long, an argument to something that will not strip whitespace for you.
vars:
backend_url: >-
https://metrics.internal.example.com/api/v2/write
?org=platform&bucket=fleet&precision=sNote that this uses >-, not |-: the folded form joins the two source
lines. Which brings us to the trap in the folded style.
> folds — including where you did not want it to
- name: Roll the logs and reload rsyslog
ansible.builtin.shell:
cmd: |
set -e
logrotate -f /etc/logrotate.d/app
systemctl reload rsyslog
executable: /bin/bash
changed_when: true> for prose that has to fit in a line-length limit
Long description, fail_msg, msg and name values. The folded form
lets you wrap at 80 columns in the file and produce one line in the
output.
- name: Refuse to proceed on an unrecognised environment
ansible.builtin.assert:
that:
- target_env in ['dev', 'staging', 'prod']
fail_msg: >-
target_env is {{ target_env | default('UNDEFINED') }}, which is not
one of dev, staging or prod. Check the inventory group this host
belongs to before rerunning.The indentation rule
A block scalar’s content indentation is set by its first non-empty line. Everything is measured from there, and anything less indented ends the block.
There are two outcomes, and only one of them is loud.
content: |
first line
second linecontent: |
line one
line two
line threeThat second block produces 'line one\\n line two\\nline three\\n' —
two leading spaces on the middle line, permanently, in the file you
write. For a config format that treats indentation as meaningful, or a
shell command where a stray indent is harmless, it does not matter. For
a key file, a checksum manifest or an /etc/hosts fragment, it does.
The reliable habit is to indent every line of the block by exactly one
level from the key, and to let nothing vary from there unless the
content genuinely wants it. yamllint catches most of these; lesson 6
covers it.
Knowledge check
Knowledge check · 4 questions
Q1. A task uses ansible.builtin.shell with a folded block scalar containing three commands on three source lines. What runs on the managed node?
Q2. Which of these produce a value with no trailing newline at all? Select all that apply.
Q3. A blank line inside a folded block scalar survives as a real newline rather than being folded into a space.
Q4. A copy task with an inline literal content block writes a file in which one line has two unexpected leading spaces. What happened?
Passing score: 75%. Answers are checked in this browser.