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 blockinfile Module: Insert & Manage Multi-Line Text Blocks (Guide)

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

How to insert and manage multi-line text blocks with Ansible blockinfile module (ansible.builtin.blockinfile). Add config sections, manage markers.

Ansible blockinfile Module: Insert & Manage Multi-Line Text Blocks (Guide)

Today we’re going to talk about how to edit a multi-line text in a file with Ansible and so much more. Ansible module blockinfile. I’m Luca Berton and welcome to today’s episode of Ansible Pilot

Ansible module blockinfile

Today we’re talking about Ansible module blockinfile. The full name is ansible.builtin.blockinfile, which means that is part of the collection of modules “builtin” with ansible and shipped with it. It’s a module pretty stable and out for years and it supports a large variety of operating systems. You are able to insert, update and remove a block of multi-line text in a file. This block is going to be surrounded by customizable marker lines, just to identify that this edit was performed by Ansible.

See also: Edit single-line text - Ansible Playbook for Changing IP Address of Remote Hosts

Main Parameters

• path _string_ • block _string_ • insertafter/insertbefore _string_ • validate _string_ • create _boolean_ • state _string_ • marker_begin/marker_end _string_ • mode/owner/group • setype/seuser/selevel

This module has some parameters to perform any tasks.

The only required is “path” parameter, where you specify the filesystem path of the file we’re going to edit.

“block” parameter is the text we would like to insert in the file, easy!

By default, the text is going to be inserted at the end of the file, but we could personalize it in a specific position with “insertafter”/”insertbefore” parameter.

If there is any tool to validate the file we could specify in the validate parameter, very useful for configuration files.

If the file does not exist we could create it!

Usually, we would like to insert a text block but we could also remove using state in conjunction with parameter absent.

Our text is going to be surrounded by some markers, some comments, that show up that we did this edit with Ansible. We could customize the text as well.

Let me also highlight that we could also specify some permissions or SELinux property.

Demo

Are you ready to make your hands dirty? Let’s jump in a live Playbook of blockinfile module usage in the Ansible playbook.
---
- name: blockinfile module demo
  hosts: all
  become: true
  tasks:
- name: Generate /etc/hosts file
    ansible.builtin.blockinfile:
      state: present
      dest: /etc/hosts
      content: |
        192.168.0.200 Playbook demo.example.com

code with ❤️ in GitHub

See also: Ansible lineinfile Module: Edit Single Lines in Config Files

Conclusion

Now you know better the Ansible module blockinfile and you could use it successfully in your playbook.

Basic Usage

Add a block to a file

- name: Add SSH banner
  ansible.builtin.blockinfile:
    path: /etc/ssh/banner.txt
    block: |
      ==========================================
      Authorized access only.
      All activity is monitored and logged.
      ==========================================
    create: true
  become: true

Insert after a specific line

- name: Add custom hosts
  ansible.builtin.blockinfile:
    path: /etc/hosts
    insertafter: '^127\.0\.0\.1'
    block: |
      192.168.1.10 web1.internal
      192.168.1.11 web2.internal
      192.168.1.20 db1.internal
  become: true

Custom markers

- name: Add nginx upstream block
  ansible.builtin.blockinfile:
    path: /etc/nginx/conf.d/upstream.conf
    marker: "# {mark} ANSIBLE MANAGED - upstream servers"
    block: |
      upstream backend {
          server 192.168.1.10:8080;
          server 192.168.1.11:8080;
          server 192.168.1.12:8080;
      }
    create: true
  become: true
  notify: reload nginx

Remove a block

- name: Remove managed block
  ansible.builtin.blockinfile:
    path: /etc/hosts
    marker: "# {mark} ANSIBLE MANAGED BLOCK"
    state: absent
  become: true

Multiple blocks in same file

- name: Add database config
  ansible.builtin.blockinfile:
    path: /etc/myapp/config.ini
    marker: "# {mark} DATABASE CONFIG"
    block: |
      [database]
      host=db.example.com
      port=5432

- name: Add cache config ansible.builtin.blockinfile: path: /etc/myapp/config.ini marker: "# {mark} CACHE CONFIG" block: | [cache] host=redis.example.com port=6379

See also: Add Secondary Groups to Linux Users with Ansible Playbook

Dynamic Content

- name: Generate hosts block from inventory
  ansible.builtin.blockinfile:
    path: /etc/hosts
    marker: "# {mark} ANSIBLE MANAGED - web servers"
    block: |
      {% for host in groups['webservers'] %}
      {{ hostvars[host].ansible_host }} {{ host }}
      {% endfor %}
  become: true

Key Parameters

| Parameter | Description | |-----------|-------------| | path | Target file | | block | Multi-line content to insert | | marker | Begin/end markers ({mark} = BEGIN/END) | | insertafter | Insert after this regex | | insertbefore | Insert before this regex | | state | present or absent | | create | Create file if missing | | backup | Create backup before modifying |

blockinfile vs lineinfile vs template

| Module | Lines | Idempotent | Best For | |--------|-------|------------|----------| | lineinfile | Single | Yes | One setting | | blockinfile | Multiple | Yes (via markers) | Config blocks | | template | Entire file | Yes | Full file control |

FAQ

Why do I see "BEGIN/END ANSIBLE MANAGED BLOCK" in my files?

Those are the default markers blockinfile uses to track its content. Customize with the marker parameter.

Can I use blockinfile without markers?

No - markers are required for idempotency. Without them, blockinfile can't find and update existing blocks.

What if I change the marker?

The old block (with old markers) remains, and a new block is added. Remove the old block first with state: absent using the old marker.

Insert Text Block

- name: Add config section
  ansible.builtin.blockinfile:
    path: /etc/myapp/config.conf
    block: |
      # Database settings
      db_host = db.internal
      db_port = 5432
      db_name = myapp
  become: true

Custom Markers

- blockinfile:
    path: /etc/nginx/nginx.conf
    marker: "# {mark} ANSIBLE MANAGED - myapp upstream"
    insertafter: "^http {"
    block: |
      upstream myapp {
          server 10.0.1.10:8080;
          server 10.0.1.11:8080;
      }
  become: true

Result:

# BEGIN ANSIBLE MANAGED - myapp upstream
upstream myapp {
    server 10.0.1.10:8080;
    server 10.0.1.11:8080;
}
# END ANSIBLE MANAGED - myapp upstream

Insert After/Before

# After a pattern
- blockinfile:
    path: /etc/hosts
    insertafter: "^# Internal hosts"
    block: |
      10.0.1.10 web1.internal
      10.0.1.11 web2.internal
      10.0.1.20 db1.internal

# Before a pattern - blockinfile: path: /etc/nginx/nginx.conf insertbefore: "^}" block: | include /etc/nginx/conf.d/*.conf;

Multiple Blocks (Different Markers)

- blockinfile:
    path: /etc/myapp/config.conf
    marker: "# {mark} DATABASE CONFIG"
    block: |
      db_host = db.internal
      db_port = 5432

- blockinfile: path: /etc/myapp/config.conf marker: "# {mark} CACHE CONFIG" block: | cache_host = redis.internal cache_port = 6379

Dynamic Block with Variables

- blockinfile:
    path: /etc/hosts
    marker: "# {mark} ANSIBLE MANAGED - {{ env }} hosts"
    block: |
      {% for host in groups['webservers'] %}
      {{ hostvars[host].ansible_host }} {{ host }}.internal
      {% endfor %}

Remove Block

- blockinfile:
    path: /etc/myapp/config.conf
    marker: "# {mark} OLD CONFIG"
    state: absent
  become: true

SSH Config Example

- blockinfile:
    path: ~/.ssh/config
    marker: "# {mark} ANSIBLE MANAGED - bastion"
    create: true
    mode: '0600'
    block: |
      Host bastion
          HostName bastion.example.com
          User deploy
          IdentityFile ~/.ssh/deploy_key

Host *.internal ProxyJump bastion User deploy

blockinfile vs lineinfile vs template

| Module | Use Case | |--------|----------| | lineinfile | Single line operations | | blockinfile | Multi-line sections with markers | | template | Full file management with Jinja2 | | copy | Static file content |

Key Parameters

| Parameter | Description | |-----------|-------------| | path | Target file | | block | Content to insert | | marker | Begin/end markers ({mark} → BEGIN/END) | | insertafter | Insert after regex match | | insertbefore | Insert before regex match | | state | present or absent | | create | Create file if missing | | backup | Create backup before change | | validate | Validate before applying | | marker_begin | Custom begin word (default: BEGIN) | | marker_end | Custom end word (default: END) |

FAQ

Can I have multiple blocks in one file?

Yes — use different marker strings for each block. Each marker identifies a unique managed section.

What if I change the block content?

blockinfile replaces everything between the markers. Only content between markers is affected.

Does it preserve file permissions?

Yes — blockinfile modifies content in place without changing file ownership or permissions.

Insert a Block

- ansible.builtin.blockinfile:
    path: /etc/hosts
    block: |
      10.0.1.10 web1.example.com
      10.0.1.11 web2.example.com
      10.0.2.10 db1.example.com
  become: true

Result:

# BEGIN ANSIBLE MANAGED BLOCK
10.0.1.10 web1.example.com
10.0.1.11 web2.example.com
10.0.2.10 db1.example.com
# END ANSIBLE MANAGED BLOCK

Custom Markers

- blockinfile:
    path: /etc/nginx/nginx.conf
    marker: "# {mark} CUSTOM UPSTREAM CONFIG"
    block: |
      upstream backend {
          server 10.0.1.10:8080;
          server 10.0.1.11:8080;
      }
    insertbefore: "server {"
  become: true

Multiple Blocks in Same File

# Use different marker IDs
- blockinfile:
    path: /etc/ssh/sshd_config
    marker: "# {mark} SECURITY SETTINGS"
    block: |
      PermitRootLogin no
      PasswordAuthentication no
  become: true

- blockinfile: path: /etc/ssh/sshd_config marker: "# {mark} ACCESS CONTROL" block: | AllowUsers deploy admin MaxAuthTries 3 become: true

Insert After/Before

# After a specific line
- blockinfile:
    path: /etc/myapp/config.conf
    insertafter: "\\[database\\]"
    block: |
      host = db.example.com
      port = 5432

# Before a specific line - blockinfile: path: /etc/myapp/config.conf insertbefore: "\\[logging\\]" block: | cache_enabled = true cache_ttl = 3600

Remove a Block

- blockinfile:
    path: /etc/hosts
    marker: "# {mark} ANSIBLE MANAGED BLOCK"
    state: absent
  become: true

Dynamic Block Content

- blockinfile:
    path: /etc/hosts
    block: |
      {% for host in groups['webservers'] %}
      {{ hostvars[host]['ansible_default_ipv4']['address'] }} {{ host }}
      {% endfor %}
  become: true

blockinfile vs lineinfile

| Feature | blockinfile | lineinfile | |---------|--------------|--------------| | Scope | Multi-line block | Single line | | Markers | ✅ Auto-managed | ❌ None | | Idempotent | ✅ Via markers | ✅ Via regexp | | Use case | Config sections | Single settings |

Create File If Missing

- blockinfile:
    path: /etc/myapp/custom.conf
    create: true
    mode: '0644'
    block: |
      # Custom configuration
      setting1 = value1
      setting2 = value2
  become: true

FAQ

Can I use blockinfile without markers?

No — markers are how blockinfile identifies its block for updates. Without them, it can't be idempotent. Use copy + content if you don't want markers.

Markers visible in config — is that a problem?

Usually not — most config formats support # comments. For formats that don't, use appropriate comment syntax in marker.

How to update an existing block?

Just run blockinfile again with the same marker and new content. It replaces the entire block between markers.

Related Articles

become_user and become_method in Ansible

Category: database-automation

Watch the video: Ansible blockinfile Module: Insert & Manage Multi-Line Text Blocks (Guide) — Video Tutorial

Browse all Ansible tutorials · AnsiblePilot Home