Working with Ansible Lineinfile: A Tutorial for Beginners
When managing multiple systems, the ability to make precise, controlled
changes to configuration files becomes essential. This is where Ansible's
lineinfile
module proves invaluable.
Unlike modules that replace entire files, lineinfile
allows you to make
surgical modifications to specific lines while preserving the rest of the file's
content. This targeted approach is perfect for updating configuration
parameters, adding entries to system files, or toggling settings without
disrupting the file's overall structure.
Consider scenarios where you need to change a single parameter in a large
configuration file, add a new entry to your hosts file, or comment out a
deprecated setting. These tasks require precision—changing too little means the
task isn't completed, while changing too much risks breaking functionality. The
lineinfile
module excels in these situations by focusing changes only where
needed.
In this guide, we'll explore the capabilities of the lineinfile
module and
learn how to use it effectively for various configuration management tasks on
your local machine.
Understanding the lineinfile module
At its core, the lineinfile
module ensures that a particular line exists in a
file in a specific form, or that it doesn't exist at all. It works by searching
for patterns using regular expressions and then applying the appropriate action.
Core parameters
The power of lineinfile
comes from its flexible parameters:
path
(ordest
) specifies the file you want to modify. This is the only required parameter and tells Ansible which file to work with.line
contains the content you want to add or use as a replacement. This is what will appear in the file after the module runs.regexp
defines the pattern to match. The module uses this regular expression to find lines that need to be changed or to determine where to add new content.state
determines whether the specified line should be present or absent in the file. By default, it's set to "present."insertafter
andinsertbefore
control where a new line should be added if it doesn't already exist in the file. You can specify a regex pattern or use special values like "BOF" (beginning of file) or "EOF" (end of file).backup
creates a backup of the original file before making changes, which is especially valuable when modifying critical system files.
When to use lineinfile
The lineinfile
module shines in specific scenarios but isn't always the best
choice. It's ideal for:
- Making small, targeted changes to configuration files
- Ensuring specific settings are configured correctly
- Adding or removing individual entries
- Working with well-structured files where lines are clearly identifiable
However, for more complex requirements, consider alternatives:
- For multi-line changes, the
blockinfile
module offers better control - When managing entire configuration files with many interdependent settings,
the
template
module provides a more holistic approach - For structured data formats like YAML or JSON, specialized modules will handle the format's syntax rules correctly
Let's see a simple example of the lineinfile
module in action:
- name: Basic lineinfile example
hosts: localhost
tasks:
- name: Add a line to a file
ansible.builtin.lineinfile:
path: /tmp/example.txt
line: 'This line will be added to the file'
create: yes
This straightforward playbook adds a line to a text file, creating the file if it doesn't exist.
To run this playbook, save it as basic_example.yml
and execute the following
command in your terminal:
ansible-playbook basic_example.yml
You should see output similar to:
PLAY [Basic lineinfile example] *********************************************
TASK [Gathering Facts] ******************************************************
ok: [localhost]
TASK [Add a line to a file] *************************************************
changed: [localhost]
PLAY RECAP ******************************************************************
localhost : ok=2 changed=1 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
After the playbook runs, you can verify the result by examining the file:
cat /tmp/example.txt
You should see:
This line will be added to the file
If you run the playbook a second time, the task will report ok
instead of
changed
because the line already exists, demonstrating Ansible's idempotent
behavior.
Basic lineinfile operations
Now let's explore some fundamental operations you can perform with the
lineinfile
module. These form the building blocks for more complex
configuration tasks.
Replacing lines that match a pattern
Often, you'll need to find an existing line and replace it with updated content.
This is where the regexp
parameter becomes crucial:
- name: Replace a line in a configuration file
hosts: localhost
tasks:
- name: Create a sample configuration file
ansible.builtin.copy:
dest: /tmp/config.ini
content: |
# Sample configuration file
server_port = 8080
debug_mode = false
log_level = info
- name: Update the server port
ansible.builtin.lineinfile:
path: /tmp/config.ini
regexp: ^server_port = .*
line: server_port = 9090
This task searches for any line that starts with "server_port = " and replaces it with the new value. Since the regular expression matches any value, this configuration allows for consistent updates regardless of the file's initial state.
To execute the playbook, run:
ansible-playbook replace_line.yml
PLAY [Replace a line in a configuration file] *******************************
TASK [Gathering Facts] ******************************************************
ok: [localhost]
TASK [Create a sample configuration file] ***********************************
changed: [localhost]
TASK [Update the server port] ***********************************************
changed: [localhost]
PLAY RECAP ******************************************************************
localhost : ok=3 changed=2 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
Examine the modified file:
cat /tmp/config.ini
Output:
# Sample configuration file
server_port = 9090
debug_mode = false
log_level = info
Notice that only the server_port
line has changed, while the rest of the file
remains untouched.
Removing lines from files
Sometimes you need to remove configuration entries entirely, such as disabling deprecated settings:
- name: Remove a line from a file
hosts: localhost
tasks:
- name: Create a sample file with multiple lines
ansible.builtin.copy:
dest: /tmp/sample.txt
content: |
Line 1: Keep this line
Line 2: This line should be removed
Line 3: Keep this line too
- name: Remove the unwanted line
ansible.builtin.lineinfile:
path: /tmp/sample.txt
regexp: ^Line 2:.*
state: absent
By setting state: absent
, you instruct Ansible to remove any line matching the
pattern. This is perfect for cleaning up configuration files by removing
outdated or unnecessary settings.
Once you execute the playbook:
ansible-playbook remove_line.yml
You will see the following output:
PLAY [Remove a line from a file] ********************************************
TASK [Gathering Facts] ******************************************************
ok: [localhost]
TASK [Create a sample file with multiple lines] *****************************
changed: [localhost]
TASK [Remove the unwanted line] *********************************************
changed: [localhost]
PLAY RECAP ******************************************************************
localhost : ok=3 changed=2 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
Examine the file to confirm the line was removed:
cat /tmp/sample.txt
Line 1: Keep this line
Line 3: Keep this line too
The second line has been removed while preserving the rest of the file's content.
Working with file permissions
When modifying system files, proper permissions are critical. The lineinfile
module allows you to set permissions as part of the modification:
- name: Add a line and set proper permissions
hosts: localhost
tasks:
- name: Add a line and set proper permissions
ansible.builtin.lineinfile:
path: /tmp/secure_config.txt
line: secure_setting = true
owner: "{{ ansible_user_id }}"
group: "{{ ansible_user_id }}"
mode: 0644
create: yes
This task not only adds a line to the file but also sets the file ownership to the current user and applies appropriate permissions—read and write for the owner, read-only for everyone else.
Running set_permissions.yml
Execute the playbook:
ansible-playbook set_permissions.yml
PLAY [Add a line and set proper permissions] ********************************
TASK [Gathering Facts] ******************************************************
ok: [localhost]
TASK [Add a line and set proper permissions] ********************************
changed: [localhost]
PLAY RECAP ******************************************************************
localhost : ok=2 changed=1 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
Verify the file content and permissions:
cat /tmp/secure_config.txt
ls -l /tmp/secure_config.txt
Output:
secure_setting = true
-rw-r--r-- 1 yourusername yourusername 21 Mar 19 10:15 /tmp/secure_config.txt
The file has been created with the specified content and permissions.
Using regular expressions with lineinfile
Regular expressions are the secret to the lineinfile
module's power, allowing
for precise targeting of content within files.
Understanding regex patterns for lineinfile
Regular expressions provide a language for pattern matching in text. With
lineinfile
, you use these patterns to identify exactly which lines to modify.
For example, to uncomment a configuration line:
- name: Uncomment a configuration setting
hosts: localhost
tasks:
- name: Create a sample configuration with commented line
ansible.builtin.copy:
dest: /tmp/app.conf
content: |
# Application settings
app_name = MyApp
# debug_mode = false
log_level = info
- name: Uncomment and update debug mode
ansible.builtin.lineinfile:
path: /tmp/app.conf
regexp: ^#\s*debug_mode\s*=
line: debug_mode = true
This task finds any line that starts with a hash followed by optional
whitespace, then debug_mode
and equals sign (a commented-out setting), and
replaces it with an active setting.
You can execute the playbook:
ansible-playbook uncomment_line.yml
PLAY [Uncomment a configuration setting] ************************************
TASK [Gathering Facts] ******************************************************
ok: [localhost]
TASK [Create a sample configuration with commented line] ********************
changed: [localhost]
TASK [Uncomment and update debug mode] **************************************
changed: [localhost]
PLAY RECAP ******************************************************************
localhost : ok=3 changed=2 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
The examine the modified file:
cat /tmp/app.conf
You should see:
# Application settings
app_name = MyApp
debug_mode = true
log_level = info
Notice that the commented debug_mode line has been uncommented and its value
changed to true
.
Capturing groups and backreferences
When you need to preserve parts of the original line while changing others, capturing groups and backreferences become invaluable:
- name: Use backreferences to preserve formatting
hosts: localhost
tasks:
- name: Create a sample formatted file
ansible.builtin.copy:
dest: /tmp/formatted_config.txt
content: |
# Settings with specific formatting
MAX_CONNECTIONS = 10
TIMEOUT = 30
RETRY_COUNT = 3
- name: Update connections while preserving format
ansible.builtin.lineinfile:
path: /tmp/formatted_config.txt
regexp: '^(MAX_CONNECTIONS\s*=\s*).*'
line: '\1100'
backrefs: yes
In this example, the regular expression captures the MAX_CONNECTIONS =
part
with exact spacing in a group. The \1
in the replacement line references this
captured group, preserving the exact formatting while changing only the value.
The backrefs: yes
parameter is necessary to enable this functionality.
Once the playbook is run:
ansible-playbook backreferences.yml
PLAY [Use backreferences to preserve formatting] ****************************
TASK [Gathering Facts] ******************************************************
ok: [localhost]
TASK [Create a sample formatted file] ***************************************
changed: [localhost]
TASK [Update connections while preserving format] ***************************
changed: [localhost]
PLAY RECAP ******************************************************************
localhost : ok=3 changed=2 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
You can examine the modified file:
cat /tmp/formatted_config.txt
It should display:
# Settings with specific formatting
MAX_CONNECTIONS = 100
TIMEOUT = 30
RETRY_COUNT = 3
Notice how the exact spacing around the equals sign has been preserved, while
only the value has been updated to 100
.
Testing regex patterns
When working with complex regex patterns, testing before implementation is prudent:
- name: Test a regex pattern before applying
hosts: localhost
tasks:
- name: Create a test configuration
ansible.builtin.copy:
dest: /tmp/test.conf
content: |
# Server configuration
Listen 8080
DocumentRoot /var/www
- name: Test a regex pattern (dry run)
ansible.builtin.lineinfile:
path: /tmp/test.conf
regexp: '^(\s*Listen\s+)\d+'
line: '\180'
backrefs: yes
check_mode: yes
register: result
- name: Show what would change
ansible.builtin.debug:
var: result
The check_mode: yes
parameter performs a dry run without making actual
changes. The result is registered and can be examined to ensure the pattern
works as expected before applying changes to production files.
Running test_regex.yml
Execute the playbook:
ansible-playbook test_regex.yml
Output:
PLAY [Test a regex pattern before applying] *********************************
TASK [Gathering Facts] ******************************************************
ok: [localhost]
TASK [Create a test configuration] ******************************************
changed: [localhost]
TASK [Test a regex pattern (dry run)] ***************************************
changed: [localhost]
TASK [Show what would change] ***********************************************
ok: [localhost] => {
"result": {
"changed": true,
"diff": [
{
"after": "Listen 80",
"after_header": "new line",
"before": "Listen 8080",
"before_header": "line"
}
],
"failed": false,
"msg": "line replaced"
}
}
PLAY RECAP ******************************************************************
localhost : ok=4 changed=2 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
The debug output shows what would change if the task were run normally. In this
case, Listen 8080
would be replaced with Listen 80
. The file remains
unchanged because of check mode, which you can verify:
cat /tmp/test.conf
Output:
# Server configuration
Listen 8080
DocumentRoot /var/www
Practical example: Editing configuration files
Let's apply these concepts to a practical scenario: configuring a web server on your local machine for development purposes.
First, we'll create a sample configuration file and then modify it to suit our development needs:
- name: Configure local web server
hosts: localhost
vars:
config_file: /tmp/webserver.conf
port: 8080
document_root: /tmp/www
tasks:
- name: Create base configuration
ansible.builtin.copy:
dest: "{{ config_file }}"
content: |
# Web Server Configuration
Listen 80
DocumentRoot /var/www/html
LogLevel warn
ErrorLog logs/error.log
CustomLog logs/access.log combined
# Security settings
# ServerTokens Prod
# ServerSignature Off
- name: Update listening port
ansible.builtin.lineinfile:
path: "{{ config_file }}"
regexp: '^Listen\s+\d+'
line: "Listen {{ port }}"
- name: Set document root for local development
ansible.builtin.lineinfile:
path: "{{ config_file }}"
regexp: '^DocumentRoot\s+'
line: "DocumentRoot {{ document_root }}"
- name: Enable security settings (uncomment)
ansible.builtin.lineinfile:
path: "{{ config_file }}"
regexp: '^#\s*(ServerTokens\s+Prod)'
line: '\1'
backrefs: yes
- name: Create document root directory
ansible.builtin.file:
path: "{{ document_root }}"
state: directory
- name: Display the modified configuration
ansible.builtin.command: cat {{ config_file }}
register: cat_result
changed_when: false
- name: Show the final configuration
ansible.builtin.debug:
var: cat_result.stdout_lines
Execute the playbook:
ansible-playbook web_server_config.yml
PLAY [Configure local web server] *******************************************
TASK [Gathering Facts] ******************************************************
ok: [localhost]
TASK [Create base configuration] ********************************************
changed: [localhost]
TASK [Update listening port] ************************************************
changed: [localhost]
TASK [Set document root for local development] ******************************
changed: [localhost]
TASK [Enable security settings (uncomment)] *********************************
changed: [localhost]
TASK [Create document root directory] ***************************************
changed: [localhost]
TASK [Display the modified configuration] ***********************************
changed: [localhost]
TASK [Show the final configuration] *****************************************
ok: [localhost] => {
"cat_result.stdout_lines": [
"# Web Server Configuration",
"",
"Listen 8080",
"DocumentRoot /tmp/www",
"",
"LogLevel warn",
"ErrorLog logs/error.log",
"CustomLog logs/access.log combined",
"",
"# Security settings",
"ServerTokens Prod",
"# ServerSignature Off"
]
}
PLAY RECAP ******************************************************************
localhost : ok=8 changed=6 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
The final output shows the modified configuration with:
- The port changed from 80 to 8080
- The document root updated to /tmp/www
- The ServerTokens line uncommented
Additionally, the playbook created the document root directory to ensure the configuration would work if implemented.
Managing application properties
Many applications use properties files for configuration. Let's see how
lineinfile
can manage these types of files:
- name: Configure application properties
hosts: localhost
vars:
properties_file: /tmp/application.properties
db_url: "jdbc:mysql://localhost:3306/devdb"
log_level: "debug"
tasks:
- name: Create properties file
ansible.builtin.copy:
dest: "{{ properties_file }}"
content: |
# Application properties
app.name=MyApp
app.version=1.0
# Database settings
spring.datasource.url=jdbc:h2:mem:testdb
spring.datasource.username=sa
spring.datasource.password=
# Logging
logging.level.root=info
logging.file=/tmp/app.log
- name: Update database connection
ansible.builtin.lineinfile:
path: "{{ properties_file }}"
regexp: '^spring\.datasource\.url='
line: "spring.datasource.url={{ db_url }}"
- name: Set logging level
ansible.builtin.lineinfile:
path: "{{ properties_file }}"
regexp: '^logging\.level\.root='
line: "logging.level.root={{ log_level }}"
- name: Display the modified properties
ansible.builtin.command: cat {{ properties_file }}
register: cat_result
changed_when: false
- name: Show the final properties
ansible.builtin.debug:
var: cat_result.stdout_lines
Execute the playbook:
ansible-playbook app_properties.yml
Output:
PLAY [Configure application properties] *************************************
TASK [Gathering Facts] ******************************************************
ok: [localhost]
TASK [Create properties file] ***********************************************
changed: [localhost]
TASK [Update database connection] *******************************************
changed: [localhost]
TASK [Set logging level] ****************************************************
changed: [localhost]
TASK [Display the modified properties] **************************************
changed: [localhost]
TASK [Show the final properties] ********************************************
ok: [localhost] => {
"cat_result.stdout_lines": [
"# Application properties",
"app.name=MyApp",
"app.version=1.0",
"",
"# Database settings",
"spring.datasource.url=jdbc:mysql://localhost:3306/devdb",
"spring.datasource.username=sa",
"spring.datasource.password=",
"",
"# Logging",
"logging.level.root=debug",
"logging.file=/tmp/app.log"
]
}
PLAY RECAP ******************************************************************
localhost : ok=6 changed=4 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
The final properties file shows:
- The database URL has been changed from an in-memory H2 database to a MySQL connection.
- The logging level has been updated from
info
todebug
.
This example demonstrates how lineinfile
can update Java properties files,
which are common in many enterprise applications.
Advanced techniques with lineinfile
As you become comfortable with basic lineinfile
operations, you can explore
more advanced techniques to handle complex scenarios.
Conditional modifications
Sometimes you need to make changes based on the current state of the file or other conditions:
- name: Make conditional changes
hosts: localhost
vars:
config_file: /tmp/conditional.conf
tasks:
- name: Create initial configuration
ansible.builtin.copy:
dest: "{{ config_file }}"
content: |
# Environment configuration
ENV=production
LOG_LEVEL=normal
DEBUG=false
- name: Check if production mode is enabled
ansible.builtin.command: grep -q "^ENV=production" {{ config_file }}
register: grep_result
changed_when: false
failed_when: false
- name: Enable detailed logging in production mode
ansible.builtin.lineinfile:
path: "{{ config_file }}"
regexp: '^LOG_LEVEL='
line: 'LOG_LEVEL=verbose'
when: grep_result.rc == 0
- name: Display the modified configuration
ansible.builtin.command: cat {{ config_file }}
register: cat_result
changed_when: false
- name: Show the final configuration
ansible.builtin.debug:
var: cat_result.stdout_lines
When you execute the playbook:
ansible-playbook conditional.yml
You will see:
PLAY [Make conditional changes] *********************************************
TASK [Gathering Facts] ******************************************************
ok: [localhost]
TASK [Create initial configuration] *****************************************
changed: [localhost]
TASK [Check if production mode is enabled] **********************************
changed: [localhost]
TASK [Enable detailed logging in production mode] ***************************
changed: [localhost]
TASK [Display the modified configuration] ***********************************
changed: [localhost]
TASK [Show the final configuration] *****************************************
ok: [localhost] => {
"cat_result.stdout_lines": [
"# Environment configuration",
"ENV=production",
"LOG_LEVEL=verbose",
"DEBUG=false"
]
}
PLAY RECAP ******************************************************************
localhost : ok=6 changed=4 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
Since the configuration has ENV=production
, the conditional task executes and
changes the LOG_LEVEL
to verbose
. This demonstrates how to make
configuration changes based on the context or content of the file.
Creating backups before changes
When modifying critical files, creating backups is a prudent safety measure that can also be achieved with the lineinfile module:
---
- name: Modify with backup
hosts: localhost
tasks:
- name: Create important configuration
ansible.builtin.copy:
dest: /tmp/important.conf
content: |
# Critical system configuration
# Do not modify manually
CRITICAL_SETTING=default_value
SYSTEM_MODE=normal
- name: Update important setting with backup
ansible.builtin.lineinfile:
path: /tmp/important.conf
regexp: '^CRITICAL_SETTING='
line: 'CRITICAL_SETTING=new_value'
backup: yes
register: backup_result
- name: Display backup file path
ansible.builtin.debug:
msg: "Backup created at: {{ backup_result.backup_file }}"
when: backup_result.changed
- name: Compare original and modified files
ansible.builtin.command: diff {{ backup_result.backup_file }} /tmp/important.conf
register: diff_result
changed_when: false
failed_when: false
when: backup_result.changed
- name: Show diff
ansible.builtin.debug:
var: diff_result.stdout_lines
when: backup_result.changed
Execute the playbook:
ansible-playbook backup.yml
Output:
PLAY [Modify with backup] ***************************************************
TASK [Gathering Facts] ******************************************************
ok: [localhost]
TASK [Create important configuration] ***************************************
changed: [localhost]
TASK [Update important setting with backup] *********************************
changed: [localhost]
TASK [Display backup file path] *********************************************
ok: [localhost] => {
"msg": "Backup created at: /tmp/important.conf.22785.2025-03-19@10:30:17~"
}
TASK [Compare original and modified files] **********************************
changed: [localhost]
TASK [Show diff] ************************************************************
ok: [localhost] => {
"diff_result.stdout_lines": [
"4c4",
"< CRITICAL_SETTING=default_value",
"---",
"> CRITICAL_SETTING=new_value"
]
}
PLAY RECAP ******************************************************************
localhost : ok=6 changed=3 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
The backup: yes
parameter creates a timestamped copy of the original file
before making changes. The playbook then shows the backup file path and displays
the differences between the original and modified files, confirming that only
the CRITICAL_SETTING line was changed.
Working with multiple lines
While lineinfile
operates on one line at a time, you can use it in a loop to
modify multiple lines efficiently:
---
- name: Update multiple settings
hosts: localhost
vars:
config_file: /tmp/multi.conf
settings:
- { key: "PORT", value: "8080" }
- { key: "DEBUG", value: "true" }
- { key: "LOG_LEVEL", value: "debug" }
tasks:
- name: Create initial configuration
ansible.builtin.copy:
dest: "{{ config_file }}"
content: |
# Multi-line configuration example
PORT=80
DEBUG=false
LOG_LEVEL=info
- name: Update each setting
ansible.builtin.lineinfile:
path: "{{ config_file }}"
regexp: '^{{ item.key }}='
line: '{{ item.key }}={{ item.value }}'
loop: "{{ settings }}"
- name: Display the final configuration
ansible.builtin.command: cat {{ config_file }}
register: cat_result
changed_when: false
- name: Show the final settings
ansible.builtin.debug:
var: cat_result.stdout_lines
Running multiple_lines.yml
Execute the playbook:
ansible-playbook multiple_lines.yml
Output:
PLAY [Update multiple settings] *********************************************
TASK [Gathering Facts] ******************************************************
ok: [localhost]
TASK [Create initial configuration] *****************************************
changed: [localhost]
TASK [Update each setting] **************************************************
changed: [localhost] => (item={'key': 'PORT', 'value': '8080'})
changed: [localhost]
TASK [Update each setting] **************************************************
changed: [localhost] => (item={'key': 'PORT', 'value': '8080'})
changed: [localhost] => (item={'key': 'DEBUG', 'value': 'true'})
changed: [localhost] => (item={'key': 'LOG_LEVEL', 'value': 'debug'})
TASK [Display the final configuration] **************************************
changed: [localhost]
TASK [Show the final settings] **********************************************
ok: [localhost] => {
"cat_result.stdout_lines": [
"# Multi-line configuration example",
"PORT=8080",
"DEBUG=true",
"LOG_LEVEL=debug"
]
}
PLAY RECAP ******************************************************************
localhost : ok=5 changed=3 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
This approach uses a variable to define multiple settings and their values, then
loops through them to update each one in the configuration file. The final
output shows all three settings successfully updated according to the values
defined in the settings
variable.
Some lineinfile best practices
As you work with the lineinfile
module, keep the following best practices in
mind:
1. Test before applying
Always test your regular expressions on sample files before applying them to
production systems. The check_mode
parameter is invaluable for seeing what
would change without making actual modifications.
For example, you can use:
ansible-playbook your_playbook.yml --check
This runs the entire playbook in check mode, showing what would change without making actual modifications.
2. Be specific with patterns
Make your regular expressions as specific as possible to avoid unintended
matches. For example, using ^setting=
rather than just setting=
ensures you
only match lines that begin with that setting.
Consider this example:
# Too general - might match in comments or other contexts
regexp: 'port='
# More specific - only matches lines starting with "port="
regexp: '^port='
# Even more specific - ensures proper formatting
regexp: '^port\s*=\s*\d+'
3. Create backups
Use the backup: yes
parameter when modifying critical files to create
automatic backups before changes are applied. This provides a safety net if
something goes wrong.
4. Ensure idempotence
Design your tasks to be idempotent—running the same playbook multiple times should produce the same result without unnecessary changes. This is a core principle of Ansible and ensures predictable behavior.
Test your playbooks by running them multiple times to verify they only make changes when needed.
5. Know when to use alternatives
While lineinfile
is powerful, recognize when other modules might be more
appropriate:
- Use
blockinfile
for multi-line changes. - Use
template
for managing complete files with complex structures. - Use format-specific modules for structured data like YAML or JSON.
Final thoughts
The lineinfile
module is a powerful tool in the Ansible ecosystem, enabling
precise control over configuration files with minimal impact. By mastering
regular expressions and understanding the module's parameters, you can automate
complex configuration tasks across multiple systems while maintaining
consistency and reliability.
Throughout this guide, we've explored how to use lineinfile
for various tasks:
adding lines, replacing content, removing entries, and preserving formatting.
We've seen how to work with different types of configuration files and how to
build more complex automation workflows by combining multiple lineinfile
tasks.
The focus on local development environments demonstrates how you can use these techniques in a practical, low-risk way before applying them to production systems. The same principles apply whether you're managing a single development machine or a fleet of production servers.
Thanks for reading!
Make your mark
Join the writer's program
Are you a developer and love writing and sharing your knowledge with the world? Join our guest writing program and get paid for writing amazing technical guides. We'll get them to the right readers that will appreciate them.
Write for us
Build on top of Better Stack
Write a script, app or project on top of Better Stack and share it with the world. Make a public repository and share it with us at our email.
community@betterstack.comor submit a pull request and help us build better products for everyone.
See the full list of amazing projects on github