Ansible Patch Management

Software updates fix security holes and bugs. Unpatched servers invite trouble, yet patching a large fleet by hand is slow and error prone. Ansible turns patching into a repeatable routine: check what needs updates, apply them in safe batches, reboot when needed, and verify that services return. Consider a school bus fleet. Each bus needs regular maintenance, and the school never takes every bus off the road on the same morning.

Diagram: The Patch Cycle

  ┌──────────────┐
  │ 1. Snapshot  │  backup or VM snapshot
  └──────┬───────┘
         ▼
  ┌──────────────┐
  │ 2. Drain     │  remove from load balancer
  └──────┬───────┘
         ▼
  ┌──────────────┐
  │ 3. Update    │  apply packages
  └──────┬───────┘
         ▼
  ┌──────────────┐
  │ 4. Reboot    │  only if required
  └──────┬───────┘
         ▼
  ┌──────────────┐
  │ 5. Verify    │  health check
  └──────┬───────┘
         ▼
  ┌──────────────┐
  │ 6. Return    │  add back to load balancer
  └──────────────┘

Update Packages on Debian Systems

- name: Patch Debian servers
  hosts: debian_servers
  become: true
  serial: 2
  tasks:
    - name: Refresh the package list
      ansible.builtin.apt:
        update_cache: true
        cache_valid_time: 3600

    - name: Apply all upgrades
      ansible.builtin.apt:
        upgrade: dist
        autoremove: true
      register: apt_result

Update Packages on Red Hat Systems

- name: Patch Red Hat servers
  hosts: redhat_servers
  become: true
  serial: 2
  tasks:
    - name: Install every available update
      ansible.builtin.dnf:
        name: "*"
        state: latest
      register: dnf_result

Restrict a run to security fixes by adding security: true to the dnf task.

Decide When to Reboot

Kernel and core library updates need a reboot. Debian systems create the file /var/run/reboot-required. Red Hat systems provide the needs-restarting command.

    - name: Check for a reboot flag on Debian
      ansible.builtin.stat:
        path: /var/run/reboot-required
      register: reboot_flag

    - name: Reboot the server when required
      ansible.builtin.reboot:
        reboot_timeout: 600
        msg: "Reboot started by Ansible patching"
      when: reboot_flag.stat.exists

The reboot module waits until the server answers again before the play continues.

Combine Both Families in One Playbook

    - name: Update Debian family
      ansible.builtin.apt:
        upgrade: dist
      when: ansible_os_family == "Debian"

    - name: Update Red Hat family
      ansible.builtin.dnf:
        name: "*"
        state: latest
      when: ansible_os_family == "RedHat"

Protect Against Problems

  • Run serial so only a small batch changes at a time
  • Set max_fail_percentage: 0 to stop the rollout after a failure
  • Hold risky packages with the exclude option or a package hold
  • Patch staging first and production after a few days of observation
  • Schedule changes inside an approved maintenance window
    - name: Update everything except the database engine
      ansible.builtin.dnf:
        name: "*"
        state: latest
        exclude: "mariadb*"

Verify After Patching

  post_tasks:
    - name: Confirm the web service responds
      ansible.builtin.uri:
        url: "http://{{ inventory_hostname }}/health"
        status_code: 200
      register: check
      retries: 6
      delay: 10
      until: check.status == 200

Create a Patch Report

    - name: Record the last patch date
      ansible.builtin.copy:
        content: "Patched on {{ ansible_date_time.iso8601 }}\n"
        dest: /etc/last_patch_date

Collect these files or send results to a monitoring system. Auditors and managers value a clear record of patch dates for each server.

Schedule the Job

Run patch playbooks from cron, a CI pipeline, or AWX schedules. AWX offers calendars, notifications, and logs for every run, which suits monthly patch windows.

Try This

Create a playbook with serial: 1 that refreshes the package cache, lists upgradable packages with apt list --upgradable or dnf check-update, and prints the result. Run it in check mode against two lab machines before applying a real update.

Summary

Ansible patching follows a cycle of snapshot, drain, update, reboot, verify, and return. Package modules handle Debian and Red Hat families, and the reboot module restarts servers safely. Batches through serial, failure limits, health checks, and reports keep patching predictable. Scheduling through AWX or a pipeline turns the routine into a dependable monthly habit.