AnsiblePilot — Master Ansible Automation

AnsiblePilot is the leading resource for learning Ansible automation, DevOps, and infrastructure as code. Browse over 1,400 tutorials covering Ansible modules, playbooks, roles, collections, and real-world examples. Whether you are a beginner or an experienced engineer, our step-by-step guides help you automate Linux, Windows, cloud, containers, and network infrastructure.

Popular Topics

About Luca Berton

Luca Berton is an Ansible automation expert, author of 8 Ansible books published by Apress and Leanpub including "Ansible for VMware by Examples" and "Ansible for Kubernetes by Example", and creator of the Ansible Pilot YouTube channel. He shares practical automation knowledge through tutorials, books, and video courses to help IT professionals and DevOps engineers master infrastructure automation.

Ansible Write to File: 5 Methods with Practical Examples (2026)

By Luca Berton · Published 2024-01-01 · Category: database-automation

Complete guide to writing content to files with Ansible. Use copy, template, lineinfile, blockinfile, and shell modules to create files, write variables.

Writing content to files is one of the most common tasks in Ansible automation. Whether you need to create configuration files, save command output, write variables to disk, or append lines to existing files, Ansible provides multiple modules for every scenario.

This guide covers five proven methods to write to files with Ansible, from simple string writes to complex template rendering.

Method 1: ansible.builtin.copy — Write String Content

The copy module with the content parameter is the simplest way to write text to a file.

Write a Simple String

- name: Write a string to a file
  ansible.builtin.copy:
    content: "Hello, World!\n"
    dest: /tmp/hello.txt
    mode: '0644'

Write a Variable to a File

- name: Gather facts and write hostname to file
  ansible.builtin.copy:
    content: "{{ ansible_hostname }}\n"
    dest: /tmp/hostname.txt
    mode: '0644'

- name: Write JSON data to file ansible.builtin.copy: content: "{{ my_variable | to_nice_json }}\n" dest: /tmp/data.json mode: '0644'

Write Multi-line Content

- name: Write multiple lines to a file
  ansible.builtin.copy:
    content: |
      # Server Configuration
      server_name={{ inventory_hostname }}
      environment={{ env }}
      deployed_at={{ ansible_date_time.iso8601 }}
    dest: /etc/app/config.txt
    mode: '0644'

Write Command Output to a File

- name: Run a command
  ansible.builtin.command: df -h
  register: disk_usage
  changed_when: false

- name: Save command output to file ansible.builtin.copy: content: "{{ disk_usage.stdout }}\n" dest: /tmp/disk_report.txt mode: '0644'

When to use copy: Simple content writes, variable dumps, command output saves, single-file creation.

See also: ansible.builtin.file Module: Manage Files, Directories & Symlinks (Complete Guide)

Method 2: ansible.builtin.template — Jinja2 Templates

For complex file generation with loops, conditionals, and dynamic content, use the template module with a Jinja2 template file.

Template File (templates/config.j2)

# Generated by Ansible on {{ ansible_date_time.iso8601 }}
# Do not edit manually

{% for item in servers %} server {{ item.name }} {{ item.ip }}:{{ item.port | default(8080) }} {% endfor %}

{% if enable_ssl | default(false) %} ssl_certificate /etc/ssl/certs/{{ domain }}.crt ssl_certificate_key /etc/ssl/private/{{ domain }}.key {% endif %}

Playbook Task

- name: Generate configuration from template
  ansible.builtin.template:
    src: templates/config.j2
    dest: /etc/app/server.conf
    owner: root
    group: root
    mode: '0644'
    backup: yes
  vars:
    servers:
      - name: web1
        ip: 192.168.1.10
      - name: web2
        ip: 192.168.1.11
        port: 9090
    enable_ssl: true
    domain: example.com

When to use template: Configuration files, dynamic content with loops/conditionals, any file that needs Jinja2 rendering.

Method 3: ansible.builtin.lineinfile — Write or Replace a Single Line

The lineinfile module ensures a specific line exists in a file, or replaces a matching line.

Add a Line to a File

- name: Ensure line exists in file
  ansible.builtin.lineinfile:
    path: /etc/environment
    line: 'JAVA_HOME=/usr/lib/jvm/java-17-openjdk'
    create: yes

Replace a Line Using Regex

- name: Update max connections in PostgreSQL config
  ansible.builtin.lineinfile:
    path: /etc/postgresql/15/main/postgresql.conf
    regexp: '^max_connections\s*='
    line: 'max_connections = 200'
    backup: yes

Append After a Pattern

- name: Add DNS server after existing entries
  ansible.builtin.lineinfile:
    path: /etc/resolv.conf
    insertafter: '^nameserver'
    line: 'nameserver 8.8.8.8'

When to use lineinfile: Single-line changes, config file updates, ensuring a specific line exists.

See also: Ansible 'fatal: template error while templating string' Fix (Guide)

Method 4: ansible.builtin.blockinfile — Write a Block of Text

The blockinfile module manages a marked block of text within a file.

Add a Block to a File

- name: Add SSH config block
  ansible.builtin.blockinfile:
    path: /etc/ssh/sshd_config
    marker: "# {mark} ANSIBLE MANAGED - Security Settings"
    block: |
      PermitRootLogin no
      PasswordAuthentication no
      MaxAuthTries 3
      ClientAliveInterval 300
    backup: yes

Dynamic Block with Variables

- name: Add host entries
  ansible.builtin.blockinfile:
    path: /etc/hosts
    marker: "# {mark} ANSIBLE MANAGED BLOCK"
    block: |
      {% for host in groups['webservers'] %}
      {{ hostvars[host]['ansible_default_ipv4']['address'] }} {{ host }}
      {% endfor %}

When to use blockinfile: Multi-line inserts, managed config sections, content that needs to be updated as a unit.

Method 5: ansible.builtin.shell — Redirect Output to File

For complex scenarios or when you need shell features like pipes and redirects.

- name: Write process list to file
  ansible.builtin.shell: ps aux --sort=-%mem | head -20 > /tmp/top_processes.txt
  changed_when: true

- name: Append to a log file ansible.builtin.shell: echo "Deploy completed at $(date)" >> /var/log/deploys.log changed_when: true

> ⚠️ Best Practice: Prefer copy or template over shell for writing files. Shell commands are harder to make idempotent and don't support check mode.

See also: Ansible Collections: What They Are & How to Use Them (2026 Guide)

Comparison Table

| Feature | copy | template | lineinfile | blockinfile | shell | |---------|------|----------|------------|-------------|-------| | Create new file | ✅ | ✅ | ✅ (create: yes) | ✅ (create: yes) | ✅ | | Write string | ✅ | ✅ | Single line | Block | ✅ | | Write variable | ✅ | ✅ | ✅ | ✅ | ❌ | | Jinja2 rendering | In content | ✅ Full | In line | In block | ❌ | | Idempotent | ✅ | ✅ | ✅ | ✅ | ❌ | | Backup support | ✅ | ✅ | ✅ | ✅ | ❌ | | Check mode | ✅ | ✅ | ✅ | ✅ | ❌ | | Append mode | ❌ | ❌ | ✅ | ✅ | ✅ |

Best Practices

1. Always Set File Permissions

- name: Write sensitive config
  ansible.builtin.copy:
    content: "{{ db_password }}"
    dest: /etc/app/db_secret
    owner: appuser
    group: appgroup
    mode: '0600'

2. Use backup: yes for Critical Files

- name: Update config with backup
  ansible.builtin.template:
    src: nginx.conf.j2
    dest: /etc/nginx/nginx.conf
    backup: yes

3. Validate Before Writing

- name: Write and validate nginx config
  ansible.builtin.template:
    src: nginx.conf.j2
    dest: /etc/nginx/nginx.conf
    validate: nginx -t -c %s

4. Use no_log for Sensitive Data

- name: Write secrets file
  ansible.builtin.copy:
    content: "{{ vault_api_key }}"
    dest: /etc/app/api_key
    mode: '0600'
  no_log: true

Write Dictionary or List to File

- name: Write dictionary as YAML
  ansible.builtin.copy:
    content: "{{ my_dict | to_nice_yaml }}\n"
    dest: /tmp/config.yml
    mode: '0644'

- name: Write list as JSON ansible.builtin.copy: content: "{{ my_list | to_nice_json }}\n" dest: /tmp/data.json mode: '0644'

- name: Write list as one item per line ansible.builtin.copy: content: "{{ my_list | join('\n') }}\n" dest: /tmp/items.txt mode: '0644'

Write to Local File (Controller)

- name: Save inventory to local file
  ansible.builtin.copy:
    content: "{{ groups['all'] | join('\n') }}\n"
    dest: /tmp/inventory_hosts.txt
    mode: '0644'
  delegate_to: localhost
  run_once: true

- name: Write to local file using local_action local_action: module: ansible.builtin.copy content: "{{ hostvars[inventory_hostname] | to_nice_json }}\n" dest: "/tmp/facts_{{ inventory_hostname }}.json"

FAQ

What is the simplest way to write a string to a file in Ansible?

Use ansible.builtin.copy with the content parameter: copy: content="Hello" dest=/tmp/file.txt. This is idempotent — it only writes when content changes.

How do I write a variable to a file?

Use the copy module with Jinja2 syntax: copy: content="{{ my_variable }}" dest=/tmp/output.txt. For structured data, use to_nice_json or to_nice_yaml filters.

How do I append to a file instead of overwriting?

Use lineinfile to add a single line, blockinfile to add a block, or shell with >> redirect for simple appends. The copy and template modules always overwrite the entire file.

How do I write command output to a file?

Register the command output then write it: first command: df -h with register: result, then copy: content="{{ result.stdout }}" dest=/tmp/output.txt.

What is the difference between copy content and template?

copy with content is best for simple strings and variables. template uses a separate .j2 file with full Jinja2 support (loops, conditionals, includes) — better for complex configuration files.

How do I create a file only if it does not exist?

Use copy with force: no: copy: content="default" dest=/tmp/file.txt force=no. This creates the file with default content but never overwrites existing content.

Conclusion

Ansible provides five primary methods to write content to files: • copy — Best for simple string and variable writes • template — Best for complex files with Jinja2 logic • lineinfile — Best for single-line additions or replacements • blockinfile — Best for managed multi-line sections • shell — Last resort for complex shell operations

Choose the simplest module that meets your needs. Start with copy, upgrade to template when you need loops or conditionals, and use lineinfile/blockinfile for surgical edits to existing files.

Related Articles

Ansible Write Variable to File: copy vs template Module GuideApply a File Template: Ansible Module templateEdit Single Line Text: Ansible Module lineinfileEdit Multi-line Text: Ansible Module blockinfileCopy Files to Remote Hosts: Ansible Module copyCreate a Text File: Ansible Module copy

Category: database-automation

Browse all Ansible tutorials · AnsiblePilot Home