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.

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 moduleblockinfile.
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/selevelThis 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
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 AnsibleCategory: database-automation
Watch the video: Ansible blockinfile Module: Insert & Manage Multi-Line Text Blocks (Guide) — Video Tutorial