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 lineinfile Module Cookbook: 25 Practical Examples

By Luca Berton · Published 2024-01-01 · Category: linux-administration

25 practical Ansible lineinfile examples. Add, modify, remove, and validate lines in configuration files.

lineinfile ensures a specific line exists (or doesn't exist) in a file. It's Ansible's surgical tool for in-place file edits — one line at a time.

Basic Operations

1. Add a Line

- name: Add DNS server
  ansible.builtin.lineinfile:
    path: /etc/resolv.conf
    line: "nameserver 8.8.8.8"
    state: present

2. Add a Line If Not Present

- name: Ensure PATH export exists
  ansible.builtin.lineinfile:
    path: /etc/profile
    line: 'export PATH="/opt/bin:$PATH"'
    state: present
  # Idempotent — skips if line already exists

3. Remove a Line

- name: Remove old DNS entry
  ansible.builtin.lineinfile:
    path: /etc/resolv.conf
    line: "nameserver 10.0.0.1"
    state: absent

4. Replace a Line by Regex

- name: Set max open files
  ansible.builtin.lineinfile:
    path: /etc/security/limits.conf
    regexp: '^\*\s+soft\s+nofile'
    line: "* soft nofile 65536"

5. Uncomment a Line

- name: Enable IP forwarding
  ansible.builtin.lineinfile:
    path: /etc/sysctl.conf
    regexp: '^#?\s*net\.ipv4\.ip_forward'
    line: "net.ipv4.ip_forward = 1"

See also: Ansible lineinfile Module: Add, Replace, Remove Lines in Files (Complete Guide)

Placement Control

6. Insert After a Line

- name: Add config after [global] section
  ansible.builtin.lineinfile:
    path: /etc/samba/smb.conf
    insertafter: '^\[global\]'
    line: "    server string = Managed by Ansible"

7. Insert Before a Line

- name: Add comment before setting
  ansible.builtin.lineinfile:
    path: /etc/ssh/sshd_config
    insertbefore: '^PermitRootLogin'
    line: "# Security: Restrict root login"

8. Insert at Beginning of File

- name: Add managed-by header
  ansible.builtin.lineinfile:
    path: /etc/myapp/config.conf
    insertbefore: BOF
    line: "# Managed by Ansible — do not edit manually"

9. Insert at End of File

- name: Append to end
  ansible.builtin.lineinfile:
    path: /etc/hosts
    insertafter: EOF
    line: "10.0.1.50 app.internal"

SSH Configuration

10. Disable Root Login

- name: Disable SSH root login
  ansible.builtin.lineinfile:
    path: /etc/ssh/sshd_config
    regexp: '^#?\s*PermitRootLogin'
    line: "PermitRootLogin no"
    validate: 'sshd -t -f %s'
  notify: restart sshd

11. Disable Password Authentication

- name: SSH key-only authentication
  ansible.builtin.lineinfile:
    path: /etc/ssh/sshd_config
    regexp: '^#?\s*PasswordAuthentication'
    line: "PasswordAuthentication no"
    validate: 'sshd -t -f %s'
  notify: restart sshd

12. Change SSH Port

- name: Set SSH port
  ansible.builtin.lineinfile:
    path: /etc/ssh/sshd_config
    regexp: '^#?\s*Port\s'
    line: "Port {{ ssh_port }}"
    validate: 'sshd -t -f %s'
  notify: restart sshd

See also: Ansible Development: Write Custom Modules, Plugins & Collections

System Configuration

13. Set Hostname in /etc/hosts

- name: Set hostname entry
  ansible.builtin.lineinfile:
    path: /etc/hosts
    regexp: '^127\.0\.1\.1'
    line: "127.0.1.1 {{ ansible_hostname }}.{{ domain }} {{ ansible_hostname }}"

14. Configure Sysctl Parameter

- name: Set kernel parameters
  ansible.builtin.lineinfile:
    path: /etc/sysctl.conf
    regexp: '^{{ item.key }}\s*='
    line: "{{ item.key }} = {{ item.value }}"
  loop:
    - { key: "net.ipv4.ip_forward", value: "1" }
    - { key: "vm.swappiness", value: "10" }
    - { key: "net.core.somaxconn", value: "65535" }
  notify: reload sysctl

15. Set Timezone

- name: Configure timezone in profile
  ansible.builtin.lineinfile:
    path: /etc/environment
    regexp: '^TZ='
    line: "TZ={{ timezone }}"

Regex with Backreferences

16. Update a Value Preserving Format

- name: Update max connections (preserve comment)
  ansible.builtin.lineinfile:
    path: /etc/postgresql/16/main/postgresql.conf
    regexp: '^(#?\s*max_connections\s*=\s*)\d+'
    line: '\g<1>200'
    backrefs: true

17. Update Version Number in Config

- name: Update app version
  ansible.builtin.lineinfile:
    path: /opt/app/VERSION
    regexp: '^version:\s*.*'
    line: "version: {{ new_version }}"

See also: Ansible on Debian 11 Bullseye: OpenSSH Hardening Complete Guide

Multiple Lines with Loop

18. Add Multiple Hosts Entries

- name: Add internal hosts
  ansible.builtin.lineinfile:
    path: /etc/hosts
    regexp: '.*\s{{ item.name }}$'
    line: "{{ item.ip }} {{ item.name }}"
  loop:
    - { ip: "10.0.1.10", name: "web01.internal" }
    - { ip: "10.0.1.20", name: "db01.internal" }
    - { ip: "10.0.1.30", name: "cache01.internal" }

File Creation

19. Create File If Missing

- name: Ensure config exists with default
  ansible.builtin.lineinfile:
    path: /etc/myapp/overrides.conf
    line: "# Custom overrides"
    create: true
    owner: myapp
    group: myapp
    mode: '0644'

blockinfile: Multi-Line Blocks

20. Add a Block of Lines

- name: Add firewall rules block
  ansible.builtin.blockinfile:
    path: /etc/iptables/rules.v4
    marker: "# {mark} ANSIBLE MANAGED - app rules"
    block: |
      -A INPUT -p tcp --dport 80 -j ACCEPT
      -A INPUT -p tcp --dport 443 -j ACCEPT
      -A INPUT -p tcp --dport 8080 -s 10.0.0.0/8 -j ACCEPT

21. Add SSH Config Block

- name: Configure SSH for bastion
  ansible.builtin.blockinfile:
    path: /home/deploy/.ssh/config
    marker: "# {mark} ANSIBLE MANAGED - bastion"
    block: |
      Host bastion
          HostName bastion.example.com
          User ubuntu
          IdentityFile ~/.ssh/bastion_key

Host 10.0.*.* ProxyJump bastion User deploy create: true owner: deploy group: deploy mode: '0600'

22. Update a Block

# blockinfile replaces content between markers on re-run
- name: Update allowed IPs
  ansible.builtin.blockinfile:
    path: /etc/nginx/conf.d/allowed-ips.conf
    marker: "# {mark} ANSIBLE MANAGED BLOCK"
    block: |
      {% for ip in allowed_ips %}
      allow {{ ip }};
      {% endfor %}
      deny all;

replace Module: Regex Replace

23. Replace All Occurrences

- name: Update all references to old domain
  ansible.builtin.replace:
    path: /etc/myapp/config.yml
    regexp: 'old\.example\.com'
    replace: 'new.example.com'

24. Comment Out a Section

- name: Comment out debug settings
  ansible.builtin.replace:
    path: /etc/myapp/config.conf
    regexp: '^(debug_.+=.+)$'
    replace: '# \1'

Validation

25. Validate Before Writing

- name: Edit sudoers safely
  ansible.builtin.lineinfile:
    path: /etc/sudoers
    regexp: '^deploy'
    line: "deploy ALL=(ALL) NOPASSWD: /usr/bin/systemctl"
    validate: 'visudo -cf %s'

lineinfile vs blockinfile vs replace vs template

| Module | Use When | |--------|----------| | lineinfile | Change a single line | | blockinfile | Add/update a multi-line block | | replace | Regex replace across entire file | | template | Generate entire file from scratch | | copy | Deploy a static file |

FAQ

Why does lineinfile keep adding duplicate lines?

You're missing the regexp parameter. Without it, lineinfile checks for exact line match. If the line has even slight whitespace differences, it adds a new one. Always use regexp to match the line you want to replace.

What does backrefs: true do?

With backrefs: true, if the regex doesn't match, the line is left unchanged (instead of being appended). It also enables \1, \g<1> backreferences in the line: parameter to reuse captured groups from the regex.

How do I edit multiple lines in one task?

Use blockinfile for a contiguous block, loop with lineinfile for multiple independent lines, or template if you're editing more than 3-4 lines (at that point, manage the whole file).

Is lineinfile idempotent?

Yes, when used correctly. With regexp, it replaces the matched line every time (idempotent). Without regexp, it checks for exact line presence. Both are idempotent — running twice produces the same result.

Conclusion

lineinfile for single lines, blockinfile for multi-line blocks, replace for regex substitutions, template for whole-file management. Start with lineinfile for quick edits, graduate to template when you're managing more than a few lines in the same file.

Related Articles

Ansible copy vs template vs lineinfileAnsible template Module GuideAnsible file Module CookbookAnsible regex_replace Filter Guide

Category: linux-administration

Browse all Ansible tutorials · AnsiblePilot Home